<commit_before>//!
//! Low level term library
//!
//! Low level term represents memory layout of Term bits to store the data
//! as compact as possible while maintaining an acceptable performance
//!
use term::immediate;
use term::primary;
use term::raw::{TuplePtr};
use emulator::atom;
use term::raw::heapobj::HeapObjClass;

use rt_defs::{Word};
use term::lterm::aspect_binary::*;
use term::lterm::aspect_boxed::*;
use term::lterm::aspect_cp::*;
use term::lterm::aspect_list::*;
use term::lterm::aspect_smallint::*;
use term::lterm::aspect_tuple::*;

use std::cmp::Ordering;
use std::fmt;
use std::ptr;

//fn module() -> &'static str { "lterm: " }

/// A low-level term, packed conveniently in a Word, or containing a
/// pointer to heap.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LTerm {
  pub value: Word
}

impl Ord for LTerm {
  fn cmp(&self, other: &LTerm) -> Ordering {
    self.value.cmp(&other.value)
  }
}

impl PartialOrd for LTerm {
  fn partial_cmp(&self, other: &LTerm) -> Option<Ordering> {
    Some(self.value.cmp(&other.value))
  }
}

// TODO: Remove deadcode directive later and fix
#[allow(dead_code)]
impl LTerm {
  /// Access the raw Word value of the low-level term.
  #[inline]
  pub fn raw(&self) -> Word { self.value }

  /// Create a NON_VALUE.
  #[inline]
  pub fn non_value() -> LTerm {
    LTerm { value: immediate::IMM2_SPECIAL_NONVALUE_RAW }
  }

  /// Check whether a value is a NON_VALUE.
  #[inline]
  pub fn is_non_value(&self) -> bool {
    self.value == immediate::IMM2_SPECIAL_NONVALUE_RAW
  }

  /// Check whether a value is NOT a NON_VALUE.
  #[inline]
  pub fn is_value(&self) -> bool {
    ! self.is_non_value()
  }

  /// Get primary tag bits from a raw term
  #[inline]
  pub fn primary_tag(&self) -> Word {
    primary::get_tag(self.value)
  }

  /// Check whether a value has immediate1 bits as prefix.
  #[inline]
  pub fn is_immediate1(&self) -> bool {
    immediate::is_immediate1(self.value)
  }

  /// Check whether a value has immediate2 bits as prefix.
  #[inline]
  pub fn is_immediate2(&self) -> bool {
    immediate::is_immediate2(self.value)
  }

  /// Check whether a value has immediate3 bits as prefix.
  #[inline]
  pub fn is_immediate3(&self) -> bool {
    immediate::is_immediate3(self.value)
  }

  /// Check whether primary tag of a value is `TAG_HEADER`.
  #[inline]
  pub fn is_header(&self) -> bool {
    self.primary_tag() == primary::TAG_HEADER
  }

  /// Retrieve the raw value of a `LTerm`.
  #[inline]
  pub fn get_raw(&self) -> Word { self.value }

  //
  // Construction
  //

  /// Any raw word becomes a term, possibly invalid
  #[inline]
  pub fn from_raw(w: Word) -> LTerm {
    LTerm { value: w }
  }

  /// From internal process index create a pid. To create a process use vm::create_process
  #[inline]
  pub fn make_pid(pindex: Word) -> LTerm {
    LTerm { value: immediate::make_pid_raw(pindex) }
  }

  #[inline]
  pub fn make_xreg(n: Word) -> LTerm {
    LTerm { value: immediate::make_xreg_raw(n) }
  }

  #[inline]
  pub fn make_yreg(n: Word) -> LTerm {
    LTerm { value: immediate::make_yreg_raw(n) }
  }

  #[inline]
  pub fn make_fpreg(n: Word) -> LTerm {
    LTerm { value: immediate::make_fpreg_raw(n) }
  }

//  #[inline]
//  pub fn make_label(n: Word) -> LTerm {
//    LTerm { value: immediate::make_label_raw(n) }
//  }

  //
  // Tuples
  //

  pub fn header_get_arity(&self) -> Word {
    assert!(self.is_header());
    primary::header::get_arity(self.value)
  }

  pub fn header_get_type(&self) -> Word {
    assert!(self.is_header());
    primary::header::get_tag(self.value)
  }

  //
  // Formatting helpers
  //

  fn format_immed(&self, v: Word, f: &mut fmt::Formatter) -> fmt::Result {
    match immediate::get_imm1_tag(v) {
      immediate::TAG_IMM1_SMALL => write!(f, "{}", self.small_get_s()),

      immediate::TAG_IMM1_PID =>
        write!(f, "Pid({})", immediate::get_imm1_value(v)),

      immediate::TAG_IMM1_PORT =>
        write!(f, "Port({})", immediate::get_imm1_value(v)),

      immediate::TAG_IMM1_IMM2 =>

        match immediate::get_imm2_tag(v) {
          immediate::TAG_IMM2_CATCH =>
            write!(f, "Catch({})", immediate::get_imm2_value(v)),

          immediate::TAG_IMM2_SPECIAL => {
            if self.is_nil() {
              write!(f, "[]")
            } else if self.is_non_value() {
              write!(f, "NON_VALUE")
            } else if self.is_empty_binary() {
              write!(f, "<<>>")
            } else if self.is_empty_tuple() {
              write!(f, "{{}}")
            } else {
              write!(f, "Special(0x{:x})", immediate::get_imm2_value(v))
            }
          },

          immediate::TAG_IMM2_ATOM => {
            match atom::to_str(*self) {
              Ok(s) => write!(f, "'{}'", s),
              Err(_e) => write!(f, "Atom?"),
            }
          },

          immediate::TAG_IMM2_IMM3 => {
            let v3 = immediate::get_imm3_value(v);

            match immediate::get_imm3_tag(v) {
              immediate::TAG_IMM3_XREG => write!(f, "X({})", v3),
              immediate::TAG_IMM3_YREG => write!(f, "Y({})", v3),
              immediate::TAG_IMM3_FPREG => write!(f, "FP({})", v3),
              immediate::TAG_IMM3_OPCODE => write!(f, "Opcode({})", v3),
              _ => panic!("Immediate3 tag must be in range 0..3")
            }
          }
          _ => panic!("Immediate2 tag must be in range 0..3")
        },
      _ => panic!("Immediate1 tag must be in range 0..3")
    }
  }

  /// Attempt to display contents of a tagged header word and the words which
  /// follow it. Arg `p` if not null is used to fetch the following memory words
  /// and display more detail.
  fn format_header(v: Word, p: *const Word,
                   f: &mut fmt::Formatter) -> fmt::Result {
    let arity = primary::header::get_arity(v);
    let h_tag = primary::header::get_tag(v);

    match h_tag {
      primary::header::TAG_HEADER_TUPLE =>
        if p.is_null() {
          write!(f, "Tuple[{}]", arity)
        } else {
          unsafe { LTerm::format_tuple(p, f) }
        },
//      primary::header::TAG_HEADER_BIGNEG => write!(f, "BigNeg"),
//      primary::header::TAG_HEADER_BIGPOS => write!(f, "BigPos"),
      primary::header::TAG_HEADER_REF => write!(f, "Ref"),
      primary::header::TAG_HEADER_FUN => write!(f, "Fun"),
      primary::header::TAG_HEADER_FLOAT => write!(f, "Float"),
//      primary::header::TAG_HEADER_EXPORT => write!(f, "Export"),
//      primary::header::TAG_HEADER_REFCBIN => write!(f, "RefcBin"),
//      primary::header::TAG_HEADER_HEAPBIN => write!(f, "HeapBin"),
//      primary::header::TAG_HEADER_SUBBIN => write!(f, "SubBin"),
      primary::header::TAG_HEADER_HEAPOBJ => unsafe {
        let ho_ptr = p.offset(1);
        let hoclass = *(ho_ptr) as *const HeapObjClass;
        // Starting word of the heap object (the primary header word) becomes
        // `this` pointer for the heapobject, hence passing `p`.
        let s = ((*hoclass).fmt_str)(p) ;
        write!(f, "{}", s)
      },
      primary::header::TAG_HEADER_EXTPID => write!(f, "ExtPid"),
      primary::header::TAG_HEADER_EXTPORT => write!(f, "ExtPort"),
      primary::header::TAG_HEADER_EXTREF => write!(f, "ExtRef"),

      _ => panic!("Unexpected header tag {} value {}",
                  h_tag, primary::get_value(v))
    }
  }

  /// Given `p`, a pointer to tuple header word, format tuple contents.
  unsafe fn format_tuple(p: *const Word,
                  f: &mut fmt::Formatter) -> fmt::Result {
    let tptr = TuplePtr::from_pointer(p);

    write!(f, "{{")?;

    let arity = tptr.arity();
    for i in 0..arity {
      write!(f, "{}", tptr.get_element_base0(i))?;
      if i < arity - 1 {
        write!(f, ", ")?
      }
    }
    write!(f, "}}")
  }

  pub unsafe fn format_cons(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "[")?;

    let mut raw_cons = self.cons_get_ptr();
    loop {
      write!(f, "{}", raw_cons.hd())?;
      let tl = raw_cons.tl();
      if tl.is_nil() {
        // Proper list ends here, do not show the tail
        break;
      } else if tl.is_cons() {
        // List continues, print a comma and follow the tail
        write!(f, ", ")?;
        raw_cons = tl.cons_get_ptr();
      } else {
        // Improper list, show tail
        write!(f, "| {}", tl)?;
        break
      }
    }
    write!(f, "]")
  }

  pub unsafe fn format_cons_ascii(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "\"")?;

    let mut raw_cons = self.cons_get_ptr();
    loop {
      write!(f, "{}", raw_cons.hd().small_get_u() as u8 as char)?;
      let tl = raw_cons.tl();
      if tl.is_nil() {
        // Proper list ends here, do not show the tail
        break;
      } else if tl.is_cons() {
        // List continues, follow the tail
        raw_cons = tl.cons_get_ptr();
      } else {
        // Improper list, must not happen because we checked for proper NIL
        // tail in cons_is_ascii_string. Let's do some panic!
        panic!("Printing an improper list as ASCII string")
      }
    }
    write!(f, "\"")
  }

  /// Raw compare two term values.
  #[inline]
  pub fn is_same(a: LTerm, b: LTerm) -> bool {
    a.raw() == b.raw()
  }
}

// Printing low_level Terms as "{}"
impl fmt::Display for LTerm {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let v = self.value;

    match primary::get_tag(v) {
      primary::TAG_BOX => unsafe {
        let p = self.box_ptr();
        if self.is_cp() {
          write!(f, "CP({:p})", self.cp_get_ptr())
        } else {
          //write!(f, "Box(")?;
          LTerm::format_header(*p, p, f)
          //write!(f, ")")
        }
      },

      primary::TAG_CONS => unsafe {
        if self.cons_is_ascii_string() {
          self.format_cons_ascii(f)
        } else {
          self.format_cons(f)
        }
      },

      primary::TAG_IMMED => self.format_immed(v, f),

      primary::TAG_HEADER => {
        write!(f, "Header(")?;
        LTerm::format_header(v, ptr::null(), f)?;
        write!(f, ")")
      },

      _ => panic!("Primary tag must be in range 0..3")
    }
  }
} // trait Display

//
// Testing section
//

#[cfg(test)]
mod tests {
  use super::*;
  use std::ptr;
  use std::mem;

  #[test]
  fn test_nil_is_not_atom() {
    // Some obscure bit mishandling made nil be recognized as atom
    let n = nil();
    assert!(!n.is_atom(), "must not be an atom {} 0x{:x} imm2_pfx 0x{:x}, imm2atompfx 0x{:x}",
            n, n.raw(), immediate::get_imm2_prefix(n.raw()),
            immediate::IMM2_ATOM_PREFIX);
  }

  #[test]
  fn test_term_size() {
    assert_eq!(mem::size_of::<LTerm>(), rt_defs::WORD_BYTES);
  }

  #[test]
  fn test_small_unsigned() {
    let s1 = LTerm::make_small_u(1);
    assert_eq!(1, s1.small_get_u());

    let s2 = LTerm::make_small_u(MAX_UNSIGNED_SMALL);
    assert_eq!(MAX_UNSIGNED_SMALL, s2.small_get_u());
  }

  #[test]
  fn test_small_signed_1() {
    let s2 = LTerm::make_small_s(1);
    let s2_out = s2.small_get_s();
    assert_eq!(1, s2_out, "Expect 1, have 0x{:x}", s2_out);

    let s1 = LTerm::make_small_s(-1);
    let s1_out = s1.small_get_s();
    assert_eq!(-1, s1_out, "Expect -1, have 0x{:x}", s1_out);
  }

  #[test]
  fn test_small_signed_limits() {
    let s2 = LTerm::make_small_s(MAX_POS_SMALL);
    assert_eq!(MAX_POS_SMALL, s2.small_get_s());

    let s3 = LTerm::make_small_s(MIN_NEG_SMALL);
    assert_eq!(MIN_NEG_SMALL, s3.small_get_s());
  }

  #[test]
  fn test_cp() {
    let s1 = make_cp(ptr::null());
    assert_eq!(s1.cp_get_ptr(), ptr::null());
  }
}
<commit_msg>Recovered tests after refact<commit_after>//!
//! Low level term library
//!
//! Low level term represents memory layout of Term bits to store the data
//! as compact as possible while maintaining an acceptable performance
//!
use term::immediate;
use term::primary;
use term::raw::{TuplePtr};
use emulator::atom;
use term::raw::heapobj::HeapObjClass;

use rt_defs::{Word};
use term::lterm::aspect_binary::*;
use term::lterm::aspect_boxed::*;
use term::lterm::aspect_cp::*;
use term::lterm::aspect_list::*;
use term::lterm::aspect_smallint::*;
use term::lterm::aspect_tuple::*;

use std::cmp::Ordering;
use std::fmt;
use std::ptr;

//fn module() -> &'static str { "lterm: " }

/// A low-level term, packed conveniently in a Word, or containing a
/// pointer to heap.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LTerm {
  pub value: Word
}

impl Ord for LTerm {
  fn cmp(&self, other: &LTerm) -> Ordering {
    self.value.cmp(&other.value)
  }
}

impl PartialOrd for LTerm {
  fn partial_cmp(&self, other: &LTerm) -> Option<Ordering> {
    Some(self.value.cmp(&other.value))
  }
}

// TODO: Remove deadcode directive later and fix
#[allow(dead_code)]
impl LTerm {
  /// Access the raw Word value of the low-level term.
  #[inline]
  pub fn raw(&self) -> Word { self.value }

  /// Create a NON_VALUE.
  #[inline]
  pub fn non_value() -> LTerm {
    LTerm { value: immediate::IMM2_SPECIAL_NONVALUE_RAW }
  }

  /// Check whether a value is a NON_VALUE.
  #[inline]
  pub fn is_non_value(&self) -> bool {
    self.value == immediate::IMM2_SPECIAL_NONVALUE_RAW
  }

  /// Check whether a value is NOT a NON_VALUE.
  #[inline]
  pub fn is_value(&self) -> bool {
    ! self.is_non_value()
  }

  /// Get primary tag bits from a raw term
  #[inline]
  pub fn primary_tag(&self) -> Word {
    primary::get_tag(self.value)
  }

  /// Check whether a value has immediate1 bits as prefix.
  #[inline]
  pub fn is_immediate1(&self) -> bool {
    immediate::is_immediate1(self.value)
  }

  /// Check whether a value has immediate2 bits as prefix.
  #[inline]
  pub fn is_immediate2(&self) -> bool {
    immediate::is_immediate2(self.value)
  }

  /// Check whether a value has immediate3 bits as prefix.
  #[inline]
  pub fn is_immediate3(&self) -> bool {
    immediate::is_immediate3(self.value)
  }

  /// Check whether primary tag of a value is `TAG_HEADER`.
  #[inline]
  pub fn is_header(&self) -> bool {
    self.primary_tag() == primary::TAG_HEADER
  }

  /// Retrieve the raw value of a `LTerm`.
  #[inline]
  pub fn get_raw(&self) -> Word { self.value }

  //
  // Construction
  //

  /// Any raw word becomes a term, possibly invalid
  #[inline]
  pub fn from_raw(w: Word) -> LTerm {
    LTerm { value: w }
  }

  /// From internal process index create a pid. To create a process use vm::create_process
  #[inline]
  pub fn make_pid(pindex: Word) -> LTerm {
    LTerm { value: immediate::make_pid_raw(pindex) }
  }

  #[inline]
  pub fn make_xreg(n: Word) -> LTerm {
    LTerm { value: immediate::make_xreg_raw(n) }
  }

  #[inline]
  pub fn make_yreg(n: Word) -> LTerm {
    LTerm { value: immediate::make_yreg_raw(n) }
  }

  #[inline]
  pub fn make_fpreg(n: Word) -> LTerm {
    LTerm { value: immediate::make_fpreg_raw(n) }
  }

//  #[inline]
//  pub fn make_label(n: Word) -> LTerm {
//    LTerm { value: immediate::make_label_raw(n) }
//  }

  //
  // Tuples
  //

  pub fn header_get_arity(&self) -> Word {
    assert!(self.is_header());
    primary::header::get_arity(self.value)
  }

  pub fn header_get_type(&self) -> Word {
    assert!(self.is_header());
    primary::header::get_tag(self.value)
  }

  //
  // Formatting helpers
  //

  fn format_immed(&self, v: Word, f: &mut fmt::Formatter) -> fmt::Result {
    match immediate::get_imm1_tag(v) {
      immediate::TAG_IMM1_SMALL => write!(f, "{}", self.small_get_s()),

      immediate::TAG_IMM1_PID =>
        write!(f, "Pid({})", immediate::get_imm1_value(v)),

      immediate::TAG_IMM1_PORT =>
        write!(f, "Port({})", immediate::get_imm1_value(v)),

      immediate::TAG_IMM1_IMM2 =>

        match immediate::get_imm2_tag(v) {
          immediate::TAG_IMM2_CATCH =>
            write!(f, "Catch({})", immediate::get_imm2_value(v)),

          immediate::TAG_IMM2_SPECIAL => {
            if self.is_nil() {
              write!(f, "[]")
            } else if self.is_non_value() {
              write!(f, "NON_VALUE")
            } else if self.is_empty_binary() {
              write!(f, "<<>>")
            } else if self.is_empty_tuple() {
              write!(f, "{{}}")
            } else {
              write!(f, "Special(0x{:x})", immediate::get_imm2_value(v))
            }
          },

          immediate::TAG_IMM2_ATOM => {
            match atom::to_str(*self) {
              Ok(s) => write!(f, "'{}'", s),
              Err(_e) => write!(f, "Atom?"),
            }
          },

          immediate::TAG_IMM2_IMM3 => {
            let v3 = immediate::get_imm3_value(v);

            match immediate::get_imm3_tag(v) {
              immediate::TAG_IMM3_XREG => write!(f, "X({})", v3),
              immediate::TAG_IMM3_YREG => write!(f, "Y({})", v3),
              immediate::TAG_IMM3_FPREG => write!(f, "FP({})", v3),
              immediate::TAG_IMM3_OPCODE => write!(f, "Opcode({})", v3),
              _ => panic!("Immediate3 tag must be in range 0..3")
            }
          }
          _ => panic!("Immediate2 tag must be in range 0..3")
        },
      _ => panic!("Immediate1 tag must be in range 0..3")
    }
  }

  /// Attempt to display contents of a tagged header word and the words which
  /// follow it. Arg `p` if not null is used to fetch the following memory words
  /// and display more detail.
  fn format_header(v: Word, p: *const Word,
                   f: &mut fmt::Formatter) -> fmt::Result {
    let arity = primary::header::get_arity(v);
    let h_tag = primary::header::get_tag(v);

    match h_tag {
      primary::header::TAG_HEADER_TUPLE =>
        if p.is_null() {
          write!(f, "Tuple[{}]", arity)
        } else {
          unsafe { LTerm::format_tuple(p, f) }
        },
//      primary::header::TAG_HEADER_BIGNEG => write!(f, "BigNeg"),
//      primary::header::TAG_HEADER_BIGPOS => write!(f, "BigPos"),
      primary::header::TAG_HEADER_REF => write!(f, "Ref"),
      primary::header::TAG_HEADER_FUN => write!(f, "Fun"),
      primary::header::TAG_HEADER_FLOAT => write!(f, "Float"),
//      primary::header::TAG_HEADER_EXPORT => write!(f, "Export"),
//      primary::header::TAG_HEADER_REFCBIN => write!(f, "RefcBin"),
//      primary::header::TAG_HEADER_HEAPBIN => write!(f, "HeapBin"),
//      primary::header::TAG_HEADER_SUBBIN => write!(f, "SubBin"),
      primary::header::TAG_HEADER_HEAPOBJ => unsafe {
        let ho_ptr = p.offset(1);
        let hoclass = *(ho_ptr) as *const HeapObjClass;
        // Starting word of the heap object (the primary header word) becomes
        // `this` pointer for the heapobject, hence passing `p`.
        let s = ((*hoclass).fmt_str)(p) ;
        write!(f, "{}", s)
      },
      primary::header::TAG_HEADER_EXTPID => write!(f, "ExtPid"),
      primary::header::TAG_HEADER_EXTPORT => write!(f, "ExtPort"),
      primary::header::TAG_HEADER_EXTREF => write!(f, "ExtRef"),

      _ => panic!("Unexpected header tag {} value {}",
                  h_tag, primary::get_value(v))
    }
  }

  /// Given `p`, a pointer to tuple header word, format tuple contents.
  unsafe fn format_tuple(p: *const Word,
                  f: &mut fmt::Formatter) -> fmt::Result {
    let tptr = TuplePtr::from_pointer(p);

    write!(f, "{{")?;

    let arity = tptr.arity();
    for i in 0..arity {
      write!(f, "{}", tptr.get_element_base0(i))?;
      if i < arity - 1 {
        write!(f, ", ")?
      }
    }
    write!(f, "}}")
  }

  pub unsafe fn format_cons(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "[")?;

    let mut raw_cons = self.cons_get_ptr();
    loop {
      write!(f, "{}", raw_cons.hd())?;
      let tl = raw_cons.tl();
      if tl.is_nil() {
        // Proper list ends here, do not show the tail
        break;
      } else if tl.is_cons() {
        // List continues, print a comma and follow the tail
        write!(f, ", ")?;
        raw_cons = tl.cons_get_ptr();
      } else {
        // Improper list, show tail
        write!(f, "| {}", tl)?;
        break
      }
    }
    write!(f, "]")
  }

  pub unsafe fn format_cons_ascii(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "\"")?;

    let mut raw_cons = self.cons_get_ptr();
    loop {
      write!(f, "{}", raw_cons.hd().small_get_u() as u8 as char)?;
      let tl = raw_cons.tl();
      if tl.is_nil() {
        // Proper list ends here, do not show the tail
        break;
      } else if tl.is_cons() {
        // List continues, follow the tail
        raw_cons = tl.cons_get_ptr();
      } else {
        // Improper list, must not happen because we checked for proper NIL
        // tail in cons_is_ascii_string. Let's do some panic!
        panic!("Printing an improper list as ASCII string")
      }
    }
    write!(f, "\"")
  }

  /// Raw compare two term values.
  #[inline]
  pub fn is_same(a: LTerm, b: LTerm) -> bool {
    a.raw() == b.raw()
  }
}

// Printing low_level Terms as "{}"
impl fmt::Display for LTerm {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let v = self.value;

    match primary::get_tag(v) {
      primary::TAG_BOX => unsafe {
        let p = self.box_ptr();
        if self.is_cp() {
          write!(f, "CP({:p})", self.cp_get_ptr())
        } else {
          //write!(f, "Box(")?;
          LTerm::format_header(*p, p, f)
          //write!(f, ")")
        }
      },

      primary::TAG_CONS => unsafe {
        if self.cons_is_ascii_string() {
          self.format_cons_ascii(f)
        } else {
          self.format_cons(f)
        }
      },

      primary::TAG_IMMED => self.format_immed(v, f),

      primary::TAG_HEADER => {
        write!(f, "Header(")?;
        LTerm::format_header(v, ptr::null(), f)?;
        write!(f, ")")
      },

      _ => panic!("Primary tag must be in range 0..3")
    }
  }
} // trait Display

//
// Testing section
//

#[cfg(test)]
mod tests {
  use std::ptr;
  use std::mem;

  use rt_defs::{MIN_NEG_SMALL, MAX_POS_SMALL, MAX_UNSIGNED_SMALL, WORD_BYTES};
  use super::*;
  use term::lterm::aspect_smallint::*;
  use term::lterm::aspect_atom::*;

  #[test]
  fn test_nil_is_not_atom() {
    // Some obscure bit mishandling made nil be recognized as atom
    let n = nil();
    assert!(!n.is_atom(), "must not be an atom {} 0x{:x} imm2_pfx 0x{:x}, imm2atompfx 0x{:x}",
            n, n.raw(), immediate::get_imm2_prefix(n.raw()),
            immediate::IMM2_ATOM_PREFIX);
  }

  #[test]
  fn test_term_size() {
    assert_eq!(mem::size_of::<LTerm>(), WORD_BYTES);
  }

  #[test]
  fn test_small_unsigned() {
    let s1 = make_small_u(1);
    assert_eq!(1, s1.small_get_u());

    let s2 = make_small_u(MAX_UNSIGNED_SMALL);
    assert_eq!(MAX_UNSIGNED_SMALL, s2.small_get_u());
  }

  #[test]
  fn test_small_signed_1() {
    let s2 = make_small_s(1);
    let s2_out = s2.small_get_s();
    assert_eq!(1, s2_out, "Expect 1, have 0x{:x}", s2_out);

    let s1 = make_small_s(-1);
    let s1_out = s1.small_get_s();
    assert_eq!(-1, s1_out, "Expect -1, have 0x{:x}", s1_out);
  }

  #[test]
  fn test_small_signed_limits() {
    let s2 = make_small_s(MAX_POS_SMALL);
    assert_eq!(MAX_POS_SMALL, s2.small_get_s());

    let s3 = make_small_s(MIN_NEG_SMALL);
    assert_eq!(MIN_NEG_SMALL, s3.small_get_s());
  }

  #[test]
  fn test_cp() {
    let s1 = make_cp(ptr::null());
    assert_eq!(s1.cp_get_ptr(), ptr::null());
  }
}
<|endoftext|>


<commit_before>use uci_engine::UciEngine;
use uci;
use std::sync::Arc;
use std::sync::Mutex;
use uci_engine::UciOption;
use uci::TimeRestriction;
use uci::EngineComm;
use search_algorithms::board::Board;
use uci::UciInfo;
use search_algorithms::board::Color::*;
use search_algorithms::board::GameResult::*;
use search_algorithms::board::GameResult;
use std::ops::Add;
use time::Duration;

extern crate ordered_float;
use ordered_float::OrderedFloat;
use std::ops::Not;
use search_algorithms::alpha_beta;
use std::fmt::Debug;
use search_algorithms::alpha_beta::AlphaBeta;
use search_algorithms::board::ExtendedBoard;
use pgn::PgnBoard;
use std::hash::Hash;
use uci_engine::UciOptionType;
use uci::TimeInfo;
use std::time::Instant;

const GAME_TIME: Duration = Duration::from_millis(60000);
const INC_MS: Duration = Duration::from_millis(600);

const EVAL_DEPENDENCE : f32 = 20.0;

pub struct MonteCarlo<B: Board> {
    root: MonteCarloTree<B>,
    time_limit: uci::TimeRestriction,
    start_time: Instant,
    engine_comm : Arc<Mutex<EngineComm>>,
}

impl<B> UciEngine<B> for MonteCarlo<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    fn init() -> Self {
        MonteCarlo {
            root: MonteCarloTree::new_root(&mut B::start_board()),
            time_limit: TimeRestriction::Infinite,
            engine_comm: Arc::new(Mutex::new(EngineComm::new())),
            start_time: Instant::now(),
        }
    }

    fn uci_options(&mut self) -> Vec<UciOption> {
        vec![]
    }

    fn set_uci_option(&mut self, _: UciOption) {}

    fn search(mut self, mut board: B, time_limit: TimeRestriction, engine_comm: Arc<Mutex<EngineComm>>,
              _: Option<Vec<<B as Board>::Move>>) -> Box<Iterator<Item=UciInfo<B>>> {

        self.root = MonteCarloTree::new_root(&mut board);
        self.time_limit = time_limit;
        self.engine_comm = engine_comm;
        self.start_time = Instant::now();

        Box::new(DepthIterator { monte_carlo: self, root_board: board })
    }
}

struct DepthIterator<B: Board> {
    monte_carlo: MonteCarlo<B>,
    root_board: B,
}

impl<B> Iterator for DepthIterator<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    type Item = UciInfo<B>;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        for _ in 0..10 {
            let mut game = vec![];
            let result = self.monte_carlo.root.select(self.root_board.clone(), &mut game);

            let mut board = self.root_board.clone();
            for (i, mv) in game.into_iter().enumerate() {
                if i == 0 && board.side_to_move() == Black {
                    print!("{}... {} ", 1, board.move_to_san(&mv));
                }
                else if board.side_to_move() == White {
                    print!("{}. {} ", (i + 1) / 2 + 1, board.move_to_san(&mv));
                }
                else {
                    print!("{} ", board.move_to_san(&mv));
                }
                board.do_move(mv);
            }
            println!(" {:?}", result);
        }

        let root = &self.monte_carlo.root;
        println!("Root: {}, static winrate: {}, true winrate: {}, score: {:?}, exploration: {}",
                 root.static_eval, eval_to_win_pct(root.static_eval),
                 root.score.win_rate(),
                 root.score, root.expl_value(root.searches()));

        let (mv, _) = root.children.as_ref().unwrap().iter()
            .min_by_key(|&(_, ref child)| OrderedFloat(child.score.win_rate()))
            .unwrap();

        for (mv, child) in root.children.as_ref().unwrap() {
            println!("\tChild {:?}: {}, static winrate: {}, true winrate: {}, score: {:?}, exploration: {}",
                     mv,
                     child.static_eval, eval_to_win_pct(child.static_eval),
                     child.score.win_rate(),
                     child.score, child.expl_value(root.searches()));
        }
        println!();

        Some(UciInfo {
            color: self.root_board.side_to_move(),
            depth: 0,
            seldepth: 0,
            time: 0,
            nodes: root.searches(),
            hashfull: 0.0,
            pvs: vec![(alpha_beta::Score::Val(win_pct_to_eval(root.score.win_rate())), vec![mv.clone()])]
        })
    }
}

#[derive(Debug)]
struct MonteCarloTree<B: Board> {
    children: Option<Vec<(B::Move, MonteCarloTree<B>)>>,
    score: Score,
    static_eval: f32,
}

impl<B> MonteCarloTree<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    fn new_root(board: &mut B) -> MonteCarloTree<B> {
        MonteCarloTree {
            children: None,
            score: Score::new(),
            static_eval: board.static_eval(),
        }
    }

    fn searches(&self) -> u64 {
        self.score.wins + self.score.losses + self.score.draws
    }

    fn expl_value(self: &MonteCarloTree<B>, parent_searches: u64) -> f32 {

        let extra_losses = EVAL_DEPENDENCE - eval_to_win_pct(self.static_eval) * EVAL_DEPENDENCE;

        let exploitation = (self.score.draws as f32 * 0.5 + self.score.losses as f32 + extra_losses)
            / (self.searches() as f32 + EVAL_DEPENDENCE);

        let exploration = 1.42 * f32::sqrt(
            f32::ln(parent_searches as f32 + 1.0) / (self.searches() as f32 + EVAL_DEPENDENCE));
        exploitation + exploration
    }

    fn select(&mut self, mut board: B, game: &mut Vec<B::Move>) -> Score {
        let searches = self.searches();
        if let Some(result) = board.game_result() {
            let mut score = Score::new();
            match (result, board.side_to_move()) {
                (WhiteWin, White) | (BlackWin, Black) => score.wins += 1,
                (WhiteWin, Black) | (BlackWin, White) => score.losses += 1,
                (Draw, _) => score.draws += 1,
            }
            return score;
        }
        if let Some(children) = self.children.as_mut() {

            let child_index = (0..children.len())
                .max_by_key(|&i| OrderedFloat(children[i].1.expl_value(searches)))
                .unwrap().clone();

            let (ref mut mv, ref mut child) = children.get_mut(child_index).unwrap();
            board.do_move(mv.clone());
            game.push(mv.clone());

            let result = !child.select(board, game);

            self.score = self.score + result;
            return result;
        }

        return self.expand(board, game);
    }

    fn expand(&mut self, mut board: B, game: &mut Vec<B::Move>) -> Score {
        let mut moves = vec![];
        board.generate_moves(&mut moves);

        let mut engine: AlphaBeta<B> = AlphaBeta::init();

        engine.set_uci_option(UciOption {
            name: "multipv".to_string(),
            option_type: UciOptionType::Spin(moves.len() as i64, 1, 1)
        });

        engine.set_uci_option(UciOption {
            name: "hash".to_string(),
            option_type: UciOptionType::Spin(32, 1, 1)
        });

        let results = engine.best_moves_multipv(
            board.clone(),
            TimeRestriction::MoveTime(INC_MS * f32::sqrt(moves.len() as f32).ceil() as u32),
        None).unwrap();

        let children = results.into_iter()
            .map(|(score, mv)| {
                let child = MonteCarloTree {
                    children: None,
                    score: Score::new(),
                    static_eval: match score {
                        alpha_beta::Score::Val(val) => - val,
                        alpha_beta::Score::Draw(_) => 0.0,
                        alpha_beta::Score::Win(_) => -100.0,
                        alpha_beta::Score::Loss(_) => 100.0,
                    }
                };
                (mv, child)
            })
            .collect::<Vec<_>>();
        {
            let (mv, _) = children.get(0).unwrap();
            board.do_move(mv.clone());
            game.push(mv.clone());
        }

        self.children = Some(children);
        let result = !simulate(board, game);
        self.score = self.score + result;

        result
    }
}

fn simulate<B>(mut board: B, game: &mut Vec<B::Move>) -> Score
    where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {

    let start_color = board.side_to_move();

    let mut white_time = GAME_TIME;
    let mut black_time = GAME_TIME;

    loop {
        if let Some(result) = board.game_result() {
            let mut score = Score::new();
            match (result, board.side_to_move()) {
                (WhiteWin, White) | (BlackWin, Black) => score.wins += 1,
                (WhiteWin, Black) | (BlackWin, White) => score.losses += 1,
                (Draw, _) => score.draws += 1,
            }
            println!("{:?} ({} to move) on the board", result, board.side_to_move());
            return if board.side_to_move() == start_color { score } else { !score };
        }

        let turn_start = Instant::now();

        let mut engine: AlphaBeta<B> = AlphaBeta::init();

        engine.set_uci_option(UciOption {
            name: "hash".to_string(),
            option_type: UciOptionType::Spin(16, 1, 1)
        });

        let time_info = TimeInfo {
            white_time,
            black_time,
            white_inc: INC_MS,
            black_inc: INC_MS,
            moves_to_go: None
        };

        let (_, mv) = engine.best_move(
            board.clone(),
            TimeRestriction::GameTime(time_info),
            None)
            .unwrap(); // TODO: handle errors

        let time_elapsed = Instant::now() - turn_start;

        match board.side_to_move() {
            White if white_time < time_elapsed => {
                println!("White wins on time");
                return Score::from_game_result(&BlackWin)
            },
            Black if black_time < time_elapsed => {
                println!("Black wins on time");
                return Score::from_game_result(&WhiteWin)
            },
            White => {
                white_time -= time_elapsed;
                white_time += INC_MS;
            },
            Black => {
                black_time -= time_elapsed;
                black_time += INC_MS;
            },
        }

        game.push(mv.clone());
        board.do_move(mv);
    }
}

pub fn win_pct_to_eval(pct: f32) -> f32 {
    2.90680623072 * f32::tan(3.096181612 * (pct - 0.5))
}

pub fn eval_to_win_pct(cp: f32) -> f32 {
    (f32::atan(cp / 2.90680623072) / 3.096181612 + 0.5)
}

#[derive(PartialEq, Clone, Debug, Copy)]
pub struct Score {
    pub wins: u64,
    pub losses: u64,
    pub draws : u64,
}

impl Score {
    pub fn new() -> Self {
        Score { wins: 0, losses: 0, draws: 0 }
    }

    /// Creates a score object containing a single result
    pub fn from_game_result(result: &GameResult) -> Self {
        let mut score = Score::new();
        match *result {
            WhiteWin => score.wins += 1,
            BlackWin => score.losses += 1,
            Draw => score.draws += 1,
        }
        score
    }

    pub fn win_rate(&self) -> f32 {
        (self.wins as f32 + self.draws as f32 * 0.5) / (self.wins + self.draws + self.losses) as f32
    }
}

impl Add for Score {
    type Output = Score;

    fn add(self, rhs: Score) -> <Self as Add<Score>>::Output {
        Score {
            wins: self.wins + rhs.wins,
            losses: self.losses + rhs.losses,
            draws: self.draws + rhs.draws,
        }
    }
}

impl Not for Score {
    type Output = Self;

    fn not(self) -> <Self as Not>::Output {
        Score {
            wins: self.losses,
            losses: self.wins,
            draws: self.draws,
        }
    }
}<commit_msg>Cut mcts eval dependence and game time in half<commit_after>use uci_engine::UciEngine;
use uci;
use std::sync::Arc;
use std::sync::Mutex;
use uci_engine::UciOption;
use uci::TimeRestriction;
use uci::EngineComm;
use search_algorithms::board::Board;
use uci::UciInfo;
use search_algorithms::board::Color::*;
use search_algorithms::board::GameResult::*;
use search_algorithms::board::GameResult;
use std::ops::Add;
use time::Duration;

extern crate ordered_float;
use ordered_float::OrderedFloat;
use std::ops::Not;
use search_algorithms::alpha_beta;
use std::fmt::Debug;
use search_algorithms::alpha_beta::AlphaBeta;
use search_algorithms::board::ExtendedBoard;
use pgn::PgnBoard;
use std::hash::Hash;
use uci_engine::UciOptionType;
use uci::TimeInfo;
use std::time::Instant;

const GAME_TIME: Duration = Duration::from_millis(30000);
const INC_MS: Duration = Duration::from_millis(300);

const EVAL_DEPENDENCE : f32 = 10.0;

pub struct MonteCarlo<B: Board> {
    root: MonteCarloTree<B>,
    time_limit: uci::TimeRestriction,
    start_time: Instant,
    engine_comm : Arc<Mutex<EngineComm>>,
}

impl<B> UciEngine<B> for MonteCarlo<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    fn init() -> Self {
        MonteCarlo {
            root: MonteCarloTree::new_root(&mut B::start_board()),
            time_limit: TimeRestriction::Infinite,
            engine_comm: Arc::new(Mutex::new(EngineComm::new())),
            start_time: Instant::now(),
        }
    }

    fn uci_options(&mut self) -> Vec<UciOption> {
        vec![]
    }

    fn set_uci_option(&mut self, _: UciOption) {}

    fn search(mut self, mut board: B, time_limit: TimeRestriction, engine_comm: Arc<Mutex<EngineComm>>,
              _: Option<Vec<<B as Board>::Move>>) -> Box<Iterator<Item=UciInfo<B>>> {

        self.root = MonteCarloTree::new_root(&mut board);
        self.time_limit = time_limit;
        self.engine_comm = engine_comm;
        self.start_time = Instant::now();

        Box::new(DepthIterator { monte_carlo: self, root_board: board })
    }
}

struct DepthIterator<B: Board> {
    monte_carlo: MonteCarlo<B>,
    root_board: B,
}

impl<B> Iterator for DepthIterator<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    type Item = UciInfo<B>;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        for _ in 0..10 {
            let mut game = vec![];
            let result = self.monte_carlo.root.select(self.root_board.clone(), &mut game);

            let mut board = self.root_board.clone();
            for (i, mv) in game.into_iter().enumerate() {
                if i == 0 && board.side_to_move() == Black {
                    print!("{}... {} ", 1, board.move_to_san(&mv));
                }
                else if board.side_to_move() == White {
                    print!("{}. {} ", (i + 1) / 2 + 1, board.move_to_san(&mv));
                }
                else {
                    print!("{} ", board.move_to_san(&mv));
                }
                board.do_move(mv);
            }
            println!(" {:?}", result);
        }

        let root = &self.monte_carlo.root;
        println!("Root: {}, static winrate: {}, true winrate: {}, score: {:?}, exploration: {}",
                 root.static_eval, eval_to_win_pct(root.static_eval),
                 root.score.win_rate(),
                 root.score, root.expl_value(root.searches()));

        let (mv, _) = root.children.as_ref().unwrap().iter()
            .min_by_key(|&(_, ref child)| OrderedFloat(child.score.win_rate()))
            .unwrap();

        for (mv, child) in root.children.as_ref().unwrap() {
            println!("\tChild {:?}: {}, static winrate: {}, true winrate: {}, score: {:?}, exploration: {}",
                     mv,
                     child.static_eval, eval_to_win_pct(child.static_eval),
                     child.score.win_rate(),
                     child.score, child.expl_value(root.searches()));
        }
        println!();

        Some(UciInfo {
            color: self.root_board.side_to_move(),
            depth: 0,
            seldepth: 0,
            time: 0,
            nodes: root.searches(),
            hashfull: 0.0,
            pvs: vec![(alpha_beta::Score::Val(win_pct_to_eval(root.score.win_rate())), vec![mv.clone()])]
        })
    }
}

#[derive(Debug)]
struct MonteCarloTree<B: Board> {
    children: Option<Vec<(B::Move, MonteCarloTree<B>)>>,
    score: Score,
    static_eval: f32,
}

impl<B> MonteCarloTree<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    fn new_root(board: &mut B) -> MonteCarloTree<B> {
        MonteCarloTree {
            children: None,
            score: Score::new(),
            static_eval: board.static_eval(),
        }
    }

    fn searches(&self) -> u64 {
        self.score.wins + self.score.losses + self.score.draws
    }

    fn expl_value(self: &MonteCarloTree<B>, parent_searches: u64) -> f32 {

        let extra_losses = EVAL_DEPENDENCE - eval_to_win_pct(self.static_eval) * EVAL_DEPENDENCE;

        let exploitation = (self.score.draws as f32 * 0.5 + self.score.losses as f32 + extra_losses)
            / (self.searches() as f32 + EVAL_DEPENDENCE);

        let exploration = 1.42 * f32::sqrt(
            f32::ln(parent_searches as f32 + 1.0) / (self.searches() as f32 + EVAL_DEPENDENCE));
        exploitation + exploration
    }

    fn select(&mut self, mut board: B, game: &mut Vec<B::Move>) -> Score {
        let searches = self.searches();
        if let Some(result) = board.game_result() {
            let mut score = Score::new();
            match (result, board.side_to_move()) {
                (WhiteWin, White) | (BlackWin, Black) => score.wins += 1,
                (WhiteWin, Black) | (BlackWin, White) => score.losses += 1,
                (Draw, _) => score.draws += 1,
            }
            return score;
        }
        if let Some(children) = self.children.as_mut() {

            let child_index = (0..children.len())
                .max_by_key(|&i| OrderedFloat(children[i].1.expl_value(searches)))
                .unwrap().clone();

            let (ref mut mv, ref mut child) = children.get_mut(child_index).unwrap();
            board.do_move(mv.clone());
            game.push(mv.clone());

            let result = !child.select(board, game);

            self.score = self.score + result;
            return result;
        }

        return self.expand(board, game);
    }

    fn expand(&mut self, mut board: B, game: &mut Vec<B::Move>) -> Score {
        let mut moves = vec![];
        board.generate_moves(&mut moves);

        let mut engine: AlphaBeta<B> = AlphaBeta::init();

        engine.set_uci_option(UciOption {
            name: "multipv".to_string(),
            option_type: UciOptionType::Spin(moves.len() as i64, 1, 1)
        });

        engine.set_uci_option(UciOption {
            name: "hash".to_string(),
            option_type: UciOptionType::Spin(32, 1, 1)
        });

        let results = engine.best_moves_multipv(
            board.clone(),
            TimeRestriction::MoveTime(INC_MS * f32::sqrt(moves.len() as f32).ceil() as u32),
        None).unwrap();

        let children = results.into_iter()
            .map(|(score, mv)| {
                let child = MonteCarloTree {
                    children: None,
                    score: Score::new(),
                    static_eval: match score {
                        alpha_beta::Score::Val(val) => - val,
                        alpha_beta::Score::Draw(_) => 0.0,
                        alpha_beta::Score::Win(_) => -100.0,
                        alpha_beta::Score::Loss(_) => 100.0,
                    }
                };
                (mv, child)
            })
            .collect::<Vec<_>>();
        {
            let (mv, _) = children.get(0).unwrap();
            board.do_move(mv.clone());
            game.push(mv.clone());
        }

        self.children = Some(children);
        let result = !simulate(board, game);
        self.score = self.score + result;

        result
    }
}

fn simulate<B>(mut board: B, game: &mut Vec<B::Move>) -> Score
    where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {

    let start_color = board.side_to_move();

    let mut white_time = GAME_TIME;
    let mut black_time = GAME_TIME;

    loop {
        if let Some(result) = board.game_result() {
            let mut score = Score::new();
            match (result, board.side_to_move()) {
                (WhiteWin, White) | (BlackWin, Black) => score.wins += 1,
                (WhiteWin, Black) | (BlackWin, White) => score.losses += 1,
                (Draw, _) => score.draws += 1,
            }
            println!("{:?} ({} to move) on the board", result, board.side_to_move());
            return if board.side_to_move() == start_color { score } else { !score };
        }

        let turn_start = Instant::now();

        let mut engine: AlphaBeta<B> = AlphaBeta::init();

        engine.set_uci_option(UciOption {
            name: "hash".to_string(),
            option_type: UciOptionType::Spin(16, 1, 1)
        });

        let time_info = TimeInfo {
            white_time,
            black_time,
            white_inc: INC_MS,
            black_inc: INC_MS,
            moves_to_go: None
        };

        let (_, mv) = engine.best_move(
            board.clone(),
            TimeRestriction::GameTime(time_info),
            None)
            .unwrap(); // TODO: handle errors

        let time_elapsed = Instant::now() - turn_start;

        match board.side_to_move() {
            White if white_time < time_elapsed => {
                println!("White wins on time");
                return Score::from_game_result(&BlackWin)
            },
            Black if black_time < time_elapsed => {
                println!("Black wins on time");
                return Score::from_game_result(&WhiteWin)
            },
            White => {
                white_time -= time_elapsed;
                white_time += INC_MS;
            },
            Black => {
                black_time -= time_elapsed;
                black_time += INC_MS;
            },
        }

        game.push(mv.clone());
        board.do_move(mv);
    }
}

pub fn win_pct_to_eval(pct: f32) -> f32 {
    2.90680623072 * f32::tan(3.096181612 * (pct - 0.5))
}

pub fn eval_to_win_pct(cp: f32) -> f32 {
    (f32::atan(cp / 2.90680623072) / 3.096181612 + 0.5)
}

#[derive(PartialEq, Clone, Debug, Copy)]
pub struct Score {
    pub wins: u64,
    pub losses: u64,
    pub draws : u64,
}

impl Score {
    pub fn new() -> Self {
        Score { wins: 0, losses: 0, draws: 0 }
    }

    /// Creates a score object containing a single result
    pub fn from_game_result(result: &GameResult) -> Self {
        let mut score = Score::new();
        match *result {
            WhiteWin => score.wins += 1,
            BlackWin => score.losses += 1,
            Draw => score.draws += 1,
        }
        score
    }

    pub fn win_rate(&self) -> f32 {
        (self.wins as f32 + self.draws as f32 * 0.5) / (self.wins + self.draws + self.losses) as f32
    }
}

impl Add for Score {
    type Output = Score;

    fn add(self, rhs: Score) -> <Self as Add<Score>>::Output {
        Score {
            wins: self.wins + rhs.wins,
            losses: self.losses + rhs.losses,
            draws: self.draws + rhs.draws,
        }
    }
}

impl Not for Score {
    type Output = Self;

    fn not(self) -> <Self as Not>::Output {
        Score {
            wins: self.losses,
            losses: self.wins,
            draws: self.draws,
        }
    }
}<|endoftext|>


<commit_before>//! The CXX code generator for constructing and compiling C++ code.
//!
//! This is intended to be used from Cargo build scripts to execute CXX's
//! C++ code generator, set up any additional compiler flags depending on
//! the use case, and make the C++ compiler invocation.
//!
//!

//!
//! # Example
//!
//! Example of a canonical Cargo build script that builds a CXX bridge:
//!
//! ```no_run
//! // build.rs
//!
//! fn main() {
//!     cxx_build::bridge("src/main.rs")
//!         .file("../demo-cxx/demo.cc")
//!         .flag("-std=c++11")
//!         .compile("cxxbridge-demo");
//!
//!     println!("cargo:rerun-if-changed=src/main.rs");
//!     println!("cargo:rerun-if-changed=../demo-cxx/demo.h");
//!     println!("cargo:rerun-if-changed=../demo-cxx/demo.cc");
//! }
//! ```
//!
//! A runnable working setup with this build script is shown in the
//! *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx].
//!
//! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx
//!
//!

//!
//! # Alternatives
//!
//! For use in non-Cargo builds like Bazel or Buck, CXX provides an
//! alternate way of invoking the C++ code generator as a standalone command
//! line tool. The tool is packaged as the `cxxbridge-cmd` crate.
//!
//! ```bash
//! $ cargo install cxxbridge-cmd  # or build it from the repo
//!
//! $ cxxbridge src/main.rs --header > path/to/mybridge.h
//! $ cxxbridge src/main.rs > path/to/mybridge.cc
//! ```

#![allow(
    clippy::inherent_to_string,
    clippy::needless_doctest_main,
    clippy::new_without_default,
    clippy::toplevel_ref_arg
)]

mod error;
mod gen;
mod paths;
mod syntax;

use crate::error::Result;
use crate::gen::Opt;
use anyhow::anyhow;
use std::fs;
use std::io::{self, Write};
use std::path::Path;
use std::process;

/// This returns a [`cc::Build`] on which you should continue to set up any
/// additional source files or compiler flags, and lastly call its [`compile`]
/// method to execute the C++ build.
///
/// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile
#[must_use]
pub fn bridge(rust_source_file: impl AsRef<Path>) -> cc::Build {
    match try_generate_bridge(rust_source_file.as_ref()) {
        Ok(build) => build,
        Err(err) => {
            let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err));
            process::exit(1);
        }
    }
}

fn try_generate_bridge(rust_source_file: &Path) -> Result<cc::Build> {
    let header = gen::do_generate_header(rust_source_file, Opt::default());
    let header_path = paths::out_with_extension(rust_source_file, ".h")?;
    fs::create_dir_all(header_path.parent().unwrap())?;
    fs::write(&header_path, header)?;
    paths::symlink_header(&header_path, rust_source_file);

    let bridge = gen::do_generate_bridge(rust_source_file, Opt::default());
    let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?;
    fs::write(&bridge_path, bridge)?;
    let mut build = paths::cc_build();
    build.file(&bridge_path);

    let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h");
    let _ = fs::create_dir_all(cxx_h.parent().unwrap());
    let _ = fs::remove_file(cxx_h);
    let _ = fs::write(cxx_h, gen::include::HEADER);

    Ok(build)
}
<commit_msg>Support code generation for multiple cxx::bridge files in one Build<commit_after>//! The CXX code generator for constructing and compiling C++ code.
//!
//! This is intended to be used from Cargo build scripts to execute CXX's
//! C++ code generator, set up any additional compiler flags depending on
//! the use case, and make the C++ compiler invocation.
//!
//!

//!
//! # Example
//!
//! Example of a canonical Cargo build script that builds a CXX bridge:
//!
//! ```no_run
//! // build.rs
//!
//! fn main() {
//!     cxx_build::bridge("src/main.rs")
//!         .file("../demo-cxx/demo.cc")
//!         .flag("-std=c++11")
//!         .compile("cxxbridge-demo");
//!
//!     println!("cargo:rerun-if-changed=src/main.rs");
//!     println!("cargo:rerun-if-changed=../demo-cxx/demo.h");
//!     println!("cargo:rerun-if-changed=../demo-cxx/demo.cc");
//! }
//! ```
//!
//! A runnable working setup with this build script is shown in the
//! *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx].
//!
//! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx
//!
//!

//!
//! # Alternatives
//!
//! For use in non-Cargo builds like Bazel or Buck, CXX provides an
//! alternate way of invoking the C++ code generator as a standalone command
//! line tool. The tool is packaged as the `cxxbridge-cmd` crate.
//!
//! ```bash
//! $ cargo install cxxbridge-cmd  # or build it from the repo
//!
//! $ cxxbridge src/main.rs --header > path/to/mybridge.h
//! $ cxxbridge src/main.rs > path/to/mybridge.cc
//! ```

#![allow(
    clippy::inherent_to_string,
    clippy::needless_doctest_main,
    clippy::new_without_default,
    clippy::toplevel_ref_arg
)]

mod error;
mod gen;
mod paths;
mod syntax;

use crate::error::Result;
use crate::gen::Opt;
use anyhow::anyhow;
use std::fs;
use std::io::{self, Write};
use std::iter;
use std::path::Path;
use std::process;

/// This returns a [`cc::Build`] on which you should continue to set up any
/// additional source files or compiler flags, and lastly call its [`compile`]
/// method to execute the C++ build.
///
/// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile
#[must_use]
pub fn bridge(rust_source_file: impl AsRef<Path>) -> cc::Build {
    bridges(iter::once(rust_source_file))
}

/// `cxx_build::bridge` but for when more than one file contains a
/// #\[cxx::bridge\] module.
///
/// ```no_run
/// let source_files = vec!["src/main.rs", "src/path/to/other.rs"];
/// cxx_build::bridges(source_files)
///     .file("../demo-cxx/demo.cc")
///     .flag("-std=c++11")
///     .compile("cxxbridge-demo");
/// ```
pub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) -> cc::Build {
    let mut build = paths::cc_build();
    for path in rust_source_files {
        if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) {
            let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err));
            process::exit(1);
        }
    }
    build
}

fn try_generate_bridge(build: &mut cc::Build, rust_source_file: &Path) -> Result<()> {
    let header = gen::do_generate_header(rust_source_file, Opt::default());
    let header_path = paths::out_with_extension(rust_source_file, ".h")?;
    fs::create_dir_all(header_path.parent().unwrap())?;
    fs::write(&header_path, header)?;
    paths::symlink_header(&header_path, rust_source_file);

    let bridge = gen::do_generate_bridge(rust_source_file, Opt::default());
    let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?;
    fs::write(&bridge_path, bridge)?;
    build.file(&bridge_path);

    let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h");
    let _ = fs::create_dir_all(cxx_h.parent().unwrap());
    let _ = fs::remove_file(cxx_h);
    let _ = fs::write(cxx_h, gen::include::HEADER);

    Ok(())
}
<|endoftext|>


<commit_before>/*! Hex map display demo */

extern crate num;
extern crate rustc_serialize;
extern crate image;
extern crate tiled;

#[macro_use] extern crate calx_ecs;
extern crate calx;

mod cmd;
mod render;
mod spr;
mod world;

use std::convert::{Into};
use std::collections::{HashSet};
use calx_ecs::{Entity};
use calx::backend::{CanvasBuilder, Canvas, CanvasUtil, Event, MouseButton, Key};
use calx::{V2, Rect, Rgba, color, Dir6};
use calx::{Projection, Kernel, KernelTerrain};

use spr::Spr;
use render::RenderTerrain;
use world::World;

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Terrain {
    Floor,
    Grass,
    Water,
    Tree,
    Wall,
    Door,
    Window,
    Magma,
    Rock,
    Void,
}

impl Terrain {
    pub fn new(id: u8) -> Terrain {
        // Tiled indexes start from 1.
        let id = id - 1;
        assert!(id <= Terrain::Rock as u8);
        unsafe {
            std::mem::transmute(id)
        }
    }

    pub fn can_walk(self) -> bool {
        use Terrain::*;
        match self {
            Floor | Grass | Door => true,
            _ => false,
        }
    }
}

impl KernelTerrain for Terrain {
    fn is_wall(&self) -> bool {
        use Terrain::*;
        match *self {
            Wall | Door | Window => true,
            _ => false
        }
    }

    fn is_block(&self) -> bool { *self == Terrain::Rock }
}

pub trait Drawable {
    fn draw(&self, ctx: &mut Canvas, offset: V2<f32>, z: f32);
}

pub struct SprDrawable {
    pub spr: Spr,
    pub fore: Rgba,
    pub back: Rgba,
}

impl SprDrawable {
    pub fn new<A: Into<Rgba>, B: Into<Rgba>>(spr: Spr, fore: A, back: B) -> SprDrawable {
        SprDrawable {
            spr: spr,
            fore: fore.into(),
            back: back.into(),
        }
    }
}

impl Drawable for SprDrawable {
    fn draw(&self, ctx: &mut Canvas, offset: V2<f32>, z: f32) {
        ctx.draw_image(self.spr.get(), offset, z, self.fore, self.back);
    }
}

pub struct Sprite {
    pub drawable: Box<Drawable>,
    pub pos: V2<f32>,

    sort_key: (i8, i32),
}

impl Sprite {
    pub fn new(drawable: Box<Drawable>, pos: V2<f32>, layer: i8) -> Sprite {
        // Fix some numerical inaccuracy noise.
        let pos = pos.map(|x| x.round());
        Sprite {
            drawable: drawable,
            pos: pos,
            sort_key: (layer, -pos.1 as i32)
        }
    }

    pub fn new_spr<A: Into<Rgba>, B: Into<Rgba>>(
        spr: Spr, fore: A, back: B, pos: V2<f32>, layer: i8) -> Sprite {
        Sprite::new(Box::new(SprDrawable::new(spr, fore, back)), pos, layer)
    }

    #[inline]
    pub fn cmp(&self, other: &Sprite) -> std::cmp::Ordering {
        // Cmp backwards, draw order is from large (far away) to small (close by) values.
        other.sort_key.cmp(&self.sort_key)
    }
}

fn unit_focus_sprites(screen_pos: V2<f32>) -> Vec<Sprite> {
    let mut ret = Vec::new();
    let color = color::LIME;
    for idx in 0..6 {
        ret.push(Sprite::new_spr(Spr::EdgeNW + idx, color, color::BLACK, screen_pos, 0));
    }
    ret
}

pub struct Effect {
    pub kind: Fx,
    pub life: u32,
    pub pos: V2<i32>,
}

impl Effect {
    pub fn new(kind: Fx, map_pos: V2<i32>) -> Effect {
        // TODO: Lifetime parametrization.
        Effect {
            kind: kind,
            life: 10,
            pos: map_pos,
        }
    }
}

#[derive(Eq, PartialEq)]
pub enum Fx {
    PathOk,
    PathBlocked,
}

#[derive(Eq, PartialEq)]
pub enum GameMode {
    RealTime,
    Paused,
    Rogue(Entity),
}

pub struct GameState {
    world: World,

    selected: HashSet<Entity>,
    mode: GameMode,

    screen_offset: V2<f32>,
    scroll_delta: V2<f32>,
    pub proj: Projection,
    mouse_cell: V2<i32>,
    drag_rect: Option<Rect<f32>>,

    effects: Vec<Box<Effect>>,
}

impl GameState {
    pub fn new(world: World) -> GameState {
        let screen_offset = V2(320.0f32, 0.0f32);
        GameState {
            world: world,
            selected: HashSet::new(),
            mode: GameMode::RealTime,
            screen_offset: screen_offset,
            scroll_delta: V2(0.0, 0.0),
            proj: Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap()
                .view_offset(screen_offset),
            mouse_cell: V2(-1, -1),
            drag_rect: None,
            effects: Vec::new(),
        }
    }

    pub fn render(&self, ctx: &mut Canvas) {
        let screen_rect = Rect(V2(-32.0f32, -32.0f32), V2(640.0f32 + 64.0, 360.0f32 + 64.0));

        let mut sprites = Vec::new();

        for pt in self.proj.inv_project_rectangle(&screen_rect).iter() {
            let pos = self.proj.project(pt);
            Kernel::new(|p| self.world.terrain_at(p), pt.map(|x| x as i32)).render(
                |layer, spr, fore, back| {
                    sprites.push(Sprite::new_spr(spr, fore, back, pos, layer));
                });
        }

        for &e in self.world.ecs.iter() {
            if let Some(spr) = cmd::sprite(&self.world, e, &self.proj) {
                // Highlight reticle on focused unit.
                if self.selected.contains(&e) {
                    for s in unit_focus_sprites(spr.pos).into_iter() {
                        sprites.push(s);
                    }
                }
                sprites.push(spr);
            }
        }

        sprites.sort_by(|a, b| a.cmp(&b));
        for spr in sprites.iter() {
            spr.drawable.draw(ctx, spr.pos, 0.5);
        }

        if let Some(rect) = self.drag_rect {
            ctx.draw_rect(&rect, 0.2, color::CYAN);
        }
    }

    pub fn update(&mut self) {
        if self.mode == GameMode::RealTime {
            self.world.update_active();
        } else {
            self.world.update_standby();
        }

        match self.mode {
            GameMode::Rogue(rogue) => {
                let proj = Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap();
                let spr = cmd::sprite(&self.world, rogue, &proj).unwrap();
                self.screen_offset = -spr.pos + V2(320.0, 180.0);
            }
            _ => { self.screen_offset = self.screen_offset - self.scroll_delta; }
        }

        self.proj = Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap()
            .view_offset(self.screen_offset);
    }

    fn go_rogue(&mut self) -> Option<Entity> {
        if let GameMode::Rogue(rogue) = self.mode { return Some(rogue); }

        if let Some(&rogue) = self.selected.iter().next() {
            self.selected.clear();
            self.mode = GameMode::Rogue(rogue);
            Some(rogue)
        } else {
            None
        }
    }

    fn toggle_pause(&mut self) {
        match self.mode {
            GameMode::Rogue(_) | GameMode::Paused => self.mode = GameMode::RealTime,
            GameMode::RealTime => self.mode = GameMode::Paused,
        }
    }

    fn rogue_step(&mut self, dir: Dir6) {
        if let Some(rogue) = self.go_rogue() {
            while !cmd::ready_to_act(&self.world, rogue) { self.world.update_active(); }
            cmd::step(&mut self.world, rogue, dir);
        }
    }

    fn add_effect(&mut self, effect: Box<Effect>) {
        self.effects.push(effect);
    }

    pub fn process_event(&mut self, evt: Event) {
        let scroll_speed = 8f32;
        match evt {
            Event::KeyPress(k) => {
                match k {
                    Key::J => { self.scroll_delta.0 = -1.0 * scroll_speed; }
                    Key::L => { self.scroll_delta.0 =  1.0 * scroll_speed; }
                    Key::I => { self.scroll_delta.1 = -1.0 * scroll_speed; }
                    Key::K => { self.scroll_delta.1 =  1.0 * scroll_speed; }
                    Key::Space => { self.toggle_pause(); }

                    Key::W => { self.rogue_step(Dir6::North); }
                    Key::E => { self.rogue_step(Dir6::NorthEast); }
                    Key::D => { self.rogue_step(Dir6::SouthEast); }
                    Key::S => { self.rogue_step(Dir6::South); }
                    Key::A => { self.rogue_step(Dir6::SouthWest); }
                    Key::Q => { self.rogue_step(Dir6::NorthWest); }
                    _ => {}
                }
            }

            Event::KeyRelease(k) => {
                match k {
                    Key::J => { self.scroll_delta.0 = 0.0; }
                    Key::L => { self.scroll_delta.0 = 0.0; }
                    Key::I => { self.scroll_delta.1 = 0.0; }
                    Key::K => { self.scroll_delta.1 = 0.0; }
                    _ => {}
                }
            }

            Event::MouseDrag(MouseButton::Left, p1, p2) => {
                self.drag_rect = Some(Rect::from_points(p1, p2))
            }

            Event::MouseMove(pos) => {
                self.mouse_cell = self.proj.inv_project(pos).map(|x| x.floor() as i32);
            }

            Event::MouseClick(MouseButton::Left) => {
                self.selected.clear();
                if let Some(p) = cmd::mob_at(&self.world, self.mouse_cell) {
                    self.selected.insert(p);
                }
            }

            Event::MouseClick(MouseButton::Right) => {
                let mut path_found = None;
                for &p in self.selected.iter() {
                    if cmd::is_player(&self.world, p) {
                        let cmd_pathed = cmd::move_to(&mut self.world, p, self.mouse_cell);
                        path_found = path_found.map_or(
                            Some(cmd_pathed), |x| Some(x || cmd_pathed));
                    }
                }

                let pos = self.mouse_cell;
                match path_found {
                    Some(true) =>
                        self.add_effect(Box::new(Effect::new(Fx::PathOk, pos))),
                    Some(false) =>
                        self.add_effect(Box::new(Effect::new(Fx::PathBlocked, pos))),
                    _ => {}
                }
            }

            Event::MouseDragEnd(MouseButton::Left, p1, p2) => {
                self.selected.clear();
                self.drag_rect = None;

                let cell_rect = Rect(V2(-8f32, -8f32), V2(16f32, 8f32));
                let select_rect = Rect::from_points(p1, p2);

                for pt in self.proj.inv_project_rectangle(&select_rect).iter() {
                    if !select_rect.intersects(&(cell_rect + self.proj.project(pt))) { continue; }

                    let pos = pt.map(|x| x.floor() as i32);

                    match cmd::mob_at(&self.world, pos) {
                        Some(e) if cmd::is_player(&self.world, e) => {
                            self.selected.insert(e);
                        }
                        _ => {}
                    }
                }
            }

            _ => {}
        }
    }
}

fn main() {
    let mut world = World::new();
    let tmx = include_str!("../assets/hexworld.tmx");
    world.load(&tiled::parse(tmx.as_bytes()).unwrap());
    let mut game = GameState::new(world);

    let mut builder = CanvasBuilder::new()
        .set_size(640, 360)
        .set_frame_interval(0.033f64)
        ;
    Spr::init(&mut builder);
    let mut ctx = builder.build();

    loop {
        match ctx.next_event() {
            Event::RenderFrame => {
                game.update();
                game.render(&mut ctx);
            }

            Event::Quit => { return; }

            Event::KeyPress(Key::F12) => {
                ctx.save_screenshot(&"azag");
            }

            Event::KeyPress(Key::Escape) => {
                return;
            }

            e => game.process_event(e),
        }
    }
}
<commit_msg>Effect feedback on move command<commit_after>/*! Hex map display demo */

extern crate num;
extern crate rustc_serialize;
extern crate image;
extern crate tiled;

#[macro_use] extern crate calx_ecs;
extern crate calx;

mod cmd;
mod render;
mod spr;
mod world;

use std::convert::{Into};
use std::collections::{HashSet};
use calx_ecs::{Entity};
use calx::backend::{CanvasBuilder, Canvas, CanvasUtil, Event, MouseButton, Key};
use calx::{V2, Rect, Rgba, color, Dir6};
use calx::{Projection, Kernel, KernelTerrain};

use spr::Spr;
use render::RenderTerrain;
use world::World;

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Terrain {
    Floor,
    Grass,
    Water,
    Tree,
    Wall,
    Door,
    Window,
    Magma,
    Rock,
    Void,
}

impl Terrain {
    pub fn new(id: u8) -> Terrain {
        // Tiled indexes start from 1.
        let id = id - 1;
        assert!(id <= Terrain::Rock as u8);
        unsafe {
            std::mem::transmute(id)
        }
    }

    pub fn can_walk(self) -> bool {
        use Terrain::*;
        match self {
            Floor | Grass | Door => true,
            _ => false,
        }
    }
}

impl KernelTerrain for Terrain {
    fn is_wall(&self) -> bool {
        use Terrain::*;
        match *self {
            Wall | Door | Window => true,
            _ => false
        }
    }

    fn is_block(&self) -> bool { *self == Terrain::Rock }
}

pub trait Drawable {
    fn draw(&self, ctx: &mut Canvas, offset: V2<f32>, z: f32);
}

pub struct SprDrawable {
    pub spr: Spr,
    pub fore: Rgba,
    pub back: Rgba,
}

impl SprDrawable {
    pub fn new<A: Into<Rgba>, B: Into<Rgba>>(spr: Spr, fore: A, back: B) -> SprDrawable {
        SprDrawable {
            spr: spr,
            fore: fore.into(),
            back: back.into(),
        }
    }
}

impl Drawable for SprDrawable {
    fn draw(&self, ctx: &mut Canvas, offset: V2<f32>, z: f32) {
        ctx.draw_image(self.spr.get(), offset, z, self.fore, self.back);
    }
}

pub struct Sprite {
    pub drawable: Box<Drawable>,
    pub pos: V2<f32>,

    sort_key: (i8, i32),
}

impl Sprite {
    pub fn new(drawable: Box<Drawable>, pos: V2<f32>, layer: i8) -> Sprite {
        // Fix some numerical inaccuracy noise.
        let pos = pos.map(|x| x.round());
        Sprite {
            drawable: drawable,
            pos: pos,
            sort_key: (layer, -pos.1 as i32)
        }
    }

    pub fn new_spr<A: Into<Rgba>, B: Into<Rgba>>(
        spr: Spr, fore: A, back: B, pos: V2<f32>, layer: i8) -> Sprite {
        Sprite::new(Box::new(SprDrawable::new(spr, fore, back)), pos, layer)
    }

    #[inline]
    pub fn cmp(&self, other: &Sprite) -> std::cmp::Ordering {
        // Cmp backwards, draw order is from large (far away) to small (close by) values.
        other.sort_key.cmp(&self.sort_key)
    }
}

fn unit_focus_sprites(screen_pos: V2<f32>) -> Vec<Sprite> {
    let mut ret = Vec::new();
    let color = color::LIME;
    for idx in 0..6 {
        ret.push(Sprite::new_spr(Spr::EdgeNW + idx, color, color::BLACK, screen_pos, 0));
    }
    ret
}

#[derive(Copy, Clone)]
pub struct Effect {
    pub kind: Fx,
    pub life: u32,
    pub pos: V2<i32>,
}

impl Effect {
    pub fn new(kind: Fx, map_pos: V2<i32>) -> Effect {
        // TODO: Lifetime parametrization.
        Effect {
            kind: kind,
            life: 10,
            pos: map_pos,
        }
    }

    pub fn update(&mut self) { if self.life > 0 { self.life -= 1; } }
    pub fn is_alive(&self) -> bool { self.life > 0 }

    pub fn sprite(&self, proj: &Projection) -> Sprite {
        // XXX: Cloning self, inefficient.
        Sprite::new(Box::new(*self), proj.project(self.pos.map(|x| (x as f32))), 0)
    }
}

impl Drawable for Effect {
    fn draw(&self, ctx: &mut Canvas, offset: V2<f32>, z: f32) {
        match self.kind {
            Fx::PathOk => {
                ctx.draw_rect(&Rect(offset, V2(16.0, 16.0)), z, color::LIME);
            }
            Fx::PathBlocked => {
                ctx.draw_line(2.0, offset, offset + V2(16.0, 16.0), z, color::RED);
                ctx.draw_line(2.0, offset + V2(0.0, 16.0), offset + V2(16.0, 0.0), z, color::RED);
            }
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Fx {
    PathOk,
    PathBlocked,
}

#[derive(Eq, PartialEq)]
pub enum GameMode {
    RealTime,
    Paused,
    Rogue(Entity),
}

pub struct GameState {
    world: World,

    selected: HashSet<Entity>,
    mode: GameMode,

    screen_offset: V2<f32>,
    scroll_delta: V2<f32>,
    pub proj: Projection,
    mouse_cell: V2<i32>,
    drag_rect: Option<Rect<f32>>,

    effects: Vec<Box<Effect>>,
}

impl GameState {
    pub fn new(world: World) -> GameState {
        let screen_offset = V2(320.0f32, 0.0f32);
        GameState {
            world: world,
            selected: HashSet::new(),
            mode: GameMode::RealTime,
            screen_offset: screen_offset,
            scroll_delta: V2(0.0, 0.0),
            proj: Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap()
                .view_offset(screen_offset),
            mouse_cell: V2(-1, -1),
            drag_rect: None,
            effects: Vec::new(),
        }
    }

    pub fn render(&self, ctx: &mut Canvas) {
        let screen_rect = Rect(V2(-32.0f32, -32.0f32), V2(640.0f32 + 64.0, 360.0f32 + 64.0));

        let mut sprites = Vec::new();

        for pt in self.proj.inv_project_rectangle(&screen_rect).iter() {
            let pos = self.proj.project(pt);
            Kernel::new(|p| self.world.terrain_at(p), pt.map(|x| x as i32)).render(
                |layer, spr, fore, back| {
                    sprites.push(Sprite::new_spr(spr, fore, back, pos, layer));
                });
        }

        for &e in self.world.ecs.iter() {
            if let Some(spr) = cmd::sprite(&self.world, e, &self.proj) {
                // Highlight reticle on focused unit.
                if self.selected.contains(&e) {
                    for s in unit_focus_sprites(spr.pos).into_iter() {
                        sprites.push(s);
                    }
                }
                sprites.push(spr);
            }
        }

        for f in self.effects.iter() {
            sprites.push(f.sprite(&self.proj));
        }

        sprites.sort_by(|a, b| a.cmp(&b));
        for spr in sprites.iter() {
            spr.drawable.draw(ctx, spr.pos, 0.5);
        }

        if let Some(rect) = self.drag_rect {
            ctx.draw_rect(&rect, 0.2, color::CYAN);
        }
    }

    pub fn update(&mut self) {
        if self.mode == GameMode::RealTime {
            self.world.update_active();
        } else {
            self.world.update_standby();
        }

        match self.mode {
            GameMode::Rogue(rogue) => {
                let proj = Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap();
                let spr = cmd::sprite(&self.world, rogue, &proj).unwrap();
                self.screen_offset = -spr.pos + V2(320.0, 180.0);
            }
            _ => { self.screen_offset = self.screen_offset - self.scroll_delta; }
        }

        self.proj = Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap()
            .view_offset(self.screen_offset);

        for e in self.effects.iter_mut() {
            e.update();
        }

        self.effects.retain(|e| e.is_alive());
    }

    fn go_rogue(&mut self) -> Option<Entity> {
        if let GameMode::Rogue(rogue) = self.mode { return Some(rogue); }

        if let Some(&rogue) = self.selected.iter().next() {
            self.selected.clear();
            self.mode = GameMode::Rogue(rogue);
            Some(rogue)
        } else {
            None
        }
    }

    fn toggle_pause(&mut self) {
        match self.mode {
            GameMode::Rogue(_) | GameMode::Paused => self.mode = GameMode::RealTime,
            GameMode::RealTime => self.mode = GameMode::Paused,
        }
    }

    fn rogue_step(&mut self, dir: Dir6) {
        if let Some(rogue) = self.go_rogue() {
            while !cmd::ready_to_act(&self.world, rogue) { self.world.update_active(); }
            cmd::step(&mut self.world, rogue, dir);
        }
    }

    fn add_effect(&mut self, effect: Box<Effect>) {
        self.effects.push(effect);
    }

    pub fn process_event(&mut self, evt: Event) {
        let scroll_speed = 8f32;
        match evt {
            Event::KeyPress(k) => {
                match k {
                    Key::J => { self.scroll_delta.0 = -1.0 * scroll_speed; }
                    Key::L => { self.scroll_delta.0 =  1.0 * scroll_speed; }
                    Key::I => { self.scroll_delta.1 = -1.0 * scroll_speed; }
                    Key::K => { self.scroll_delta.1 =  1.0 * scroll_speed; }
                    Key::Space => { self.toggle_pause(); }

                    Key::W => { self.rogue_step(Dir6::North); }
                    Key::E => { self.rogue_step(Dir6::NorthEast); }
                    Key::D => { self.rogue_step(Dir6::SouthEast); }
                    Key::S => { self.rogue_step(Dir6::South); }
                    Key::A => { self.rogue_step(Dir6::SouthWest); }
                    Key::Q => { self.rogue_step(Dir6::NorthWest); }
                    _ => {}
                }
            }

            Event::KeyRelease(k) => {
                match k {
                    Key::J => { self.scroll_delta.0 = 0.0; }
                    Key::L => { self.scroll_delta.0 = 0.0; }
                    Key::I => { self.scroll_delta.1 = 0.0; }
                    Key::K => { self.scroll_delta.1 = 0.0; }
                    _ => {}
                }
            }

            Event::MouseDrag(MouseButton::Left, p1, p2) => {
                self.drag_rect = Some(Rect::from_points(p1, p2))
            }

            Event::MouseMove(pos) => {
                self.mouse_cell = self.proj.inv_project(pos).map(|x| x.floor() as i32);
            }

            Event::MouseClick(MouseButton::Left) => {
                self.selected.clear();
                if let Some(p) = cmd::mob_at(&self.world, self.mouse_cell) {
                    self.selected.insert(p);
                }
            }

            Event::MouseClick(MouseButton::Right) => {
                let mut path_found = None;
                for &p in self.selected.iter() {
                    if cmd::is_player(&self.world, p) {
                        let cmd_pathed = cmd::move_to(&mut self.world, p, self.mouse_cell);
                        path_found = path_found.map_or(
                            Some(cmd_pathed), |x| Some(x || cmd_pathed));
                    }
                }

                let pos = self.mouse_cell;
                match path_found {
                    Some(true) =>
                        self.add_effect(Box::new(Effect::new(Fx::PathOk, pos))),
                    Some(false) =>
                        self.add_effect(Box::new(Effect::new(Fx::PathBlocked, pos))),
                    _ => {}
                }
            }

            Event::MouseDragEnd(MouseButton::Left, p1, p2) => {
                self.selected.clear();
                self.drag_rect = None;

                let cell_rect = Rect(V2(-8f32, -8f32), V2(16f32, 8f32));
                let select_rect = Rect::from_points(p1, p2);

                for pt in self.proj.inv_project_rectangle(&select_rect).iter() {
                    if !select_rect.intersects(&(cell_rect + self.proj.project(pt))) { continue; }

                    let pos = pt.map(|x| x.floor() as i32);

                    match cmd::mob_at(&self.world, pos) {
                        Some(e) if cmd::is_player(&self.world, e) => {
                            self.selected.insert(e);
                        }
                        _ => {}
                    }
                }
            }

            _ => {}
        }
    }
}

fn main() {
    let mut world = World::new();
    let tmx = include_str!("../assets/hexworld.tmx");
    world.load(&tiled::parse(tmx.as_bytes()).unwrap());
    let mut game = GameState::new(world);

    let mut builder = CanvasBuilder::new()
        .set_size(640, 360)
        .set_frame_interval(0.033f64)
        ;
    Spr::init(&mut builder);
    let mut ctx = builder.build();

    loop {
        match ctx.next_event() {
            Event::RenderFrame => {
                game.update();
                game.render(&mut ctx);
            }

            Event::Quit => { return; }

            Event::KeyPress(Key::F12) => {
                ctx.save_screenshot(&"azag");
            }

            Event::KeyPress(Key::Escape) => {
                return;
            }

            e => game.process_event(e),
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::File;

mod lexer;
mod parser;
mod token;

fn prelude () -> ~str {
    ~"extern mod extra;
extern mod http;

use super::super::{View, SafeHtmlString};"
}

fn get_file_contents() -> Result<~[u8], io::IoError> {
    let path = from_str::<Path>("src/compiler/index.rs.html").unwrap();

    let mut file = if_ok!(File::open(&path));
    file.read_to_end()
}

fn main() {
    let contents = match get_file_contents() {
        Ok(contents) => std::str::from_utf8_owned(contents).expect("Non-utf8 source file"),
        Err(e) => fail!(e)
    };

    let mut lexer = lexer::Lexer::new(contents);
    let mut parser = parser::Parser::new(&mut lexer);
    parser.parse();

    println!("{}", prelude());

    println!("Lines: {}\nLast column: {}", parser.lexer.line, parser.lexer.column);

    for section in parser.sections.iter() {
        match section {
            &parser::Html(ref s) => {
                println!("Html({})", *s);
            },
            &parser::Rust(ref s) => {
                println!("Rust({})", *s);
            },
            _ => {}
        }
    }
}
<commit_msg>Update for rust changes<commit_after>use std::io;
use std::io::File;

mod lexer;
mod parser;
mod token;

fn prelude () -> ~str {
    ~"extern mod extra;
extern mod http;

use super::super::{View, SafeHtmlString};"
}

fn get_file_contents() -> Result<~[u8], io::IoError> {
    let path = from_str::<Path>("src/compiler/index.rs.html").unwrap();

    File::open(&path).and_then(|file| {
        let mut file = file;
        file.read_to_end()
    })
}

fn main() {
    let contents = match get_file_contents() {
        Ok(contents) => std::str::from_utf8_owned(contents).expect("Non-utf8 source file"),
        Err(e) => fail!(e)
    };

    let mut lexer = lexer::Lexer::new(contents);
    let mut parser = parser::Parser::new(&mut lexer);
    parser.parse();

    println!("{}", prelude());

    println!("Lines: {}\nLast column: {}", parser.lexer.line, parser.lexer.column);

    for section in parser.sections.iter() {
        match section {
            &parser::Html(ref s) => {
                println!("Html({})", *s);
            },
            &parser::Rust(ref s) => {
                println!("Rust({})", *s);
            },
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GdkPixbufLoader — Application-driven progressive image loading.

use std;
use ffi;
use glib::{to_bool, GlibContainer, Error};
use glib::translate::ToGlibPtr;
use libc::{c_int, c_uint};

#[repr(C)]
pub struct PixbufLoader {
    pointer: *mut ffi::C_GdkPixbufLoader
}

impl PixbufLoader {
    /// Creates a new pixbuf loader object.
    ///
    /// # Failures
    /// Returns `None` if the pixbuf loader cannot be created.
    pub fn new() -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new() };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    /// Creates a new pixbuf loader object that always attempts to parse image
    /// data as if it were an image of type `image_type`, instead of
    /// identifying the type automatically. Useful if you want an error if the
    /// image isn't the expected type, for loading image formats that can't be
    /// reliably identified by looking at the data, or if the user manually
    /// forces a specific type.
    ///
    /// The list of supported image formats depends on what image loaders are
    /// installed, but typically "png", "jpeg", "gif", "tiff" and "xpm" are
    /// among the supported formats.
    ///
    /// # Failures
    /// Returns `None` if the pixbuf loader cannot be created.
    pub fn new_with_type(image_type: &str, error: &mut Error) -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_type(image_type.borrow_to_glib().0, &mut error.unwrap()) };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    /// Creates a new pixbuf loader object that always attempts to parse image
    /// data as if it were an image of mime type `mime_type`, instead of
    /// identifying the type automatically. Useful if you want an error if the
    /// image isn't the expected mime type, for loading image formats that can't
    /// be reliably identified by looking at the data, or if the user manually
    /// forces a specific mime type.
    ///
    /// The list of supported mime types depends on what image loaders are
    /// installed, but typically "image/png", "image/jpeg", "image/gif",
    /// "image/tiff" and "image/x-xpixmap" are among the supported mime types.
    ///
    /// # Failures
    /// Returns `None` if the pixbuf loader cannot be created.
    pub fn new_with_mime_type(mime_type: &str, error: &mut Error) -> Option<PixbufLoader> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_mime_type(mime_type.borrow_to_glib().0, &mut error.unwrap()) };

        if tmp.is_null() {
            None
        } else {
            Some(PixbufLoader::wrap_pointer(tmp))
        }
    }

    /// Obtains the available information about the format of the currently
    /// loading image file.
    ///
    /// # Failures
    /// Returns `None` if the pixbuf format cannot be retrieved, or if not
    /// enough data is available to create the format.
    pub fn get_format(&self) -> Option<::PixbufFormat> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_format(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::PixbufFormat::wrap_pointer(tmp))
        }
    }

    /// This will cause a pixbuf loader to parse the  bytes of an image stored
    /// in `buf`. It will return `Ok` if the data was loaded successfully, and
    /// `Err` if an error occurred. In the latter case, the loader will be
    /// closed, and will not accept further writes.
    ///
    /// # Failures
    /// Returns an `Error` if `buf` cannot be written to the loader.  Query the
    /// `Error` for more detailed information.
    pub fn loader_write(&self, buf: &[u8]) -> Result<(), Error> {
        unsafe {
            let mut error = std::ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_write(self.unwrap_pointer(), buf.as_ptr(), buf.len() as c_uint, &mut error)) {
                true => Ok(()),
                false => Err(Error::wrap(error))
            }
        }
    }

    /*pub fn loader_write_bytes(&self, buffer: &glib::Bytes, error: &mut Error) -> bool {
      gdk_pixbuf_loader_write_bytes
      }*/

    /// Causes the image to be scaled while it is loaded. The desired image
    /// size can be determined relative to the original size of the image by
    /// calling `set_size()` from a signal handler for the `size-prepared`
    /// signal.
    ///
    /// Attempts to set the desired image size are ignored after the emission
    /// of the `size-prepared` signal.
    pub fn set_size(&self, width: isize, height: isize) {
        unsafe { ffi::gdk_pixbuf_loader_set_size(self.unwrap_pointer(), width as c_int, height as c_int) }
    }

    /// Queries the `Pixbuf` that a pixbuf loader is currently creating. In
    /// general it only makes sense to call this function after the
    /// `area-prepared` signal has been emitted by the loader; this means that
    /// enough data has been read to know the size of the image that will be
    /// allocated. If the loader has not received enough data via
    /// `loader_write()`, then this function returns `None`. If the loader is
    /// an animation, it will return the "static image" of the animation.
    ///
    /// # Failures
    /// Returns `None` if the pixbuf cannot be retrieved, or if not enough data
    /// is available to create the pixbuf.
    pub fn get_pixbuf(&self) -> Option<::Pixbuf> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_pixbuf(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::Pixbuf::wrap_pointer(tmp))
        }
    }

    /*pub fn get_animation(&self) -> Option<::PixbufAnimation> {
      let tmp = unsafe { ffi::gdk_pixbuf_loader_get_animation(self.unwrap_pointer()) };

      if tmp.is_null() {
      None
      } else {
      Some(::PixbufAnimation::wrap_pointer(tmp))
      }
      }*/

    /// Informs a pixbuf loader that no further writes with `loader_write()`
    /// will occur, so that it can free its internal loading structures. Also,
    /// tries to parse any data that hasn't yet been parsed.
    ///
    /// # Failures
    /// If the remaining data is partial or corrupt, an error will be returned.
    /// Query the `Error` for more detailed information.
    pub fn close(&self) -> Result<(), Error> {
        unsafe {
            let mut error = std::ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_close(self.unwrap_pointer(), &mut error)) {
                true => Ok(()),
                false => Err(Error::wrap(error))
            }
        }
    }
}

impl_GObjectFunctions!(PixbufLoader, C_GdkPixbufLoader);
<commit_msg>Adjusting return types<commit_after>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GdkPixbufLoader — Application-driven progressive image loading.

use std;
use ffi;
use glib::{to_bool, GlibContainer, Error};
use glib::translate::ToGlibPtr;
use libc::{c_int, c_uint};

#[repr(C)]
pub struct PixbufLoader {
    pointer: *mut ffi::C_GdkPixbufLoader
}

impl PixbufLoader {
    /// Creates a new pixbuf loader object.
    ///
    /// # Panics
    /// Fails if the pixbuf loader cannot be retrieved.
    pub fn new() -> PixbufLoader {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new() };
        assert!(!tmp.is_null());
        PixbufLoader::wrap_pointer(tmp)
    }

    /// Creates a new pixbuf loader object that always attempts to parse image
    /// data as if it were an image of type `image_type`, instead of
    /// identifying the type automatically. Useful if you want an error if the
    /// image isn't the expected type, for loading image formats that can't be
    /// reliably identified by looking at the data, or if the user manually
    /// forces a specific type.
    ///
    /// The list of supported image formats depends on what image loaders are
    /// installed, but typically "png", "jpeg", "gif", "tiff" and "xpm" are
    /// among the supported formats.
    ///
    /// # Failures
    /// Returns an `Error` if the pixbuf loader cannot be created. Query the
    /// error for more detailed information.
    ///
    /// # Panics
    /// Fails if the pixbuf loader cannot be retrieved.
    pub fn new_with_type(image_type: &str) -> Result<PixbufLoader, Error> {
        let mut error = std::ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_type(image_type.borrow_to_glib().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            Ok(PixbufLoader::wrap_pointer(tmp))
        } else {
            Err(Error::wrap(error))
        }
    }

    /// Creates a new pixbuf loader object that always attempts to parse image
    /// data as if it were an image of mime type `mime_type`, instead of
    /// identifying the type automatically. Useful if you want an error if the
    /// image isn't the expected mime type, for loading image formats that can't
    /// be reliably identified by looking at the data, or if the user manually
    /// forces a specific mime type.
    ///
    /// The list of supported mime types depends on what image loaders are
    /// installed, but typically "image/png", "image/jpeg", "image/gif",
    /// "image/tiff" and "image/x-xpixmap" are among the supported mime types.
    ///
    /// # Failures
    /// Returns an `Error` if the pixbuf loader cannot be created. Query the
    /// error for more detailed information.
    ///
    /// # Panics
    /// Fails if the pixbuf loader cannot be retrieved.
    pub fn new_with_mime_type(mime_type: &str) -> Result<PixbufLoader, Error> {
        let mut error = std::ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_loader_new_with_mime_type(mime_type.borrow_to_glib().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            Ok(PixbufLoader::wrap_pointer(tmp))
        } else {
            Err(Error::wrap(error))
        }
    }

    /// Obtains the available information about the format of the currently
    /// loading image file.  Returns `None` if not enough data has been written
    /// to determine the format.
    pub fn get_format(&self) -> Option<::PixbufFormat> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_format(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::PixbufFormat::wrap_pointer(tmp))
        }
    }

    /// This will cause a pixbuf loader to parse the  bytes of an image stored
    /// in `buf`. It will return `Ok` if the data was loaded successfully, and
    /// `Err` if an error occurred. In the latter case, the loader will be
    /// closed, and will not accept further writes.
    ///
    /// # Failures
    /// Returns an `Error` if `buf` cannot be written to the loader.  Query the
    /// `Error` for more detailed information.
    pub fn loader_write(&self, buf: &[u8]) -> Result<(), Error> {
        unsafe {
            let mut error = std::ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_write(self.unwrap_pointer(), buf.as_ptr(), buf.len() as c_uint, &mut error)) {
                true => Ok(()),
                false => Err(Error::wrap(error))
            }
        }
    }

    /*pub fn loader_write_bytes(&self, buffer: &glib::Bytes, error: &mut Error) -> bool {
      gdk_pixbuf_loader_write_bytes
      }*/

    /// Causes the image to be scaled while it is loaded. The desired image
    /// size can be determined relative to the original size of the image by
    /// calling `set_size()` from a signal handler for the `size-prepared`
    /// signal.
    ///
    /// Attempts to set the desired image size are ignored after the emission
    /// of the `size-prepared` signal.
    pub fn set_size(&self, width: isize, height: isize) {
        unsafe { ffi::gdk_pixbuf_loader_set_size(self.unwrap_pointer(), width as c_int, height as c_int) }
    }

    /// Queries the `Pixbuf` that a pixbuf loader is currently creating. In
    /// general it only makes sense to call this function after the
    /// `area-prepared` signal has been emitted by the loader; this means that
    /// enough data has been read to know the size of the image that will be
    /// allocated. If the loader has not received enough data via
    /// `loader_write()`, then this function returns `None`. If the loader is
    /// an animation, it will return the "static image" of the animation.
    pub fn get_pixbuf(&self) -> Option<::Pixbuf> {
        let tmp = unsafe { ffi::gdk_pixbuf_loader_get_pixbuf(self.unwrap_pointer()) };

        if tmp.is_null() {
            None
        } else {
            Some(::Pixbuf::wrap_pointer(tmp))
        }
    }

    /*pub fn get_animation(&self) -> Option<::PixbufAnimation> {
      let tmp = unsafe { ffi::gdk_pixbuf_loader_get_animation(self.unwrap_pointer()) };

      if tmp.is_null() {
      None
      } else {
      Some(::PixbufAnimation::wrap_pointer(tmp))
      }
      }*/

    /// Informs a pixbuf loader that no further writes with `loader_write()`
    /// will occur, so that it can free its internal loading structures. Also,
    /// tries to parse any data that hasn't yet been parsed.
    ///
    /// # Failures
    /// If the remaining data is partial or corrupt, an error will be returned.
    /// Query the `Error` for more detailed information.
    pub fn close(&self) -> Result<(), Error> {
        unsafe {
            let mut error = std::ptr::null_mut();
            match to_bool(ffi::gdk_pixbuf_loader_close(self.unwrap_pointer(), &mut error)) {
                true => Ok(()),
                false => Err(Error::wrap(error))
            }
        }
    }
}

impl_GObjectFunctions!(PixbufLoader, C_GdkPixbufLoader);
<|endoftext|>