<commit_before>use std::num::One;
use nalgebra::mat::{Translation, Rotate, Transform};
use nalgebra::vec::AlgebraicVecExt;
use bounding_volume::{HasAABB, AABB};
use geom::{Plane, Ball, Box, Cone, Cylinder, Implicit, HasMargin, CompoundAABB};
use ray::{Ray, RayCast, RayCastWithTransform};

/// Enumeration grouping all common shapes. Used to simplify collision detection
/// dispatch.
pub enum Geom<N, V, M, II> { // FIXME: rename that
    PlaneGeom(Plane<N, V>),
    CompoundGeom(@CompoundAABB<N, V, M, Geom<N, V, M, II>>),
    ImplicitGeom(IGeom<N, V, M>)
}

pub enum IGeom<N, V, M> {
    BallGeom(Ball<N>),
    BoxGeom(Box<N, V>),
    ConeGeom(Cone<N>),
    CylinderGeom(Cylinder<N>),
}

impl<N: One + ToStr, V: ToStr, M, II: ToStr> Geom<N, V, M, II> {
    /// Creates a new `Geom` from a plane.
    #[inline]
    pub fn new_plane(p: Plane<N, V>) -> Geom<N, V, M, II> {
        PlaneGeom(p)
    }

    /// Creates a new `Geom` from a compound geometry.
    #[inline]
    pub fn new_compound(c: @CompoundAABB<N, V, M, Geom<N, V, M, II>>)
        -> Geom<N, V, M, II> {
        CompoundGeom(c)
    }

    /// Creates a new `Geom` from a ball.
    #[inline]
    pub fn new_ball(b: Ball<N>) -> Geom<N, V, M, II> {
        ImplicitGeom(BallGeom(b))
    }

    /// Creates a new `Geom` from a cylinder.
    #[inline]
    pub fn new_cylinder(b: Cylinder<N>) -> Geom<N, V, M, II> {
        ImplicitGeom(CylinderGeom(b))
    }

    /// Creates a new `Geom` from a box.
    #[inline]
    pub fn new_box(b: Box<N, V>) -> Geom<N, V, M, II> {
        ImplicitGeom(BoxGeom(b))
    }

    /// Creates a new `Geom` from a cone.
    #[inline]
    pub fn new_cone(b: Cone<N>) -> Geom<N, V, M, II> {
        ImplicitGeom(ConeGeom(b))
    }

}

impl<N, V, M, II> Geom<N, V, M, II> {
    /**
     * Convenience method to extract a plane from the enumation. Fails if the
     * pattern `Plane(_)` is not matched.
     */
    #[inline]
    pub fn plane<'r>(&'r self) -> &'r Plane<N, V> {
        match *self {
            PlaneGeom(ref p) => p,
            _ => fail!("Unexpected geometry: this is not a plane.")
        }
    }

    /**
     * Convenience method to extract a compound geometry from the enumation. Fails if the
     * pattern `Compound(_)` is not matched.
     */
    #[inline]
    pub fn compound(&self) -> @CompoundAABB<N, V, M, Geom<N, V, M, II>> {
        match *self {
            CompoundGeom(c) => c,
            _ => fail!("Unexpected geometry: this is not a compound.")
        }
    }

    /**
     * Convenience method to extract an implicit geometry from the enumation. Fails if the
     * pattern `Implicit(_)` is not matched.
     */
    #[inline]
    pub fn implicit<'r>(&'r self) -> &'r IGeom<N, V, M> {
        match *self {
            ImplicitGeom(ref i) => i,
            _ => fail!("Unexpected geometry: this is not an implicit.")
        }
    }

    /**
     * Convenience method to extract a ball from the enumation. Fails if the
     * pattern `Implicit(Ball(_))` is not matched.
     */
    #[inline]
    pub fn ball<'r>(&'r self) -> &'r Ball<N> {
        match *self {
            ImplicitGeom(BallGeom(ref b)) => b,
            _ => fail!("Unexpected geometry: this is not a ball.")
        }
    }

    /**
     * Convenience method to extract a cone from the enumation. Fails if the
     * pattern `Implicit(Cone(_))` is not matched.
     */
    #[inline]
    pub fn cone<'r>(&'r self) -> &'r Cone<N> {
        match *self {
            ImplicitGeom(ConeGeom(ref c)) => c,
            _ => fail!("Unexpected geometry: this is not a ball.")
        }
    }

    /**
     * Convenience method to extract a cylinder from the enumation. Fails if the
     * pattern `Implicit(Cylinder(_))` is not matched.
     */
    #[inline]
    pub fn cylinder<'r>(&'r self) -> &'r Cylinder<N> {
        match *self {
            ImplicitGeom(CylinderGeom(ref c)) => c,
            _ => fail!("Unexpected geometry: this is not a ball.")
        }
    }

    /**
     * Convenience method to extract a box from the enumation. Fails if the
     * pattern `Implicit(Box(_))` is not matched.
     */
    #[inline]
    pub fn box<'r>(&'r self) -> &'r Box<N, V> {
        match *self {
            ImplicitGeom(BoxGeom(ref b)) => b,
            _ => fail!("Unexpected geometry: this is not a ball.")
        }
    }
}

impl<N: NumCast + Primitive + Orderable + Algebraic + Signed + Clone + ToStr,
     V: AlgebraicVecExt<N> + Clone + ToStr,
     M: Translation<V> + Rotate<V> + Transform<V> + Mul<M, M>,
     II>
HasAABB<N, V, M> for Geom<N, V, M, II> {
    #[inline]
    fn aabb(&self, m: &M) -> AABB<N, V> {
        match *self {
            PlaneGeom(ref p)    => p.aabb(m),
            CompoundGeom(ref c) => c.aabb(m),
            ImplicitGeom(ref i) => {
                match *i {
                    BallGeom(ref b)     => b.aabb(m),
                    BoxGeom(ref b)      => b.aabb(m),
                    ConeGeom(ref c)     => c.aabb(m),
                    CylinderGeom(ref c) => c.aabb(m),
                }
            }
        }
    }
}

// FIXME: move this to the ray folder?
impl<N: Algebraic + Bounded + Orderable + Primitive + Float + Clone + ToStr,
     V: 'static + AlgebraicVecExt<N> + Clone + ToStr,
     M: Rotate<V> + Transform<V>,
     II>
RayCast<N, V> for Geom<N, V, M, II> {
    #[inline]
    fn toi_with_ray(&self, ray: &Ray<V>) -> Option<N> {
        match *self {
            PlaneGeom(ref p)    => p.toi_with_ray(ray),
            CompoundGeom(ref c) => c.toi_with_ray(ray),
            ImplicitGeom(ref i) => {
                match *i {
                    BallGeom(ref b)     => b.toi_with_ray(ray),
                    BoxGeom(ref b)      => b.toi_with_ray(ray),
                    ConeGeom(ref c)     => c.toi_with_ray(ray),
                    CylinderGeom(ref c) => c.toi_with_ray(ray),
                }
            }
        }
    }
}

impl<N: Algebraic + Bounded + Orderable + Primitive + Float + Clone + ToStr,
     V: 'static + AlgebraicVecExt<N> + Clone + ToStr,
     M: Rotate<V> + Transform<V>,
     II>
RayCastWithTransform<N, V, M> for Geom<N, V, M, II>;

impl<N: Clone + Add<N, N>, V, M> HasMargin<N> for IGeom<N, V, M> {
    #[inline]
    fn margin(&self) -> N {
        match *self {
            BallGeom(ref b)     => b.margin(),
            BoxGeom(ref b)      => b.margin(),
            ConeGeom(ref c)     => c.margin(),
            CylinderGeom(ref c) => c.margin()
        }
    }
}

impl<N: Algebraic + Signed + Orderable + Clone,
     V: AlgebraicVecExt<N> + Clone,
     M: Translation<V> + Rotate<V> + Transform<V>>
Implicit<N, V, M> for IGeom<N, V, M> {
    #[inline]
    fn support_point_without_margin(&self, transform: &M, dir: &V) -> V {
        match *self {
            BallGeom(ref b)     => b.support_point_without_margin(transform, dir),
            BoxGeom(ref b)      => b.support_point_without_margin(transform, dir),
            ConeGeom(ref c)     => c.support_point_without_margin(transform, dir),
            CylinderGeom(ref c) => c.support_point_without_margin(transform, dir),
        }
    }

    #[inline]
    fn support_point(&self, transform: &M, dir: &V) -> V {
        match *self {
            BallGeom(ref b)     => b.support_point(transform, dir),
            BoxGeom(ref b)      => b.support_point(transform, dir),
            ConeGeom(ref c)     => c.support_point(transform, dir),
            CylinderGeom(ref c) => c.support_point(transform, dir),
        }
    }
}

<commit_msg>Make `Geom` and `IGeom` cloneable.<commit_after>use std::num::One;
use nalgebra::mat::{Translation, Rotate, Transform};
use nalgebra::vec::AlgebraicVecExt;
use bounding_volume::{HasAABB, AABB};
use geom::{Plane, Ball, Box, Cone, Cylinder, Implicit, HasMargin, CompoundAABB};
use ray::{Ray, RayCast, RayCastWithTransform};

/// Enumeration grouping all common shapes. Used to simplify collision detection
/// dispatch.
#[deriving(Clone)]
pub enum Geom<N, V, M, II> { // FIXME: rename that
    PlaneGeom(Plane<N, V>),
    CompoundGeom(@CompoundAABB<N, V, M, Geom<N, V, M, II>>),
    ImplicitGeom(IGeom<N, V, M>)
}

#[deriving(Clone)]
pub enum IGeom<N, V, M> {
    BallGeom(Ball<N>),
    BoxGeom(Box<N, V>),
    ConeGeom(Cone<N>),
    CylinderGeom(Cylinder<N>),
}

impl<N: One + ToStr, V: ToStr, M, II: ToStr> Geom<N, V, M, II> {
    /// Creates a new `Geom` from a plane.
    #[inline]
    pub fn new_plane(p: Plane<N, V>) -> Geom<N, V, M, II> {
        PlaneGeom(p)
    }

    /// Creates a new `Geom` from a compound geometry.
    #[inline]
    pub fn new_compound(c: @CompoundAABB<N, V, M, Geom<N, V, M, II>>)
        -> Geom<N, V, M, II> {
        CompoundGeom(c)
    }

    /// Creates a new `Geom` from a ball.
    #[inline]
    pub fn new_ball(b: Ball<N>) -> Geom<N, V, M, II> {
        ImplicitGeom(BallGeom(b))
    }

    /// Creates a new `Geom` from a cylinder.
    #[inline]
    pub fn new_cylinder(b: Cylinder<N>) -> Geom<N, V, M, II> {
        ImplicitGeom(CylinderGeom(b))
    }

    /// Creates a new `Geom` from a box.
    #[inline]
    pub fn new_box(b: Box<N, V>) -> Geom<N, V, M, II> {
        ImplicitGeom(BoxGeom(b))
    }

    /// Creates a new `Geom` from a cone.
    #[inline]
    pub fn new_cone(b: Cone<N>) -> Geom<N, V, M, II> {
        ImplicitGeom(ConeGeom(b))
    }

}

impl<N, V, M, II> Geom<N, V, M, II> {
    /**
     * Convenience method to extract a plane from the enumation. Fails if the
     * pattern `Plane(_)` is not matched.
     */
    #[inline]
    pub fn plane<'r>(&'r self) -> &'r Plane<N, V> {
        match *self {
            PlaneGeom(ref p) => p,
            _ => fail!("Unexpected geometry: this is not a plane.")
        }
    }

    /**
     * Convenience method to extract a compound geometry from the enumation. Fails if the
     * pattern `Compound(_)` is not matched.
     */
    #[inline]
    pub fn compound(&self) -> @CompoundAABB<N, V, M, Geom<N, V, M, II>> {
        match *self {
            CompoundGeom(c) => c,
            _ => fail!("Unexpected geometry: this is not a compound.")
        }
    }

    /**
     * Convenience method to extract an implicit geometry from the enumation. Fails if the
     * pattern `Implicit(_)` is not matched.
     */
    #[inline]
    pub fn implicit<'r>(&'r self) -> &'r IGeom<N, V, M> {
        match *self {
            ImplicitGeom(ref i) => i,
            _ => fail!("Unexpected geometry: this is not an implicit.")
        }
    }

    /**
     * Convenience method to extract a ball from the enumation. Fails if the
     * pattern `Implicit(Ball(_))` is not matched.
     */
    #[inline]
    pub fn ball<'r>(&'r self) -> &'r Ball<N> {
        match *self {
            ImplicitGeom(BallGeom(ref b)) => b,
            _ => fail!("Unexpected geometry: this is not a ball.")
        }
    }

    /**
     * Convenience method to extract a cone from the enumation. Fails if the
     * pattern `Implicit(Cone(_))` is not matched.
     */
    #[inline]
    pub fn cone<'r>(&'r self) -> &'r Cone<N> {
        match *self {
            ImplicitGeom(ConeGeom(ref c)) => c,
            _ => fail!("Unexpected geometry: this is not a ball.")
        }
    }

    /**
     * Convenience method to extract a cylinder from the enumation. Fails if the
     * pattern `Implicit(Cylinder(_))` is not matched.
     */
    #[inline]
    pub fn cylinder<'r>(&'r self) -> &'r Cylinder<N> {
        match *self {
            ImplicitGeom(CylinderGeom(ref c)) => c,
            _ => fail!("Unexpected geometry: this is not a ball.")
        }
    }

    /**
     * Convenience method to extract a box from the enumation. Fails if the
     * pattern `Implicit(Box(_))` is not matched.
     */
    #[inline]
    pub fn box<'r>(&'r self) -> &'r Box<N, V> {
        match *self {
            ImplicitGeom(BoxGeom(ref b)) => b,
            _ => fail!("Unexpected geometry: this is not a ball.")
        }
    }
}

impl<N: NumCast + Primitive + Orderable + Algebraic + Signed + Clone + ToStr,
     V: AlgebraicVecExt<N> + Clone + ToStr,
     M: Translation<V> + Rotate<V> + Transform<V> + Mul<M, M>,
     II>
HasAABB<N, V, M> for Geom<N, V, M, II> {
    #[inline]
    fn aabb(&self, m: &M) -> AABB<N, V> {
        match *self {
            PlaneGeom(ref p)    => p.aabb(m),
            CompoundGeom(ref c) => c.aabb(m),
            ImplicitGeom(ref i) => {
                match *i {
                    BallGeom(ref b)     => b.aabb(m),
                    BoxGeom(ref b)      => b.aabb(m),
                    ConeGeom(ref c)     => c.aabb(m),
                    CylinderGeom(ref c) => c.aabb(m),
                }
            }
        }
    }
}

// FIXME: move this to the ray folder?
impl<N: Algebraic + Bounded + Orderable + Primitive + Float + Clone + ToStr,
     V: 'static + AlgebraicVecExt<N> + Clone + ToStr,
     M: Rotate<V> + Transform<V>,
     II>
RayCast<N, V> for Geom<N, V, M, II> {
    #[inline]
    fn toi_with_ray(&self, ray: &Ray<V>) -> Option<N> {
        match *self {
            PlaneGeom(ref p)    => p.toi_with_ray(ray),
            CompoundGeom(ref c) => c.toi_with_ray(ray),
            ImplicitGeom(ref i) => {
                match *i {
                    BallGeom(ref b)     => b.toi_with_ray(ray),
                    BoxGeom(ref b)      => b.toi_with_ray(ray),
                    ConeGeom(ref c)     => c.toi_with_ray(ray),
                    CylinderGeom(ref c) => c.toi_with_ray(ray),
                }
            }
        }
    }
}

impl<N: Algebraic + Bounded + Orderable + Primitive + Float + Clone + ToStr,
     V: 'static + AlgebraicVecExt<N> + Clone + ToStr,
     M: Rotate<V> + Transform<V>,
     II>
RayCastWithTransform<N, V, M> for Geom<N, V, M, II>;

impl<N: Clone + Add<N, N>, V, M> HasMargin<N> for IGeom<N, V, M> {
    #[inline]
    fn margin(&self) -> N {
        match *self {
            BallGeom(ref b)     => b.margin(),
            BoxGeom(ref b)      => b.margin(),
            ConeGeom(ref c)     => c.margin(),
            CylinderGeom(ref c) => c.margin()
        }
    }
}

impl<N: Algebraic + Signed + Orderable + Clone,
     V: AlgebraicVecExt<N> + Clone,
     M: Translation<V> + Rotate<V> + Transform<V>>
Implicit<N, V, M> for IGeom<N, V, M> {
    #[inline]
    fn support_point_without_margin(&self, transform: &M, dir: &V) -> V {
        match *self {
            BallGeom(ref b)     => b.support_point_without_margin(transform, dir),
            BoxGeom(ref b)      => b.support_point_without_margin(transform, dir),
            ConeGeom(ref c)     => c.support_point_without_margin(transform, dir),
            CylinderGeom(ref c) => c.support_point_without_margin(transform, dir),
        }
    }

    #[inline]
    fn support_point(&self, transform: &M, dir: &V) -> V {
        match *self {
            BallGeom(ref b)     => b.support_point(transform, dir),
            BoxGeom(ref b)      => b.support_point(transform, dir),
            ConeGeom(ref c)     => c.support_point(transform, dir),
            CylinderGeom(ref c) => c.support_point(transform, dir),
        }
    }
}

<|endoftext|>


<commit_before>/*
    Copyright 2013 Jesse 'Jeaye' Wilkerson
    See licensing in LICENSE file, or at:
        http://www.opensource.org/licenses/BSD-3-Clause

    File: gl/shader.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      Abstracts loading, compiling, linking, and
      the setup of GLSL shaders. For debug builds,
      the Debug_Shader is used, which allows shaders
      loaded from files to be dynamically reloaded if
      the file changes. Release_Shaders drop this
      functionality for performance.
*/

extern mod std;
extern mod opengles;
use gl = opengles::gl2;
use math::{ Mat4x4 };
pub use Shader = self::Shaderable;

#[cfg(debug_shader)]
pub use Shader_Builder = self::Debug_Shader;
#[cfg(release_shader)]
pub use Shader_Builder = self::Release_Shader;

pub trait Shaderable
{
  pub fn bind(&mut self);
  pub fn get_uniform_location(&self, uniform: &str) -> gl::GLint;
  pub fn update_uniform_i32(&self, location: gl::GLint, i: i32);
  pub fn update_uniform_mat(&self, location: gl::GLint, mat: &Mat4x4);
}

#[cfg(debug_shader)]
pub struct Debug_Shader
{
  prog: gl::GLuint,
  vert_obj: gl::GLuint,
  frag_obj: gl::GLuint,
  vert_file: ~str,
  frag_file: ~str,
  vert_file_time: libc::time_t,
  frag_file_time: libc::time_t,
}

#[cfg(debug_shader)]
impl Debug_Shader
{
  pub fn new(vert_src : &str, frag_src : &str) -> @Shaderable
  {
    let mut shader = @mut Debug_Shader
    {
      prog: 0,
      vert_obj: 0,
      frag_obj: 0,
      vert_file: ~"",
      frag_file: ~"",
      vert_file_time: 0,
      frag_file_time: 0,
    };

    assert!(shared::load(shader, vert_src, frag_src));

    shader as @Shaderable
  }

  pub fn new_with_files(new_vert_file : &str, new_frag_file : &str) -> @Shaderable
  {
    let mut shader = @mut Debug_Shader
    {
      prog: 0,
      vert_obj: 0,
      frag_obj: 0,
      vert_file: new_vert_file.to_owned(),
      frag_file: new_frag_file.to_owned(),
      vert_file_time: 0,
      frag_file_time: 0,
    };
    shader.vert_file_time = match Path(new_vert_file).stat()
    {
      Some(ref st) => st.st_mtime,
      None => 0
    };
    shader.frag_file_time = match Path(new_frag_file).stat()
    {
      Some(ref st) => st.st_mtime,
      None => 0
    };

    let fio = io::file_reader(&Path(new_vert_file)).unwrap();
    let vert_src = str::from_bytes(fio.read_whole_stream());

    let fio = io::file_reader(&Path(new_frag_file)).unwrap();
    let frag_src = str::from_bytes(fio.read_whole_stream());

    assert!(shared::load(shader, vert_src, frag_src));

    shader as @Shaderable
  }
}

#[cfg(debug_shader)]
impl Shader for Debug_Shader
{
  pub fn bind(&mut self)
  {
    /* Get the time stamp on the files. */
    let vert_time = match Path(self.vert_file).stat()
    {
      Some(ref st) => st.st_mtime,
      None => 0
    };
    let frag_time = match Path(self.frag_file).stat()
    {
      Some(ref st) => st.st_mtime,
      None => 0
    };

    /* Check if the files are newer than before. */
    if vert_time > self.vert_file_time || frag_time > self.frag_file_time
    {
      let fio = io::file_reader(&Path(self.vert_file)).unwrap();
      let vert_src = str::from_bytes(fio.read_whole_stream());

      let fio = io::file_reader(&Path(self.frag_file)).unwrap();
      let frag_src = str::from_bytes(fio.read_whole_stream());

      shared::load(self, vert_src, frag_src);

      self.vert_file_time = vert_time;
      self.frag_file_time = frag_time;
    }

    shared::bind(self);
  }

  pub fn get_uniform_location(&self, uniform: &str) -> gl::GLint
  { shared::get_uniform_location(self, uniform) }

  pub fn update_uniform_i32(&self, location: gl::GLint, i: i32)
  { shared::update_uniform_i32(location, i) }

  pub fn update_uniform_mat(&self, location: gl::GLint, mat: &Mat4x4)
  { shared::update_uniform_mat(location, mat) }
}

#[cfg(release_shader)]
pub struct Release_Shader
{
  prog: gl::GLuint,
  vert_obj: gl::GLuint,
  frag_obj: gl::GLuint,
}

#[cfg(release_shader)]
impl Release_Shader
{
  pub fn new(vert_src : &str, frag_src : &str) -> @Shaderable
  {
    let mut shader = @mut Release_Shader{ prog: 0, vert_obj: 0, frag_obj: 0 };

    assert!(shared::load(shader, vert_src, frag_src));

    shader as @Shaderable
  }

  pub fn new_with_files(vert_file : &str, frag_file : &str) -> @Shaderable
  {
    let mut shader = @mut Release_Shader{ prog: 0, vert_obj: 0, frag_obj: 0 };

    let fio = io::file_reader(&Path(vert_file)).unwrap();
    let vert_src = str::from_bytes(fio.read_whole_stream());

    let fio = io::file_reader(&Path(frag_file)).unwrap();
    let frag_src = str::from_bytes(fio.read_whole_stream());

    assert!(shared::load(shader, vert_src, frag_src));

    shader as @Shaderable
  }
}

#[cfg(release_shader)]
impl Shader for Release_Shader
{
  pub fn bind(&mut self)
  { shared::bind(self); }

  pub fn get_uniform_location(&self, uniform: &str) -> gl::GLint
  { shared::get_uniform_location(self, uniform) }

  pub fn update_uniform_i32(&self, location: gl::GLint, i: i32)
  { shared::update_uniform_i32(location, i) }

  pub fn update_uniform_mat(&self, location: gl::GLint, mat: &Mat4x4)
  { shared::update_uniform_mat(location, mat) }
}

mod shared
{
  use gl = opengles::gl2;
  use math::{ Mat4x4 };

  #[path = "../util.rs"]
  mod util;
  #[macro_escape]
  #[path = "../check_internal.rs"]
  mod check_internal;

  pub fn load(shader: &mut super::Shader_Builder, vert_src: &str, frag_src: &str) -> bool
  {
    if check!(gl::is_program(shader.prog)) > 0
    { check!(gl::delete_program(shader.prog)); }

    shader.prog = check!(gl::create_program());

    let compile_check = |obj| -> bool
    {
      /* Error check. */
      let result = check!(gl::get_shader_iv(obj, gl::COMPILE_STATUS));
      if result == 0 as gl::GLint
      {
        let err = check!(gl::get_shader_info_log(obj));
        error!(err);
      }
      result != 0
    };

    /* Compile the provided shaders. */
    if vert_src.len() > 0
    {
      shader.vert_obj = check!(gl::create_shader(gl::VERTEX_SHADER));
      assert!(shader.vert_obj != 0);

      let src = [vert_src];
      check!(gl::shader_source(shader.vert_obj, src.map(|x| str::to_bytes(*x))));
      check!(gl::compile_shader(shader.vert_obj));

      /* Error checking. */
      if !compile_check(shader.vert_obj)
      { check!(gl::delete_shader(shader.vert_obj)); return false; }
    }
    if frag_src.len() > 0
    {
      shader.frag_obj = check!(gl::create_shader(gl::FRAGMENT_SHADER));
      assert!(shader.frag_obj != 0);

      let src = [frag_src];
      check!(gl::shader_source(shader.frag_obj, src.map(|x| str::to_bytes(*x))));
      check!(gl::compile_shader(shader.frag_obj));

      /* Error checking. */
      if !compile_check(shader.frag_obj)
      { check!(gl::delete_shader(shader.frag_obj)); return false; }
    }

    /* Check if one of the shaders was properly compiled. */
    if shader.vert_obj > 0
    { check!(gl::attach_shader(shader.prog, shader.vert_obj)); }
    if shader.frag_obj > 0
    { check!(gl::attach_shader(shader.prog, shader.frag_obj)); }

    check!(gl::link_program(shader.prog));

    /* Error check. */
    let result = check!(gl::get_program_iv(shader.prog, gl::LINK_STATUS));
    if result == 0 as gl::GLint
    {
      let err = check!(gl::get_program_info_log(shader.prog));
      error!(err);

      /* Delete shaders. */
      check!(gl::detach_shader(shader.prog, shader.vert_obj));
      check!(gl::delete_shader(shader.vert_obj));

      check!(gl::detach_shader(shader.prog, shader.frag_obj));
      check!(gl::delete_shader(shader.frag_obj));

      check!(gl::delete_program(shader.prog));

      return false;
    }

    true
  }

  pub fn bind(shader: &mut super::Shader_Builder)
  { check!(gl::use_program(shader.prog)); }

  pub fn get_uniform_location(shader: &super::Shader_Builder, uniform: &str) -> gl::GLint
  { check!(gl::get_uniform_location(shader.prog, uniform.to_owned())) }

  pub fn update_uniform_i32(location: gl::GLint, i: i32)
  { check!(gl::uniform_1i(location, i)); }

  pub fn update_uniform_mat(location: gl::GLint, mat: &Mat4x4)
  {
    unsafe
    {
      check!(gl::uniform_matrix_4fv(
                   location,
                   false,
                   cast::transmute::<[[f32, ..4], ..4], [f32, ..16]>(mat.data)))
    };
  }
}

<commit_msg> #10 If dynamic shader compilation fails, Q3 continues<commit_after>/*
    Copyright 2013 Jesse 'Jeaye' Wilkerson
    See licensing in LICENSE file, or at:
        http://www.opensource.org/licenses/BSD-3-Clause

    File: gl/shader.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      Abstracts loading, compiling, linking, and
      the setup of GLSL shaders. For debug builds,
      the Debug_Shader is used, which allows shaders
      loaded from files to be dynamically reloaded if
      the file changes. Release_Shaders drop this
      functionality for performance.
*/

extern mod std;
extern mod opengles;
use gl = opengles::gl2;
use math::{ Mat4x4 };
pub use Shader = self::Shaderable;

#[cfg(debug_shader)]
pub use Shader_Builder = self::Debug_Shader;
#[cfg(release_shader)]
pub use Shader_Builder = self::Release_Shader;

pub trait Shaderable
{
  pub fn bind(&mut self);
  pub fn get_uniform_location(&self, uniform: &str) -> gl::GLint;
  pub fn update_uniform_i32(&self, location: gl::GLint, i: i32);
  pub fn update_uniform_mat(&self, location: gl::GLint, mat: &Mat4x4);
}

#[cfg(debug_shader)]
pub struct Debug_Shader
{
  prog: gl::GLuint,
  vert_obj: gl::GLuint,
  frag_obj: gl::GLuint,
  vert_file: ~str,
  frag_file: ~str,
  vert_file_time: libc::time_t,
  frag_file_time: libc::time_t,
  valid: bool, /* Whether or not the last compilation succeeded. */
}

#[cfg(debug_shader)]
impl Debug_Shader
{
  pub fn new(vert_src : &str, frag_src : &str) -> @Shaderable
  {
    let mut shader = @mut Debug_Shader
    {
      prog: 0,
      vert_obj: 0,
      frag_obj: 0,
      vert_file: ~"",
      frag_file: ~"",
      vert_file_time: 0,
      frag_file_time: 0,
      valid: false,
    };

    assert!(shared::load(shader, vert_src, frag_src));
    shader.valid = true;

    shader as @Shaderable
  }

  pub fn new_with_files(new_vert_file : &str, new_frag_file : &str) -> @Shaderable
  {
    let mut shader = @mut Debug_Shader
    {
      prog: 0,
      vert_obj: 0,
      frag_obj: 0,
      vert_file: new_vert_file.to_owned(),
      frag_file: new_frag_file.to_owned(),
      vert_file_time: 0,
      frag_file_time: 0,
      valid: false,
    };
    shader.vert_file_time = match Path(new_vert_file).stat()
    {
      Some(ref st) => st.st_mtime,
      None => 0
    };
    shader.frag_file_time = match Path(new_frag_file).stat()
    {
      Some(ref st) => st.st_mtime,
      None => 0
    };

    let fio = io::file_reader(&Path(new_vert_file)).unwrap();
    let vert_src = str::from_bytes(fio.read_whole_stream());

    let fio = io::file_reader(&Path(new_frag_file)).unwrap();
    let frag_src = str::from_bytes(fio.read_whole_stream());

    assert!(shared::load(shader, vert_src, frag_src));
    shader.valid = true;

    shader as @Shaderable
  }
}

#[cfg(debug_shader)]
impl Shader for Debug_Shader
{
  pub fn bind(&mut self)
  {
    /* Get the time stamp on the files. */
    let vert_time = match Path(self.vert_file).stat()
    {
      Some(ref st) => st.st_mtime,
      None => 0
    };
    let frag_time = match Path(self.frag_file).stat()
    {
      Some(ref st) => st.st_mtime,
      None => 0
    };

    /* Check if the files are newer than before. */
    if vert_time > self.vert_file_time || frag_time > self.frag_file_time
    {
      let fio = io::file_reader(&Path(self.vert_file)).unwrap();
      let vert_src = str::from_bytes(fio.read_whole_stream());

      let fio = io::file_reader(&Path(self.frag_file)).unwrap();
      let frag_src = str::from_bytes(fio.read_whole_stream());

      self.valid = shared::load(self, vert_src, frag_src);

      self.vert_file_time = vert_time;
      self.frag_file_time = frag_time;
    }

    if self.valid
    { shared::bind(self); }
  }

  pub fn get_uniform_location(&self, uniform: &str) -> gl::GLint
  { if self.valid { return shared::get_uniform_location(self, uniform); } -1 }

  pub fn update_uniform_i32(&self, location: gl::GLint, i: i32)
  { if self.valid { shared::update_uniform_i32(location, i); } }

  pub fn update_uniform_mat(&self, location: gl::GLint, mat: &Mat4x4)
  { if self.valid { shared::update_uniform_mat(location, mat); } }
}

#[cfg(release_shader)]
pub struct Release_Shader
{
  prog: gl::GLuint,
  vert_obj: gl::GLuint,
  frag_obj: gl::GLuint,
}

#[cfg(release_shader)]
impl Release_Shader
{
  pub fn new(vert_src : &str, frag_src : &str) -> @Shaderable
  {
    let mut shader = @mut Release_Shader{ prog: 0, vert_obj: 0, frag_obj: 0 };

    assert!(shared::load(shader, vert_src, frag_src));

    shader as @Shaderable
  }

  pub fn new_with_files(vert_file : &str, frag_file : &str) -> @Shaderable
  {
    let mut shader = @mut Release_Shader{ prog: 0, vert_obj: 0, frag_obj: 0 };

    let fio = io::file_reader(&Path(vert_file)).unwrap();
    let vert_src = str::from_bytes(fio.read_whole_stream());

    let fio = io::file_reader(&Path(frag_file)).unwrap();
    let frag_src = str::from_bytes(fio.read_whole_stream());

    assert!(shared::load(shader, vert_src, frag_src));

    shader as @Shaderable
  }
}

#[cfg(release_shader)]
impl Shader for Release_Shader
{
  pub fn bind(&mut self)
  { shared::bind(self); }

  pub fn get_uniform_location(&self, uniform: &str) -> gl::GLint
  { shared::get_uniform_location(self, uniform) }

  pub fn update_uniform_i32(&self, location: gl::GLint, i: i32)
  { shared::update_uniform_i32(location, i) }

  pub fn update_uniform_mat(&self, location: gl::GLint, mat: &Mat4x4)
  { shared::update_uniform_mat(location, mat) }
}

mod shared
{
  use gl = opengles::gl2;
  use math::{ Mat4x4 };

  #[path = "../util.rs"]
  mod util;
  #[macro_escape]
  #[path = "../check_internal.rs"]
  mod check_internal;

  pub fn load(shader: &mut super::Shader_Builder, vert_src: &str, frag_src: &str) -> bool
  {
    if check!(gl::is_program(shader.prog)) > 0
    { check!(gl::delete_program(shader.prog)); }

    shader.prog = check!(gl::create_program());

    let compile_check = |obj| -> bool
    {
      /* Error check. */
      let result = check!(gl::get_shader_iv(obj, gl::COMPILE_STATUS));
      if result == 0 as gl::GLint
      {
        let err = check!(gl::get_shader_info_log(obj));
        error!(err);
      }
      result != 0
    };

    /* Compile the provided shaders. */
    if vert_src.len() > 0
    {
      shader.vert_obj = check!(gl::create_shader(gl::VERTEX_SHADER));
      assert!(shader.vert_obj != 0);

      let src = [vert_src];
      check!(gl::shader_source(shader.vert_obj, src.map(|x| str::to_bytes(*x))));
      check!(gl::compile_shader(shader.vert_obj));

      /* Error checking. */
      if !compile_check(shader.vert_obj)
      { check!(gl::delete_shader(shader.vert_obj)); return false; }
    }
    if frag_src.len() > 0
    {
      shader.frag_obj = check!(gl::create_shader(gl::FRAGMENT_SHADER));
      assert!(shader.frag_obj != 0);

      let src = [frag_src];
      check!(gl::shader_source(shader.frag_obj, src.map(|x| str::to_bytes(*x))));
      check!(gl::compile_shader(shader.frag_obj));

      /* Error checking. */
      if !compile_check(shader.frag_obj)
      { check!(gl::delete_shader(shader.frag_obj)); return false; }
    }

    /* Check if one of the shaders was properly compiled. */
    if shader.vert_obj > 0
    { check!(gl::attach_shader(shader.prog, shader.vert_obj)); }
    if shader.frag_obj > 0
    { check!(gl::attach_shader(shader.prog, shader.frag_obj)); }

    check!(gl::link_program(shader.prog));

    /* Error check. */
    let result = check!(gl::get_program_iv(shader.prog, gl::LINK_STATUS));
    if result == 0 as gl::GLint
    {
      let err = check!(gl::get_program_info_log(shader.prog));
      error!(err);

      /* Delete shaders. */
      check!(gl::detach_shader(shader.prog, shader.vert_obj));
      check!(gl::delete_shader(shader.vert_obj));

      check!(gl::detach_shader(shader.prog, shader.frag_obj));
      check!(gl::delete_shader(shader.frag_obj));

      check!(gl::delete_program(shader.prog));

      return false;
    }

    true
  }

  pub fn bind(shader: &mut super::Shader_Builder)
  { check!(gl::use_program(shader.prog)); }

  pub fn get_uniform_location(shader: &super::Shader_Builder, uniform: &str) -> gl::GLint
  { check!(gl::get_uniform_location(shader.prog, uniform.to_owned())) }

  pub fn update_uniform_i32(location: gl::GLint, i: i32)
  { check!(gl::uniform_1i(location, i)); }

  pub fn update_uniform_mat(location: gl::GLint, mat: &Mat4x4)
  {
    unsafe
    {
      check!(gl::uniform_matrix_4fv(
                   location,
                   false,
                   cast::transmute::<[[f32, ..4], ..4], [f32, ..16]>(mat.data)))
    };
  }
}

<|endoftext|>


<commit_before>use serde_json;
use std;
use std::collections::{BTreeMap, BTreeSet};
use std::collections::HashMap;
use std::marker::PhantomData;

use constraints::{Constraint, ConstraintValue};
use core::{Apply, Bindings, Unify};
use pedigree::{Origin, Pedigree, RenderType};
use utils;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Rule<T: ConstraintValue, U: Unify<T>> {
    #[serde(default)]
    pub constraints: Vec<Constraint>,
    #[serde(default = "Vec::default")]
    pub lhs: Vec<U>,
    pub rhs: U,
    #[serde(default)]
    _marker: PhantomData<T>,
}

impl<T, U> Apply<T, U> for Rule<T, U>
    where T: ConstraintValue,
          U: Unify<T>
{
    fn arg_count(&self) -> usize {
        self.lhs.len()
    }

    fn apply(&self, facts: &Vec<&U>, bindings: &Bindings<T>) -> Option<(U, Bindings<T>)> {
        self.unify(facts, bindings)
            .and_then(|bindings| self.solve_constraints(&bindings))
            .and_then(|bindings| self.apply_bindings(&bindings).and_then(|rhs| Some((rhs, bindings))))
    }

    fn constraints<'a>(&'a self) -> Vec<&'a Constraint> {
        self.constraints.iter().collect()
    }

    fn r_apply(&self, fact: &U, bindings: &Bindings<T>) -> Option<(Vec<U>, Bindings<T>)> {
        self.rhs
            .apply_bindings(bindings)
            .and_then(|bound_rhs| {
                bound_rhs.unify(&fact, bindings)
                    .and_then(|bindings| self.solve_constraints(&bindings))
                    .and_then(|bindings| {
                        utils::filter_map_all(&mut self.lhs.iter(), &|f| f.apply_bindings(&bindings))
                            .and_then(|bound_lhs| Some((bound_lhs, bindings.clone())))
                    })
            })
    }

    /// Construct a new version of this rule but with all variables updated to be unique for this invocation
    fn snowflake(&self, suffix: String) -> Self {
        // Gather all variables
        let mut variables = self.rhs.variables();
        for lhs in self.lhs.iter() {
            variables.extend(lhs.variables());
        }
        for constraint in self.constraints.iter() {
            variables.extend(constraint.variables());
        }

        let renamed_variable: HashMap<String, String> = variables.into_iter()
            .map(|var| (var.clone(), format!("{}::{}", var, suffix)))
            .collect();

        let rhs = self.rhs.rename_variables(&renamed_variable);
        let lhs: Vec<U> = self.lhs.iter().map(|lhs| lhs.rename_variables(&renamed_variable)).collect();
        let constraints: Vec<Constraint> = self.constraints.iter().map(|constraint| constraint.rename_variables(&renamed_variable)).collect();

        Rule {
            constraints: constraints,
            lhs: lhs,
            rhs: rhs,
            _marker: PhantomData,
        }
    }
}

impl<T, U> Rule<T, U>
    where T: ConstraintValue,
          U: Unify<T>
{
    pub fn unify(&self, facts: &Vec<&U>, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        utils::fold_while_some(bindings.clone(),
                               &mut self.lhs.iter().zip(facts.iter()),
                               &|bindings, (t1, t2)| t1.unify(t2, &bindings))
    }

    pub fn apply_bindings(&self, bindings: &Bindings<T>) -> Option<U> {
        self.rhs.apply_bindings(bindings)
    }

    fn solve_constraints(&self, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        Constraint::solve_many(self.constraints.iter().collect(), bindings).ok()
    }
}

impl<T, U> std::fmt::Display for Rule<T, U>
    where T: ConstraintValue,
          U: Unify<T>
{
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", serde_json::to_string(&self).unwrap())
    }
}

impl<T, U> Eq for Rule<T, U>
    where T: ConstraintValue,
          U: Unify<T>
{
}

#[derive(Clone, Debug, PartialEq)]
pub struct OriginCache {
    items: BTreeSet<Origin>,
}

impl OriginCache {
    pub fn new() -> Self {
        OriginCache { items: BTreeSet::new() }
    }

    pub fn has_item(&self, item: &Origin) -> bool {
        self.items.contains(item)
    }

    pub fn insert_item_mut(&mut self, item: Origin) {
        self.items.insert(item);
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct InferenceEngine<'a, T, U>
    where T: 'a + ConstraintValue,
          U: 'a + Unify<T>
{
    pub rules: BTreeMap<&'a String, &'a Rule<T, U>>,
    pub facts: BTreeMap<&'a String, &'a U>,
    // Facts derived from this inference process
    pub derived_facts: BTreeMap<String, U>,
    pub pedigree: Pedigree,
    pub prefix: String,
    // Used to check if an inference has already been performed,
    // allowing us to short-circuit a potentially expensive unification process.
    pub origin_cache: OriginCache,
}

impl<'a, T, U> InferenceEngine<'a, T, U>
    where T: 'a + ConstraintValue,
          U: 'a + Unify<T>
{
    pub fn new(prefix: String, rules: BTreeMap<&'a String, &'a Rule<T, U>>, facts: BTreeMap<&'a String, &'a U>) -> Self {
        InferenceEngine {
            rules: rules,
            facts: facts,
            derived_facts: BTreeMap::new(),
            pedigree: Pedigree::new(),
            prefix: prefix,
            origin_cache: OriginCache::new(),
        }
    }

    pub fn all_facts(&'a self) -> Vec<(&'a String, &'a U)> {
        self.derived_facts
            .iter()
            .map(|(key, value)| (key, value))
            .chain(self.facts.iter().map(|(key, value)| (key.clone(), value.clone())))
            .collect()
    }

    pub fn chain_until_match(&self, max_iterations: usize, goal: &U) -> (Option<(U, String)>, Self) {
        self.chain_until(max_iterations,
                         &|f| goal.unify(f, &Bindings::new()).is_some())
    }

    pub fn chain_until(&self, max_iterations: usize, satisfied: &Fn(&U) -> bool) -> (Option<(U, String)>, Self) {
        let mut engine = self.clone();
        let mut target: Option<(U, String)> = None;
        for _idx in 0..max_iterations {
            for (fact, _bindings, origin) in engine.chain_forward().into_iter() {
                let id = engine.construct_id(&fact);

                if satisfied(&fact) {
                    target = Some((fact.clone(), id.clone()));
                }

                engine.pedigree.insert_mut(id.clone(), origin);
                engine.derived_facts.insert(id, fact);
            }
            if target.is_some() {
                break;
            }
        }
        (target, engine)
    }

    pub fn chain_forward(&mut self) -> Vec<(U, Bindings<T>, Origin)> {
        let mut origin_cache = self.origin_cache.clone();
        let results = chain_forward(self.all_facts(),
                                    self.rules.iter().map(|(id, rule)| (id.clone(), rule.clone())).collect(),
                                    &mut origin_cache);
        self.origin_cache = origin_cache;
        results
    }

    fn construct_id(&self, _fact: &U) -> String {
        format!("{}-{}", self.prefix, self.derived_facts.len())
    }

    pub fn render_inference_tree(&self, id: &String, render_type: RenderType) -> String {
        let node_renderer = |x| {
            self.facts
                .get(&x)
                .and_then(|y| Some(format!("{}", y)))
                .or_else(|| self.derived_facts.get(&x).and_then(|y| Some(format!("{}", y))))
                .or_else(|| self.rules.get(&x).and_then(|y| Some(format!("{}", y))))
                .unwrap_or(format!("{}?", x))
        };

        self.pedigree.render_inference_tree(id,
                                            &node_renderer,
                                            &node_renderer,
                                            &|x, _y| x.clone(),
                                            render_type)
    }
}

pub fn chain_forward<T, U>(facts: Vec<(&String, &U)>,
                           rules: Vec<(&String, &Rule<T, U>)>,
                           origin_cache: &mut OriginCache)
                           -> Vec<(U, Bindings<T>, Origin)>
    where T: ConstraintValue,
          U: Unify<T>
{
    let mut new_facts: Vec<(U, Bindings<T>, Origin)> = Vec::new();

    for (ref rule_id, ref rule) in rules.into_iter() {
        for fact_arg_indexes in construct_fact_args_for_rule(rule.arg_count(), facts.len()).into_iter() {
            let (fact_ids, facts_data): (Vec<_>, Vec<_>) = utils::multi_index(&facts, &fact_arg_indexes)
                .into_iter()
                .unzip();
            let origin = Origin {
                source_id: (*rule_id).clone(),
                args: fact_ids.into_iter().cloned().collect(),
            };
            if origin_cache.has_item(&origin) {
                continue;
            } else {
                origin_cache.insert_item_mut(origin.clone());
            }
            match rule.apply(&facts_data, &Bindings::new()) {
                Some((new_fact, bindings)) => {
                    if is_new_fact(&new_fact, &facts) {
                        new_facts.push((new_fact, bindings, origin))
                    }
                }
                None => (),
            }
        }
    }
    new_facts
}

fn construct_fact_args_for_rule(rule_arg_count: usize, fact_count: usize) -> Vec<Vec<usize>> {
    if rule_arg_count == 0 {
        return vec![Vec::new()];
    }
    if fact_count == 0 {
        return Vec::new();
    }
    let fact_indexes: Vec<usize> = (0..fact_count).collect();
    utils::permutations(fact_indexes, rule_arg_count)
}

fn is_new_fact<T, U>(f: &U, facts: &Vec<(&String, &U)>) -> bool
    where T: ConstraintValue,
          U: Unify<T>
{
    for &(_id, fact) in facts.iter() {
        if fact.equiv(f) {
            return false;
        }
    }
    return true;
}

#[cfg(test)]
mod tests;
<commit_msg>Inference engine now operates on anything that implements `Apply`<commit_after>use serde_json;
use std;
use std::collections::{BTreeMap, BTreeSet};
use std::collections::HashMap;
use std::marker::PhantomData;

use constraints::{Constraint, ConstraintValue};
use core::{Apply, Bindings, Unify};
use pedigree::{Origin, Pedigree, RenderType};
use utils;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Rule<T: ConstraintValue, U: Unify<T>> {
    #[serde(default)]
    pub constraints: Vec<Constraint>,
    #[serde(default = "Vec::default")]
    pub lhs: Vec<U>,
    pub rhs: U,
    #[serde(default)]
    _marker: PhantomData<T>,
}

impl<T, U> Apply<T, U> for Rule<T, U>
    where T: ConstraintValue,
          U: Unify<T>
{
    fn arg_count(&self) -> usize {
        self.lhs.len()
    }

    fn apply(&self, facts: &Vec<&U>, bindings: &Bindings<T>) -> Option<(U, Bindings<T>)> {
        self.unify(facts, bindings)
            .and_then(|bindings| self.solve_constraints(&bindings))
            .and_then(|bindings| self.apply_bindings(&bindings).and_then(|rhs| Some((rhs, bindings))))
    }

    fn constraints<'a>(&'a self) -> Vec<&'a Constraint> {
        self.constraints.iter().collect()
    }

    fn r_apply(&self, fact: &U, bindings: &Bindings<T>) -> Option<(Vec<U>, Bindings<T>)> {
        self.rhs
            .apply_bindings(bindings)
            .and_then(|bound_rhs| {
                bound_rhs.unify(&fact, bindings)
                    .and_then(|bindings| self.solve_constraints(&bindings))
                    .and_then(|bindings| {
                        utils::filter_map_all(&mut self.lhs.iter(), &|f| f.apply_bindings(&bindings))
                            .and_then(|bound_lhs| Some((bound_lhs, bindings.clone())))
                    })
            })
    }

    /// Construct a new version of this rule but with all variables updated to be unique for this invocation
    fn snowflake(&self, suffix: String) -> Self {
        // Gather all variables
        let mut variables = self.rhs.variables();
        for lhs in self.lhs.iter() {
            variables.extend(lhs.variables());
        }
        for constraint in self.constraints.iter() {
            variables.extend(constraint.variables());
        }

        let renamed_variable: HashMap<String, String> = variables.into_iter()
            .map(|var| (var.clone(), format!("{}::{}", var, suffix)))
            .collect();

        let rhs = self.rhs.rename_variables(&renamed_variable);
        let lhs: Vec<U> = self.lhs.iter().map(|lhs| lhs.rename_variables(&renamed_variable)).collect();
        let constraints: Vec<Constraint> = self.constraints.iter().map(|constraint| constraint.rename_variables(&renamed_variable)).collect();

        Rule {
            constraints: constraints,
            lhs: lhs,
            rhs: rhs,
            _marker: PhantomData,
        }
    }
}

impl<T, U> Rule<T, U>
    where T: ConstraintValue,
          U: Unify<T>
{
    pub fn unify(&self, facts: &Vec<&U>, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        utils::fold_while_some(bindings.clone(),
                               &mut self.lhs.iter().zip(facts.iter()),
                               &|bindings, (t1, t2)| t1.unify(t2, &bindings))
    }

    pub fn apply_bindings(&self, bindings: &Bindings<T>) -> Option<U> {
        self.rhs.apply_bindings(bindings)
    }

    fn solve_constraints(&self, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        Constraint::solve_many(self.constraints.iter().collect(), bindings).ok()
    }
}

impl<T, U> std::fmt::Display for Rule<T, U>
    where T: ConstraintValue,
          U: Unify<T>
{
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", serde_json::to_string(&self).unwrap())
    }
}

impl<T, U> Eq for Rule<T, U>
    where T: ConstraintValue,
          U: Unify<T>
{
}

#[derive(Clone, Debug, PartialEq)]
pub struct OriginCache {
    items: BTreeSet<Origin>,
}

impl OriginCache {
    pub fn new() -> Self {
        OriginCache { items: BTreeSet::new() }
    }

    pub fn has_item(&self, item: &Origin) -> bool {
        self.items.contains(item)
    }

    pub fn insert_item_mut(&mut self, item: Origin) {
        self.items.insert(item);
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct InferenceEngine<'a, T, U, A>
    where T: 'a + ConstraintValue,
          U: 'a + Unify<T>,
          A: 'a + Apply<T, U>
{
    pub rules: BTreeMap<&'a String, &'a A>,
    pub facts: BTreeMap<&'a String, &'a U>,
    // Facts derived from this inference process
    pub derived_facts: BTreeMap<String, U>,
    pub pedigree: Pedigree,
    pub prefix: String,
    // Used to check if an inference has already been performed,
    // allowing us to short-circuit a potentially expensive unification process.
    pub origin_cache: OriginCache,
    _marker: PhantomData<T>,
}

impl<'a, T, U, A> InferenceEngine<'a, T, U, A>
    where T: 'a + ConstraintValue,
          U: 'a + Unify<T>,
          A: 'a + Apply<T, U>
{
    pub fn new(prefix: String, rules: BTreeMap<&'a String, &'a A>, facts: BTreeMap<&'a String, &'a U>) -> Self {
        InferenceEngine {
            rules: rules,
            facts: facts,
            derived_facts: BTreeMap::new(),
            pedigree: Pedigree::new(),
            prefix: prefix,
            origin_cache: OriginCache::new(),
            _marker: PhantomData,
        }
    }

    pub fn all_facts(&'a self) -> Vec<(&'a String, &'a U)> {
        self.derived_facts
            .iter()
            .map(|(key, value)| (key, value))
            .chain(self.facts.iter().map(|(key, value)| (key.clone(), value.clone())))
            .collect()
    }

    pub fn chain_until_match(&self, max_iterations: usize, goal: &U) -> (Option<(U, String)>, Self) {
        self.chain_until(max_iterations,
                         &|f| goal.unify(f, &Bindings::new()).is_some())
    }

    pub fn chain_until(&self, max_iterations: usize, satisfied: &Fn(&U) -> bool) -> (Option<(U, String)>, Self) {
        let mut engine = self.clone();
        let mut target: Option<(U, String)> = None;
        for _idx in 0..max_iterations {
            for (fact, _bindings, origin) in engine.chain_forward().into_iter() {
                let id = engine.construct_id(&fact);

                if satisfied(&fact) {
                    target = Some((fact.clone(), id.clone()));
                }

                engine.pedigree.insert_mut(id.clone(), origin);
                engine.derived_facts.insert(id, fact);
            }
            if target.is_some() {
                break;
            }
        }
        (target, engine)
    }

    pub fn chain_forward(&mut self) -> Vec<(U, Bindings<T>, Origin)> {
        let mut origin_cache = self.origin_cache.clone();
        let results = chain_forward(self.all_facts(),
                                    self.rules.iter().map(|(id, rule)| (id.clone(), rule.clone())).collect(),
                                    &mut origin_cache);
        self.origin_cache = origin_cache;
        results
    }

    fn construct_id(&self, _fact: &U) -> String {
        format!("{}-{}", self.prefix, self.derived_facts.len())
    }

    pub fn render_inference_tree(&self, id: &String, render_type: RenderType) -> String {
        let node_renderer = |x| {
            self.facts
                .get(&x)
                .and_then(|y| Some(format!("{}", y)))
                .or_else(|| self.derived_facts.get(&x).and_then(|y| Some(format!("{}", y))))
                .or_else(|| self.rules.get(&x).and_then(|y| Some(format!("{}", y))))
                .unwrap_or(format!("{}?", x))
        };

        self.pedigree.render_inference_tree(id,
                                            &node_renderer,
                                            &node_renderer,
                                            &|x, _y| x.clone(),
                                            render_type)
    }
}

pub fn chain_forward<T, U, A>(facts: Vec<(&String, &U)>, rules: Vec<(&String, &A)>, origin_cache: &mut OriginCache) -> Vec<(U, Bindings<T>, Origin)>
    where T: ConstraintValue,
          U: Unify<T>,
          A: Apply<T, U>
{
    let mut new_facts: Vec<(U, Bindings<T>, Origin)> = Vec::new();

    for (ref rule_id, ref rule) in rules.into_iter() {
        for fact_arg_indexes in construct_fact_args_for_rule(rule.arg_count(), facts.len()).into_iter() {
            let (fact_ids, facts_data): (Vec<_>, Vec<_>) = utils::multi_index(&facts, &fact_arg_indexes)
                .into_iter()
                .unzip();
            let origin = Origin {
                source_id: (*rule_id).clone(),
                args: fact_ids.into_iter().cloned().collect(),
            };
            if origin_cache.has_item(&origin) {
                continue;
            } else {
                origin_cache.insert_item_mut(origin.clone());
            }
            match rule.apply(&facts_data, &Bindings::new()) {
                Some((new_fact, bindings)) => {
                    if is_new_fact(&new_fact, &facts) {
                        new_facts.push((new_fact, bindings, origin))
                    }
                }
                None => (),
            }
        }
    }
    new_facts
}

fn construct_fact_args_for_rule(rule_arg_count: usize, fact_count: usize) -> Vec<Vec<usize>> {
    if rule_arg_count == 0 {
        return vec![Vec::new()];
    }
    if fact_count == 0 {
        return Vec::new();
    }
    let fact_indexes: Vec<usize> = (0..fact_count).collect();
    utils::permutations(fact_indexes, rule_arg_count)
}

fn is_new_fact<T, U>(f: &U, facts: &Vec<(&String, &U)>) -> bool
    where T: ConstraintValue,
          U: Unify<T>
{
    for &(_id, fact) in facts.iter() {
        if fact.equiv(f) {
            return false;
        }
    }
    return true;
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>use game_state::{GameState, GameMode};
use command::{CommandProcessor, HelpInfo};
use items::Item;
use items::Item::*;

pub enum InventoryCommand {
  Buy(Item),
  Quit,
}

impl CommandProcessor<InventoryCommand> for InventoryCommand {
  fn prompt() -> &'static str { "What do you want to buy? " }

  fn get_help() -> Vec<HelpInfo> {
    let buy = |item: Item| format!("buy {} (${})", item, item.price());

    HelpInfo::list(vec![
      ('1', buy(Torch)),
      ('2', buy(Axe)),
      ('3', buy(Sword)),
      ('4', buy(Food)),
      ('5', buy(Amulet)),
      ('6', buy(Armor)),
      ('0', String::from("continue adventure")),
    ])
  }

  fn from_char(c: char) -> Option<InventoryCommand> {
    match c {
      '1' => Some(InventoryCommand::Buy(Torch)),
      '2' => Some(InventoryCommand::Buy(Axe)),
      '3' => Some(InventoryCommand::Buy(Sword)),
      '4' => Some(InventoryCommand::Buy(Food)),
      '5' => Some(InventoryCommand::Buy(Amulet)),
      '6' => Some(InventoryCommand::Buy(Armor)),
      _ => Some(InventoryCommand::Quit),
    }
  }
}

impl<'a> GameState<'a> {
  pub fn tick_inventory_mode(&mut self) {
    if self.show_desc {
      println!("Provisions & inventory\n");
      self.print_wealth();
      println!("");
      InventoryCommand::show_help();
      println!("");
      self.show_desc = false;
    }

    if let Some(cmd) = InventoryCommand::get() {
      match cmd {
        InventoryCommand::Buy(item) => {
          let price = item.price();
          if price > self.wealth {
            println!("You don't have enough money to buy that.");
          } else {
            self.wealth -= price;
            println!("You bought {}.", item);
            self.print_wealth();
            match item {
              Torch => self.light = true,
              Axe => self.axe = true,
              Sword => self.sword = true,
              Food => self.food += 1,
              Amulet => self.amulet = true,
              Armor => self.suit = true,
            }
          }
          println!("");
        },
        InventoryCommand::Quit => { self.set_mode(GameMode::Primary) },
      }
    }
  }
}
<commit_msg>Make code a bit less verbose.<commit_after>use game_state::{GameState, GameMode};
use command::{CommandProcessor, HelpInfo};
use items::Item;
use items::Item::*;

use self::InventoryCommand::*;

pub enum InventoryCommand {
  Buy(Item),
  Leave,
}

impl CommandProcessor<InventoryCommand> for InventoryCommand {
  fn prompt() -> &'static str { "What do you want to buy? " }

  fn get_help() -> Vec<HelpInfo> {
    let buy = |item: Item| format!("buy {} (${})", item, item.price());

    HelpInfo::list(vec![
      ('1', buy(Torch)),
      ('2', buy(Axe)),
      ('3', buy(Sword)),
      ('4', buy(Food)),
      ('5', buy(Amulet)),
      ('6', buy(Armor)),
      ('0', String::from("continue adventure")),
    ])
  }

  fn from_char(c: char) -> Option<InventoryCommand> {
    match c {
      '1' => Some(Buy(Torch)),
      '2' => Some(Buy(Axe)),
      '3' => Some(Buy(Sword)),
      '4' => Some(Buy(Food)),
      '5' => Some(Buy(Amulet)),
      '6' => Some(Buy(Armor)),
      _ => Some(Leave),
    }
  }
}

impl<'a> GameState<'a> {
  pub fn tick_inventory_mode(&mut self) {
    if self.show_desc {
      println!("Provisions & inventory\n");
      self.print_wealth();
      println!("");
      InventoryCommand::show_help();
      println!("");
      self.show_desc = false;
    }

    if let Some(cmd) = InventoryCommand::get() {
      match cmd {
        Buy(item) => {
          let price = item.price();
          if price > self.wealth {
            println!("You don't have enough money to buy that.");
          } else {
            self.wealth -= price;
            println!("You bought {}.", item);
            self.print_wealth();
            match item {
              Torch => self.light = true,
              Axe => self.axe = true,
              Sword => self.sword = true,
              Food => self.food += 1,
              Amulet => self.amulet = true,
              Armor => self.suit = true,
            }
          }
          println!("");
        },
        Leave => { self.set_mode(GameMode::Primary) },
      }
    }
  }
}
<|endoftext|>


<commit_before>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::ffi::OsStr;
use std::io;
use std::ops::{Deref, DerefMut};
use std::os::windows::ffi::OsStrExt;

use super::device::*;
use super::ffi::*;

pub enum SerialTimeouts {
    /// Read any byte upon available in input buffers
    ReadUponAvailable,

    /// Wait for all requested bytes to be available
    WaitToFill,
}

impl SerialTimeouts {
    fn as_raw(&self) -> COMMTIMEOUTS {
        match *self {
            SerialTimeouts::ReadUponAvailable => COMMTIMEOUTS::read_upon_available(),
            SerialTimeouts::WaitToFill => COMMTIMEOUTS::wait_to_fill(),
        }
    }
}

pub struct Serial {
    dev: Device
}

impl Serial {

    pub fn open(port: &str) -> io::Result<Serial> {
        let encoded_port: Vec<u16> = OsStr::new(port)
        	.encode_wide()
        	.chain(Some(0).into_iter())
        	.collect();
        let handle = checked_handle! { valid =>
            CreateFileW(
                encoded_port.as_ptr() as LPCWSTR,
          		GENERIC_READ | GENERIC_WRITE,
          		0,
          		0 as LPSECURITY_ATTRIBUTES,
           		OPEN_EXISTING,
          		FILE_FLAG_OVERLAPPED,
          		0 as HANDLE)
        };

        Ok(Serial {
            dev: Device::from_handle(handle)
        })
    }

    pub fn open_arduino(port: &str, baud_rate: usize) ->io::Result<Serial> {
        let mut port = try!(Serial::open(port));

        // Set the serial settings for Arduino
    	let mut dcb = try!(port.dcb());
		dcb.BaudRate = baud_rate as DWORD;
		dcb.ByteSize = 8;
		dcb.StopBits = 1;
		dcb.Parity = 0;
		dcb.setDtrControl();
		try!(port.set_dcb(&dcb));

		// Purge the buffers to eliminate accumulated messages prior to reset
        checked_result!(PurgeComm(port.handle(), PURGE_TXCLEAR | PURGE_RXCLEAR));
		Ok(port)
    }

    pub fn set_timeouts(&mut self, timeouts: &SerialTimeouts) -> io::Result<()> {
        let raw = timeouts.as_raw();
		checked_result!(SetCommTimeouts(self.dev.handle(), &raw as LPCCOMMTIMEOUTS));
		Ok(())
    }

    pub fn dcb(&self) -> io::Result<DCB> {
        let mut dcb = DCB::new();
        checked_result!(GetCommState(self.dev.handle(), &mut dcb as LPDCB));
        Ok(dcb)
    }

    pub fn set_dcb(&mut self, dcb: &DCB) -> io::Result<()> {
        checked_result!(SetCommState(self.dev.handle(), dcb as LPCDCB));
        Ok(())
    }
}

impl Deref for Serial {
    type Target = Device;

    fn deref(&self) -> &Device {
        &self.dev
    }
}

impl DerefMut for Serial {
    fn deref_mut(&mut self) -> &mut Device {
        &mut self.dev
    }
}

#[cfg(test)]
mod test {

	use std::time::Duration;

	use io::device::*;
	use io::iocp::*;

	use super::*;

	#[test]
	fn should_read_and_write() {
	    let mut iocp = CompletionPort::new().unwrap();
		let mut port = Serial::open_arduino("COM3", 9600).unwrap();
		port.set_timeouts(&SerialTimeouts::WaitToFill).unwrap();
		iocp.attach(&port).unwrap();

		port.request_read_bytes(6).unwrap();
		let dev = iocp.process_event(&Duration::from_millis(5000)).unwrap();
		assert_eq!(dev, port.id());
		let event = port.process_event();
		assert_eq!(event, Event::BytesRead(6));
		assert_eq!(port.recv_bytes(), b"Hello\n");

		port.request_write(b"FlightVars").unwrap();
		let dev = iocp.process_event(&Duration::from_millis(5000)).unwrap();
		assert_eq!(dev, port.id());
		let event = port.process_event();
		assert_eq!(event, Event::BytesWritten(10));

		port.reset_recv_buffer();
		port.request_read_bytes(19).unwrap();
		let dev = iocp.process_event(&Duration::from_millis(5000)).unwrap();
		assert_eq!(dev, port.id());
		let event = port.process_event();
		assert_eq!(event, Event::BytesRead(19));
		assert_eq!(port.recv_bytes(), b"Goodbye FlightVars\n");
	}
}<commit_msg>Ignore integration test for Arduino in `io::serial` <commit_after>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::ffi::OsStr;
use std::io;
use std::ops::{Deref, DerefMut};
use std::os::windows::ffi::OsStrExt;

use super::device::*;
use super::ffi::*;

pub enum SerialTimeouts {
    /// Read any byte upon available in input buffers
    ReadUponAvailable,

    /// Wait for all requested bytes to be available
    WaitToFill,
}

impl SerialTimeouts {
    fn as_raw(&self) -> COMMTIMEOUTS {
        match *self {
            SerialTimeouts::ReadUponAvailable => COMMTIMEOUTS::read_upon_available(),
            SerialTimeouts::WaitToFill => COMMTIMEOUTS::wait_to_fill(),
        }
    }
}

pub struct Serial {
    dev: Device
}

impl Serial {

    pub fn open(port: &str) -> io::Result<Serial> {
        let encoded_port: Vec<u16> = OsStr::new(port)
        	.encode_wide()
        	.chain(Some(0).into_iter())
        	.collect();
        let handle = checked_handle! { valid =>
            CreateFileW(
                encoded_port.as_ptr() as LPCWSTR,
          		GENERIC_READ | GENERIC_WRITE,
          		0,
          		0 as LPSECURITY_ATTRIBUTES,
           		OPEN_EXISTING,
          		FILE_FLAG_OVERLAPPED,
          		0 as HANDLE)
        };

        Ok(Serial {
            dev: Device::from_handle(handle)
        })
    }

    pub fn open_arduino(port: &str, baud_rate: usize) ->io::Result<Serial> {
        let mut port = try!(Serial::open(port));

        // Set the serial settings for Arduino
    	let mut dcb = try!(port.dcb());
		dcb.BaudRate = baud_rate as DWORD;
		dcb.ByteSize = 8;
		dcb.StopBits = 1;
		dcb.Parity = 0;
		dcb.setDtrControl();
		try!(port.set_dcb(&dcb));

		// Purge the buffers to eliminate accumulated messages prior to reset
        checked_result!(PurgeComm(port.handle(), PURGE_TXCLEAR | PURGE_RXCLEAR));
		Ok(port)
    }

    pub fn set_timeouts(&mut self, timeouts: &SerialTimeouts) -> io::Result<()> {
        let raw = timeouts.as_raw();
		checked_result!(SetCommTimeouts(self.dev.handle(), &raw as LPCCOMMTIMEOUTS));
		Ok(())
    }

    pub fn dcb(&self) -> io::Result<DCB> {
        let mut dcb = DCB::new();
        checked_result!(GetCommState(self.dev.handle(), &mut dcb as LPDCB));
        Ok(dcb)
    }

    pub fn set_dcb(&mut self, dcb: &DCB) -> io::Result<()> {
        checked_result!(SetCommState(self.dev.handle(), dcb as LPCDCB));
        Ok(())
    }
}

impl Deref for Serial {
    type Target = Device;

    fn deref(&self) -> &Device {
        &self.dev
    }
}

impl DerefMut for Serial {
    fn deref_mut(&mut self) -> &mut Device {
        &mut self.dev
    }
}

#[cfg(test)]
mod test {

	use std::time::Duration;

	use io::device::*;
	use io::iocp::*;

	use super::*;

	/// A test that uses an Arduino board connected to serial port to interact with the machine.
    /// The following Arduino sketch is suggested:
    ///
    /// ```c++
    /// void setup() {
    ///   Serial.begin(9600);
    ///   Serial.setTimeout(50);
    /// }

    /// void loop() {
    ///   Serial.write("Hello\n");
    ///   String s = "";
    ///   while (s.length() == 0) {
    ///     s = Serial.readString();
    ///   }
    ///   Serial.write("Goodbye ");
    ///   Serial.write(s.c_str());
    ///   Serial.write("\n");
    /// }
    ///```
    ///
    /// Remove the #[ignore] attribute and set the correct COM port to execute it.
	#[test]
	#[ignore]
	fn should_read_and_write() {
	    let mut iocp = CompletionPort::new().unwrap();
		let mut port = Serial::open_arduino("COM3", 9600).unwrap();
		port.set_timeouts(&SerialTimeouts::WaitToFill).unwrap();
		iocp.attach(&port).unwrap();

		port.request_read_bytes(6).unwrap();
		let dev = iocp.process_event(&Duration::from_millis(5000)).unwrap();
		assert_eq!(dev, port.id());
		let event = port.process_event();
		assert_eq!(event, Event::BytesRead(6));
		assert_eq!(port.recv_bytes(), b"Hello\n");

		port.request_write(b"FlightVars").unwrap();
		let dev = iocp.process_event(&Duration::from_millis(5000)).unwrap();
		assert_eq!(dev, port.id());
		let event = port.process_event();
		assert_eq!(event, Event::BytesWritten(10));

		port.reset_recv_buffer();
		port.request_read_bytes(19).unwrap();
		let dev = iocp.process_event(&Duration::from_millis(5000)).unwrap();
		assert_eq!(dev, port.id());
		let event = port.process_event();
		assert_eq!(event, Event::BytesRead(19));
		assert_eq!(port.recv_bytes(), b"Goodbye FlightVars\n");
	}
}<|endoftext|>


<commit_before><commit_msg>Match against reference instead of value<commit_after><|endoftext|>


<commit_before><commit_msg>Remove invalid assertion<commit_after><|endoftext|>


<commit_before>extern crate rand;
extern crate sync;

#[cfg(test)]
extern crate test;

use std::slice;
use std::str;
use std::fmt;
use std::option;
use std::io::Timer;
use rand::{task_rng, Rng};

#[cfg(test)]
use test::BenchHarness;

static LIVE_CELL: char = '@';
static DEAD_CELL: char = '.';

fn main() {
  let mut brd = Board::new(65, 250).random();
  let mut timer = Timer::new().unwrap();

  let periodic = timer.periodic(64);
  loop {
    println!("\x1b[H\x1b[2J{}", brd);
    periodic.recv();
    brd = brd.next_generation();
  }
}

#[deriving(Eq)]
struct Board {
  board: ~[bool],
  rows: uint,
  cols: uint
}

impl Board {
  fn new(rows: uint, cols: uint) -> Board {
    let new_board = slice::from_elem(rows * cols, false);
    Board { board: new_board, rows: rows, cols: cols }
  }

  fn len(&self) -> uint {
    self.rows * self.cols
  }

  fn random(&self) -> Board {
    let board = task_rng().gen_vec(self.len());

    Board { board: board, rows: self.rows, cols: self.cols }
  }

  fn next_generation(&self) -> Board {
    let new_brd = slice::from_fn(self.len(), |cell| self.successor_cell(cell));
    Board { board: new_brd, rows: self.rows, cols: self.cols }
  }

  fn cell_live(&self, x: uint, y: uint) -> bool {
    if x >= self.cols || y >= self.rows {
      false
    } else {
      self.board[y * self.cols + x]
    }
  }

  fn living_neighbors(&self, x: uint, y: uint) -> uint {
    let neighbors = [
      self.cell_live(x-1, y-1), self.cell_live(x, y-1), self.cell_live(x+1, y-1),
      self.cell_live(x-1, y+0),                         self.cell_live(x+1, y+0),
      self.cell_live(x-1, y+1), self.cell_live(x, y+1), self.cell_live(x+1, y+1),
    ];
    neighbors.iter().count(|&x| x)
  }

  fn successor_cell(&self, cell:uint) -> bool {
    self.successor(cell % self.cols, cell / self.cols)
  }

  fn successor(&self, x:uint, y:uint) -> bool {
    let neighbors = self.living_neighbors(x, y);
    if self.cell_live(x, y) {
      neighbors == 2 || neighbors == 3
    } else {
      neighbors == 3
    }
  }

  fn from_str(string: &str) -> Option<Board> {
    let rows: ~[&str] = string.split_terminator('\n').collect();
    let (row_cnt, col_cnt) = (rows[0].len(), rows.len());

    if rows.iter().any(|x| x.len() != row_cnt) { return None; };

    let brd = option::collect(
      rows.concat().chars().map(|c| match c {
        LIVE_CELL => Some(true),
        DEAD_CELL => Some(false),
        _         => None
      })
    );

    match brd {
      Some(board) => Some(Board { board: board, rows: row_cnt, cols: col_cnt }),
      None        => None
    }
  }
}

impl fmt::Show for Board {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

    fn row_to_str(row: &[bool]) -> ~str {
      let chars: ~[char] = row.iter().map(|&cell|
        if cell {LIVE_CELL} else {DEAD_CELL}
      ).collect();
      str::from_chars(chars)
    }

    let rows: ~[~str] = self.board.as_slice().chunks(self.cols).map(|row|
      row_to_str(row)
    ).collect();

    write!(f.buf, "{}", rows.connect("\n"))
  }
}

impl Clone for Board {
  fn clone(&self) -> Board {
    Board { board: self.board.clone(), rows: self.rows, cols: self.cols }
  }
}

#[cfg(test)]
fn testing_board(n: int) -> Board {
  let brds = [
    ".@.\n" +
    ".@@\n" +
    ".@@\n"
    ,
    "...\n" +
    "@@@\n" +
    "...\n"
    ,
    ".@.\n" +
    ".@.\n" +
    ".@.\n" ];
  Board::from_str(brds[n]).unwrap()
}

#[test]
fn test_cell_live() {
  let brd = testing_board(0);
  assert!(!brd.cell_live(0, 0));
  assert!(brd.cell_live(2, 2));
}

#[test]
fn test_live_count() {
  let brd = testing_board(0);
  assert!(brd.living_neighbors(0, 0) == 2);
  assert!(brd.living_neighbors(2, 2) == 3);
}

#[test]
fn test_next_generation() {
  assert!(testing_board(1).next_generation() == testing_board(2))
}

#[bench]
fn bench_random(b: &mut BenchHarness) {
  let brd = Board::new(200,200);
  b.iter(|| {brd.random();})
}

#[bench]
fn bench_hundred_generations(b: &mut BenchHarness) {
  let mut brd = Board::new(200,200).random();
  b.iter(|| {
    for _ in range(0,100) { brd = brd.next_generation() }
  });
}
<commit_msg>parallelize the next generation calculation<commit_after>extern crate rand;
extern crate sync;

#[cfg(test)]
extern crate test;

use std::slice;
use std::cmp;
use std::str;
use std::fmt;
use std::rt;
use std::option;
use std::io::Timer;
use rand::{task_rng, Rng};
use sync::{Arc, Future};

#[cfg(test)]
use test::BenchHarness;

static LIVE_CELL: char = '@';
static DEAD_CELL: char = '.';

fn main() {
  let mut brd = Board::new(65, 250).random();
  let mut timer = Timer::new().unwrap();

  let periodic = timer.periodic(64);
  loop {
    println!("\x1b[H\x1b[2J{}", brd);
    periodic.recv();
    brd = brd.parallel_next_generation();
  }
}

#[deriving(Eq)]
struct Board {
  board: ~[bool],
  rows: uint,
  cols: uint
}

impl Board {
  fn new(rows: uint, cols: uint) -> Board {
    let new_board = slice::from_elem(rows * cols, false);
    Board { board: new_board, rows: rows, cols: cols }
  }

  fn len(&self) -> uint {
    self.rows * self.cols
  }

  fn random(&self) -> Board {
    let board = task_rng().gen_vec(self.len());

    Board { board: board, rows: self.rows, cols: self.cols }
  }

  fn next_generation(&self) -> Board {
    let new_brd = slice::from_fn(self.len(), |cell| self.successor_cell(cell));
    Board { board: new_brd, rows: self.rows, cols: self.cols }
  }

  fn parallel_next_generation(&self) -> Board {
    let length = self.len();
    let num_tasks = cmp::min(rt::default_sched_threads(), length);
    let shared_brd = Arc::new(self.clone());
    let all_tasks = slice::from_fn(length, |i| i);
    let tasks: ~[&[uint]] = all_tasks.chunks(length / num_tasks).collect();

    fn future_batch(task_brd: Arc<Board>, task: ~[uint]) -> Future<~[bool]> {
      Future::spawn(proc()
        task.iter().map(|&idx| task_brd.successor_cell(idx)).collect()
      )
    }

    let future_batches: ~[Future<~[bool]>] = tasks.move_iter().map(|task| {
      future_batch(shared_brd.clone(), task.into_owned())
    }).collect();

    let mut new_brd: ~[bool] = slice::with_capacity(length);

    for b in future_batches.move_iter() {
      new_brd = slice::append(new_brd, b.unwrap());
    }

    Board { board: new_brd, rows: self.rows, cols: self.cols }
  }

  fn cell_live(&self, x: uint, y: uint) -> bool {
    !(x >= self.cols || y >= self.rows) && self.board[y * self.cols + x]
  }

  fn living_neighbors(&self, x: uint, y: uint) -> uint {
    let neighbors = [
      self.cell_live(x-1, y-1), self.cell_live(x, y-1), self.cell_live(x+1, y-1),
      self.cell_live(x-1, y+0),                         self.cell_live(x+1, y+0),
      self.cell_live(x-1, y+1), self.cell_live(x, y+1), self.cell_live(x+1, y+1),
    ];
    neighbors.iter().count(|&x| x)
  }

  fn successor_cell(&self, cell:uint) -> bool {
    self.successor(cell % self.cols, cell / self.cols)
  }

  fn successor(&self, x:uint, y:uint) -> bool {
    let neighbors = self.living_neighbors(x, y);
    if self.cell_live(x, y) {
      neighbors == 2 || neighbors == 3
    } else {
      neighbors == 3
    }
  }

  fn from_str(string: &str) -> Option<Board> {
    let rows: ~[&str] = string.split_terminator('\n').collect();
    let (row_cnt, col_cnt) = (rows[0].len(), rows.len());

    if rows.iter().any(|x| x.len() != row_cnt) { return None; };

    let brd = option::collect(
      rows.concat().chars().map(|c| match c {
        LIVE_CELL => Some(true),
        DEAD_CELL => Some(false),
        _         => None
      })
    );

    match brd {
      Some(board) => Some(Board { board: board, rows: row_cnt, cols: col_cnt }),
      None        => None
    }
  }
}

impl fmt::Show for Board {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

    fn row_to_str(row: &[bool]) -> ~str {
      let chars: ~[char] = row.iter().map(|&cell|
        if cell {LIVE_CELL} else {DEAD_CELL}
      ).collect();
      str::from_chars(chars)
    }

    let rows: ~[~str] = self.board.as_slice().chunks(self.cols).map(|row|
      row_to_str(row)
    ).collect();

    write!(f.buf, "{}", rows.connect("\n"))
  }
}

impl Clone for Board {
  fn clone(&self) -> Board {
    Board { board: self.board.clone(), rows: self.rows, cols: self.cols }
  }
}

#[cfg(test)]
fn testing_board(n: int) -> Board {
  let brds = [
    ".@.\n" +
    ".@@\n" +
    ".@@\n"
    ,
    "...\n" +
    "@@@\n" +
    "...\n"
    ,
    ".@.\n" +
    ".@.\n" +
    ".@.\n" ];
  Board::from_str(brds[n]).unwrap()
}

#[test]
fn test_cell_live() {
  let brd = testing_board(0);
  assert!(!brd.cell_live(0, 0));
  assert!(brd.cell_live(2, 2));
}

#[test]
fn test_live_count() {
  let brd = testing_board(0);
  assert!(brd.living_neighbors(0, 0) == 2);
  assert!(brd.living_neighbors(2, 2) == 3);
}

#[test]
fn test_next_generation() {
  assert!(testing_board(1).next_generation() == testing_board(2))
}

#[test]
fn test_parallel_next_generation() {
  assert!(testing_board(1).parallel_next_generation() == testing_board(2))
}

#[bench]
fn bench_random(b: &mut BenchHarness) {
  let brd = Board::new(200,200);
  b.iter(|| {brd.random();})
}

#[bench]
fn bench_hundred_generations(b: &mut BenchHarness) {
  let mut brd = Board::new(200,200).random();
  b.iter(|| {
    for _ in range(0,100) { brd = brd.next_generation() }
  });
}

#[bench]
fn bench_hundred_parallel_generations(b: &mut BenchHarness) {
  let mut brd = Board::new(200,200).random();
  b.iter(|| {
    for _ in range(0,100) { brd = brd.parallel_next_generation() }
  });
}
<|endoftext|>


<commit_before>use term_painter::ToStyle;
use term_painter::Color::*;
use std::io;

mod types;

// Game logic (board, initialization, valid move, set, play or finished)

/// Visualization of the boards.
fn print_boards(board1: &Vec<types::SubField>, board2: &Vec<types::SubField>) {
    println!("{}{}", "------------------ O W N   B O A R D ------------------",
        "------------------------- O P P O N E N T ----------------");

    let mut cnt = 9;

    for i in 0..10 {
        println!("");
        print!("{}  ", cnt);
        for j in 0..10 {
            if board1[j + i * 10] == types::SubField::Hit {
                print!(" {} ", Red.paint(board1[j + i * 10]));
            } else {
                print!(" {} ", board1[j + i * 10]);
            }
        }
        print!("        ");
        print!("{}  ", cnt);
        for j in 0..10 {
            if board2[j + i * 10] == types::SubField::Hit {
                print!(" {} ", Green.paint(board2[j + i * 10]));
            } else {
                print!(" {} ", board2[j + i * 10]);
            }
        }
        println!("");
        cnt -= 1;
    }
    println!("");
    println!("{}{}", "     A    B    C    D    E    F    G    H    I    J",
        "               A    B    C    D    E    F    G    H    I    J");
    println!("");
    println!("{}{}", "----------------------------------------------------",
        "--------------------------------------------------------------");
}

/// Reads a string from the terminal / user.
fn read_string() -> String {
    let mut buffer = String::new();
    io::stdin()
        .read_line(&mut buffer)
        .expect("something went horribly wrong...");

    /* Discard trailing newline */
    let new_len = buffer.trim_right().len();
    buffer.truncate(new_len);

    buffer
}

/// The actual placement of the ships.
fn place(player: &mut types::Player, ship: &types::ShipType) -> Result<(), String> {

    /* Vector to collect the indices for the possible ship position. */
    let mut indices = Vec::new();

    /* 100 --> invalid value */
    let mut input = 100;
    while input == 100 {
        input = get_input();
        match player.own_board[input] {
            types::SubField::Water => { indices.push(input); },
            _ => {
                println!("Invalid input, again please.");
                input = 100;
            },
        }
    }

    println!("{}{}", "Enter 'h' for a horizontal (upwards) orientation of the ship,",
        "'v' for a vertical (rightwards) one.");
    /* x --> invalid value */
    let mut orientation = "x".to_string();
    while orientation == "x" {
        orientation = read_string();
        if orientation != "h" && orientation != "v" {
            println!("Invalid input, again please.");
            orientation = "x".to_string();
        }
    }

    if orientation == "v" {
        for _ in 0..ship.size - 1 {
            /* Check to prevent out of bounds errors. */
            if input >= 10 {
                input -= 10;
            }
            /* input < 10 --> no field above */
            if player.own_board[input] != types::SubField::Water || input < 10 {
                return Err("Invalid position for this ship, please choose another coordinate."
                    .to_string())
            }
            indices.push(input);
        }
    } else {
        for _ in 0..ship.size - 1 {
            /* Check to prevent out of bounds errors. */
            if input < 99 {
                input += 1;
            }
            /* unit position == 9 --> no field to the right */
            if player.own_board[input] != types::SubField::Water || (input % 10) == 9 {
                return Err("Invalid position for this ship, please choose another coordinate."
                    .to_string())
            }
            indices.push(input);
        }
    }

    /* Places the ships on the board. */
    for i in indices.iter() {
        player.own_board[*i] = types::SubField::Ship;
    }

    Ok(())
}

/// Handles the initial ship placement for each player.
fn place_ships(mut player1: &mut types::Player, mut player2: &mut types::Player) {

    /* A vector of all the ships each player needs to place.
       The default version: #  Class of ship Size
                            1   Carrier       5
                            2   Battleship    4
                            3   Cruiser       3
                            4   Submarine     3
                            5   Destroyer     2
    */
    let s1 = types::ShipType{ name: "Carrier".to_string(), size: 5, amount: 1 };
    let s2 = types::ShipType{ name: "Battleship".to_string(), size: 4, amount: 2 };
    let s3 = types::ShipType{ name: "Cruiser".to_string(), size: 3, amount: 3 };
    let s4 = types::ShipType{ name: "Submarine".to_string(), size: 3, amount: 4 };
    let s5 = types::ShipType{ name: "Destroyer".to_string(), size: 2, amount: 5 };
    let ships = vec![s1, s2, s3, s4, s5];

    print_boards(&player1.own_board, &player1.op_board);

    /* Asks player1 to place the ships. */
    for i in ships.iter() {
        for _ in 0..i.amount {
            loop {
                println!("{}, please enter the first coordinate for your {:?} ({} fields).",
                    player1.name, i.name, i.size);
                match place(&mut player1, i) {
                    Ok(_) => { break; },
                    Err(e) => { println!("{}", e); },
                }
            }
            player1.capacity += i.size;
            print_boards(&player1.own_board, &player1.op_board);
        }
    }

    /* Asks player2 to place the ships. */
    for i in ships.iter() {
        for _ in 0..i.amount {
            loop {
                println!("{}, please enter the first coordinate for your {:?} ({} fields).",
                    player2.name, i.name, i.size);
                match place(&mut player2, i) {
                    Ok(_) => { break; },
                    Err(e) => { println!("{}", e); },
                }
            }
            player2.capacity += i.size;
            print_boards(&player2.own_board, &player2.op_board);
        }
    }
}

/// Reads the coordinates of a field from the user
/// and returns the corresponding index.
fn get_input() -> usize {

    /* 100 --> invalid */
    let mut input = 100;

    while input == 100 {
        input = match read_string().as_ref() {
            "A0" => 90, "A1" => 80,"A2" => 70, "A3" => 60, "A4" => 50,
            "A5" => 40, "A6" => 30, "A7" => 20, "A8" => 10, "A9" => 0,
            "B0" => 91, "B1" => 81, "B2" => 71, "B3" => 61, "B4" => 51,
            "B5" => 41, "B6" => 31, "B7" => 21, "B8" => 11, "B9" => 1,
            "C0" => 92, "C1" => 82, "C2" => 72, "C3" => 62, "C4" => 52,
            "C5" => 42, "C6" => 32, "C7" => 22, "C8" => 12, "C9" => 2,
            "D0" => 93, "D1" => 83, "D2" => 73, "D3" => 63, "D4" => 53,
            "D5" => 43, "D6" => 33, "D7" => 23, "D8" => 13, "D9" => 3,
            "E0" => 94, "E1" => 84, "E2" => 74, "E3" => 64, "E4" => 54,
            "E5" => 44, "E6" => 34, "E7" => 24, "E8" => 14, "E9" => 4,
            "F0" => 95, "F1" => 85, "F2" => 75, "F3" => 65, "F4" => 55,
            "F5" => 45, "F6" => 35, "F7" => 25, "F8" => 15, "F9" => 5,
            "G0" => 96, "G1" => 86, "G2" => 76, "G3" => 66, "G4" => 56,
            "G5" => 46, "G6" => 36, "G7" => 26, "G8" => 16, "G9" => 6,
            "H0" => 97, "H1" => 87, "H2" => 77, "H3" => 67, "H4" => 57,
            "H5" => 47, "H6" => 37, "H7" => 27, "H8" => 17, "H9" => 7,
            "I0" => 98, "I1" => 88, "I2" => 78, "I3" => 68, "I4" => 58,
            "I5" => 48, "I6" => 38, "I7" => 28, "I8" => 18, "I9" => 8,
            "J0" => 99, "J1" => 89, "J2" => 79, "J3" => 69, "J4" => 59,
            "J5" => 49, "J6" => 39, "J7" => 29, "J8" => 19, "J9" => 9,
            _ => 100,
        };

        if input == 100 {
            println!("Invalid field, try again.")
        }
    }
    input
}

/// Determines the type of the SubField that got hit
/// by the current move and sets it accordingly.
fn match_move(first: &mut types::Player, second: &mut types::Player, idx: usize) {

    match second.own_board[idx] {
        types::SubField::Water => {
            println!("Miss - try again.");
            first.op_board[idx] = types::SubField::WaterHit;
        }
        types::SubField::Ship => {
            println!("Hit!");
            first.op_board[idx] = types::SubField::Hit;
            second.own_board[idx] = types::SubField::Hit;
            second.capacity -= 1;
        },
        types::SubField::Hit => { println!("Already hit."); }
        types::SubField::WaterHit => { println!("Miss - try again."); }
    }
}

/// Lets the players perform their moves.
fn make_move(mut first: &mut types::Player, mut second: &mut types::Player) {
    println!("Enter coordinates, {}:", first.name);
    let input = get_input();
    match_move(&mut first, &mut second, input);
}

/// Returns whether all the player's ships got destroyed.
pub fn game_over(player: &types::Player) -> bool {
    player.capacity <= 0
}

/// Initializes the players and the boards and provides the
/// game loop which lets the players perform their moves alternately.
pub fn start_match() {

    /* Creates the initial (empty) boards (10 x 10) for player1. */
    let mut player1 = types::Player {
        own_board: vec![types::SubField::Water; 100],
        op_board: vec![types::SubField::Water; 100],
        capacity: 0,
        name: "Player1".to_string(),
    };
    /* Creates the initial (empty) boards (10 x 10) for player2. */
    let mut player2 = types::Player {
        own_board: vec![types::SubField::Water; 100],
        op_board: vec![types::SubField::Water; 100],
        capacity: 0,
        name: "Player2".to_string(),
    };

    /* Initializes the boards with the player's ships. */
    place_ships(&mut player1, &mut player2);

    loop {
        print_boards(&player1.own_board, &player1.op_board);
        make_move(&mut player1, &mut player2);
        if game_over(&player2) {
            println!("G A M E   O V E R");
            println!("Congratulations, Player1");
            break;
        }

        print_boards(&player2.own_board, &player2.op_board);
        make_move(&mut player2, &mut player1);
        if game_over(&player1) {
            println!("G A M E   O V E R");
            println!("Congratulations, Player2");
            break;
        }
    }
}
<commit_msg>minor changes<commit_after>use term_painter::ToStyle;
use term_painter::Color::*;
use std::io;

mod types;

// Game logic (board, initialization, valid move, set, play or finished)

/// Visualization of the boards.
fn print_boards(board1: &Vec<types::SubField>, board2: &Vec<types::SubField>) {
    println!("{}{}", "------------------ O W N   B O A R D ------------------",
        "-------------------------- O P P O N E N T ---------------");

    let mut cnt = 9;

    for i in 0..10 {
        println!("");
        print!("{}  ", cnt);
        for j in 0..10 {
            if board1[j + i * 10] == types::SubField::Hit {
                print!(" {} ", Red.paint(board1[j + i * 10]));
            } else {
                print!(" {} ", board1[j + i * 10]);
            }
        }
        print!("        ");
        print!("{}  ", cnt);
        for j in 0..10 {
            if board2[j + i * 10] == types::SubField::Hit {
                print!(" {} ", Green.paint(board2[j + i * 10]));
            } else {
                print!(" {} ", board2[j + i * 10]);
            }
        }
        println!("");
        cnt -= 1;
    }
    println!("");
    println!("{}{}", "     A    B    C    D    E    F    G    H    I    J",
        "               A    B    C    D    E    F    G    H    I    J");
    println!("");
    println!("{}{}", "----------------------------------------------------",
        "-------------------------------------------------------------");
}

/// Reads a string from the terminal / user.
fn read_string() -> String {
    let mut buffer = String::new();
    io::stdin()
        .read_line(&mut buffer)
        .expect("something went horribly wrong...");

    /* Discard trailing newline */
    let new_len = buffer.trim_right().len();
    buffer.truncate(new_len);

    buffer
}

/// The actual placement of the ships.
fn place(player: &mut types::Player, ship: &types::ShipType) -> Result<(), String> {

    /* Vector to collect the indices for the possible ship position. */
    let mut indices = Vec::new();

    /* 100 --> invalid value */
    let mut input = 100;
    while input == 100 {
        input = get_input();
        match player.own_board[input] {
            types::SubField::Water => { indices.push(input); },
            _ => {
                println!("Invalid input, again please.");
                input = 100;
            },
        }
    }

    println!("{}{}", "Enter 'h' for a horizontal (upwards) orientation of the ship,",
        "'v' for a vertical (rightwards) one.");
    /* x --> invalid value */
    let mut orientation = "x".to_string();
    while orientation == "x" {
        orientation = read_string();
        if orientation != "h" && orientation != "v" {
            println!("Invalid input, again please.");
            orientation = "x".to_string();
        }
    }

    if orientation == "v" {
        for _ in 0..ship.size - 1 {
            /* Check to prevent out of bounds errors. */
            if input >= 10 {
                input -= 10;
            }
            /* input < 10 --> no field above */
            if player.own_board[input] != types::SubField::Water || input < 10 {
                return Err("Invalid position for this ship, please choose another coordinate."
                    .to_string())
            }
            indices.push(input);
        }
    } else {
        for _ in 0..ship.size - 1 {
            /* Check to prevent out of bounds errors. */
            if input < 99 {
                input += 1;
            }
            /* unit position == 9 --> no field to the right */
            if player.own_board[input] != types::SubField::Water || (input % 10) == 9 {
                return Err("Invalid position for this ship, please choose another coordinate."
                    .to_string())
            }
            indices.push(input);
        }
    }

    /* Places the ships on the board. */
    for i in indices.iter() {
        player.own_board[*i] = types::SubField::Ship;
    }

    Ok(())
}

/// Handles the initial ship placement for each player.
fn place_ships(mut player1: &mut types::Player, mut player2: &mut types::Player) {

    /* A vector of all the ships each player needs to place.
       The default version: #  Class of ship Size
                            1   Carrier       5
                            2   Battleship    4
                            3   Cruiser       3
                            4   Submarine     3
                            5   Destroyer     2
    */
    let s1 = types::ShipType{ name: "Carrier".to_string(), size: 5, amount: 1 };
    let s2 = types::ShipType{ name: "Battleship".to_string(), size: 4, amount: 2 };
    let s3 = types::ShipType{ name: "Cruiser".to_string(), size: 3, amount: 3 };
    let s4 = types::ShipType{ name: "Submarine".to_string(), size: 3, amount: 4 };
    let s5 = types::ShipType{ name: "Destroyer".to_string(), size: 2, amount: 5 };
    let ships = vec![s1, s2, s3, s4, s5];

    print_boards(&player1.own_board, &player1.op_board);

    /* Asks player1 to place the ships. */
    for i in ships.iter() {
        for _ in 0..i.amount {
            loop {
                println!("{}, please enter the first coordinate for your {:?} ({} fields).",
                    player1.name, i.name, i.size);
                match place(&mut player1, i) {
                    Ok(_) => { break; },
                    Err(e) => { println!("{}", e); },
                }
            }
            player1.capacity += i.size;
            print_boards(&player1.own_board, &player1.op_board);
        }
    }

    /* Asks player2 to place the ships. */
    for i in ships.iter() {
        for _ in 0..i.amount {
            loop {
                println!("{}, please enter the first coordinate for your {:?} ({} fields).",
                    player2.name, i.name, i.size);
                match place(&mut player2, i) {
                    Ok(_) => { break; },
                    Err(e) => { println!("{}", e); },
                }
            }
            player2.capacity += i.size;
            print_boards(&player2.own_board, &player2.op_board);
        }
    }
}

/// Reads the coordinates of a field from the user
/// and returns the corresponding index.
fn get_input() -> usize {

    /* 100 --> invalid */
    let mut input = 100;

    while input == 100 {
        input = match read_string().as_ref() {
            "A0" => 90, "A1" => 80,"A2" => 70, "A3" => 60, "A4" => 50,
            "A5" => 40, "A6" => 30, "A7" => 20, "A8" => 10, "A9" => 0,
            "B0" => 91, "B1" => 81, "B2" => 71, "B3" => 61, "B4" => 51,
            "B5" => 41, "B6" => 31, "B7" => 21, "B8" => 11, "B9" => 1,
            "C0" => 92, "C1" => 82, "C2" => 72, "C3" => 62, "C4" => 52,
            "C5" => 42, "C6" => 32, "C7" => 22, "C8" => 12, "C9" => 2,
            "D0" => 93, "D1" => 83, "D2" => 73, "D3" => 63, "D4" => 53,
            "D5" => 43, "D6" => 33, "D7" => 23, "D8" => 13, "D9" => 3,
            "E0" => 94, "E1" => 84, "E2" => 74, "E3" => 64, "E4" => 54,
            "E5" => 44, "E6" => 34, "E7" => 24, "E8" => 14, "E9" => 4,
            "F0" => 95, "F1" => 85, "F2" => 75, "F3" => 65, "F4" => 55,
            "F5" => 45, "F6" => 35, "F7" => 25, "F8" => 15, "F9" => 5,
            "G0" => 96, "G1" => 86, "G2" => 76, "G3" => 66, "G4" => 56,
            "G5" => 46, "G6" => 36, "G7" => 26, "G8" => 16, "G9" => 6,
            "H0" => 97, "H1" => 87, "H2" => 77, "H3" => 67, "H4" => 57,
            "H5" => 47, "H6" => 37, "H7" => 27, "H8" => 17, "H9" => 7,
            "I0" => 98, "I1" => 88, "I2" => 78, "I3" => 68, "I4" => 58,
            "I5" => 48, "I6" => 38, "I7" => 28, "I8" => 18, "I9" => 8,
            "J0" => 99, "J1" => 89, "J2" => 79, "J3" => 69, "J4" => 59,
            "J5" => 49, "J6" => 39, "J7" => 29, "J8" => 19, "J9" => 9,
            _ => 100,
        };

        if input == 100 {
            println!("Invalid field, try again.")
        }
    }
    input
}

/// Determines the type of the SubField that got hit
/// by the current move and sets it accordingly.
fn match_move(first: &mut types::Player, second: &mut types::Player, idx: usize) {

    match second.own_board[idx] {
        types::SubField::Water => {
            println!("Miss - try again.");
            first.op_board[idx] = types::SubField::WaterHit;
        }
        types::SubField::Ship => {
            println!("Hit!");
            first.op_board[idx] = types::SubField::Hit;
            second.own_board[idx] = types::SubField::Hit;
            second.capacity -= 1;
        },
        types::SubField::Hit => { println!("Already hit."); }
        types::SubField::WaterHit => { println!("Miss - try again."); }
    }
}

/// Lets the players perform their moves.
fn make_move(mut first: &mut types::Player, mut second: &mut types::Player) {
    println!("Enter coordinates, {}:", first.name);
    let input = get_input();
    match_move(&mut first, &mut second, input);
}

/// Returns whether all the player's ships got destroyed.
pub fn game_over(player: &types::Player) -> bool {
    player.capacity <= 0
}

/// Initializes the players and the boards and provides the
/// game loop which lets the players perform their moves alternately.
pub fn start_match() {

    /* Creates the initial (empty) boards (10 x 10) for player1. */
    let mut player1 = types::Player {
        own_board: vec![types::SubField::Water; 100],
        op_board: vec![types::SubField::Water; 100],
        capacity: 0,
        name: "Player1".to_string(),
    };
    /* Creates the initial (empty) boards (10 x 10) for player2. */
    let mut player2 = types::Player {
        own_board: vec![types::SubField::Water; 100],
        op_board: vec![types::SubField::Water; 100],
        capacity: 0,
        name: "Player2".to_string(),
    };

    /* Initializes the boards with the player's ships. */
    place_ships(&mut player1, &mut player2);

    loop {
        print_boards(&player1.own_board, &player1.op_board);
        make_move(&mut player1, &mut player2);
        if game_over(&player2) {
            println!("G A M E   O V E R");
            println!("Congratulations, Player1");
            break;
        }

        print_boards(&player2.own_board, &player2.op_board);
        make_move(&mut player2, &mut player1);
        if game_over(&player1) {
            println!("G A M E   O V E R");
            println!("Congratulations, Player2");
            break;
        }
    }
}
<|endoftext|>


<commit_before>use std::convert::TryFrom;

use crate::errors::{Error, Result};

/// Namespaces seperate different types of directory entries - which might have the same title -
/// stored in the ZIM File Format.
#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum Namespace {
    Layout = b'-',
    Articles = b'A',
    ArticleMetaData = b'B',
    ImagesFile = b'I',
    ImagesText = b'J',
    Metadata = b'M',
    CategoriesText = b'U',
    CategoriesArticleList = b'V',
    CategoriesArticle = b'W',
    FulltextIndex = b'X',
}

impl TryFrom<u8> for Namespace {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        use Namespace::*;
        match value {
            b'-' => Ok(Layout),
            b'A' => Ok(Articles),
            b'B' => Ok(ArticleMetaData),
            b'I' => Ok(ImagesFile),
            b'J' => Ok(ImagesText),
            b'M' => Ok(Metadata),
            b'U' => Ok(CategoriesText),
            b'V' => Ok(CategoriesArticleList),
            b'W' => Ok(CategoriesArticle),
            b'X' => Ok(FulltextIndex),
            _ => Err(Error::InvalidNamespace),
        }
    }
}
<commit_msg>fix: handle new namespaces<commit_after>use std::convert::TryFrom;

use crate::errors::{Error, Result};

/// Namespaces seperate different types of directory entries - which might have the same title -
/// stored in the ZIM File Format.
#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum Namespace {
    Layout = b'-',
    Articles = b'A',
    ArticleMetaData = b'B',
    UserContent = b'C',
    ImagesFile = b'I',
    ImagesText = b'J',
    Metadata = b'M',
    CategoriesText = b'U',
    CategoriesArticleList = b'V',
    CategoriesArticle = b'W',
    FulltextIndex = b'X',
}

impl TryFrom<u8> for Namespace {
    type Error = Error;

    fn try_from(value: u8) -> Result<Self> {
        use Namespace::*;
        match value {
            b'-' => Ok(Layout),
            b'A' => Ok(Articles),
            b'B' => Ok(ArticleMetaData),
            b'C' => Ok(UserContent),
            b'I' => Ok(ImagesFile),
            b'J' => Ok(ImagesText),
            b'M' => Ok(Metadata),
            b'U' => Ok(CategoriesText),
            b'V' => Ok(CategoriesArticleList),
            b'W' => Ok(CategoriesArticle),
            b'X' => Ok(FulltextIndex),
            _ => Err(Error::InvalidNamespace),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The noise-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[pkgid = "noise#0.1"];
#[comment = "Procedural noise generation library."];
#[license = "ASL2"];
#[crate_type = "lib"];

pub use perlin::Perlin;
pub mod perlin;
<commit_msg>replace pkgid with crate_id<commit_after>// Copyright 2013 The noise-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[crate_id = "noise#0.1"];
#[comment = "Procedural noise generation library."];
#[license = "ASL2"];
#[crate_type = "lib"];

pub use perlin::Perlin;
pub mod perlin;
<|endoftext|>