<commit_before>#![feature(non_ascii_idents, plugin, test)]

#![allow(unknown_lints)]

#![allow(if_not_else, unused_features, clone_on_ref_ptr, unreadable_literal)]
#![warn(missing_debug_implementations, missing_copy_implementations,
        trivial_casts, trivial_numeric_casts,
        unused_import_braces, unused_qualifications)]

extern crate argparse;
extern crate ansi_term;
#[cfg_attr(test, macro_use)] extern crate itertools;
#[macro_use] extern crate log;
extern crate lru_cache;
extern crate parking_lot;
extern crate rustc_serialize;
extern crate time;
extern crate twox_hash;

#[macro_use] mod sorceries;
mod space;
mod identity;
mod motion;
mod landmark;
mod life;
mod mind;
mod substrate;
mod uci; // Unlikely Command Integration
mod test_landmark;

use std::fs::OpenOptions;
use std::io;
use std::io::Write;
use std::process;

use ansi_term::Colour as Color;
use argparse::{ArgumentParser, Print, Store, StoreOption, StoreTrue};
use log::{LogLevelFilter, LogMetadata, LogRecord, SetLoggerError};
use rustc_serialize::json;
use time::{Duration, get_time};

use identity::{Agent, Team};
use life::{Commit, Patch, WorldState};
use mind::{Variation, fixed_depth_sequence_kickoff, iterative_deepening_kickoff,
           kickoff, pagan_variation_format};
use substrate::memory_free;

struct DebugLogger;

impl DebugLogger {
    pub fn init() -> Result<(), SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(LogLevelFilter::Debug);
            Box::new(DebugLogger)
        })
    }
}

impl log::Log for DebugLogger {
    fn enabled(&self, _metadata: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        // XXX: can't the open file handle live inside the DebugLogger struct?!
        let mut log_file = OpenOptions::new()
                               .write(true)
                               .append(true)
                               .create(true)
                               .open("leafline.log")
                               .expect("couldn't open log file?!");
        let log_message = format!("[{}] {}\n",
                                  time::now()
                                      .strftime("%Y-%m-%d %H:%M:%S.%f")
                                      .unwrap(),
                                  record.args());
        log_file.write_all(&log_message.into_bytes())
                .expect("couldn't write to log file?!");
    }
}

#[derive(Debug, Clone)]
enum LookaheadBound {
    Depth(u8, Option<u8>),
    DepthSequence(Vec<u8>),
    Seconds(u8),
}

impl LookaheadBound {
    pub fn duration(&self) -> Duration {
        match *self {
            LookaheadBound::Seconds(secs) => Duration::seconds(i64::from(secs)),
            _ => {
                moral_panic!("`duration()` called on non-Seconds LookaheadBound \
                              variant")
            }
        }
    }

    pub fn new_from_sequence_depiction(depiction: &str) -> Self {
        let depth_runes = depiction.split(',');
        let depth_sequence = depth_runes.map(|dd| {
                                            dd.parse::<u8>()
                                              .expect("couldn't parse depth \
                                                       sequence")
                                        })
                                        .collect::<Vec<_>>();
        LookaheadBound::DepthSequence(depth_sequence)
    }

    pub fn from_args(lookahead_depth: Option<u8>,
                     lookahead_extension: Option<u8>,
        lookahead_depth_sequence: Option<String>,
        lookahead_seconds: Option<u8>)
                     -> Result<Option<Self>, String> {
        let mut bound = None;
        let confirm_bound_is_none =
            |b: &Option<LookaheadBound>| -> Result<bool, String> {
                if b.is_some() {
                    Err("more than one of `--depth`, `--depth-sequence`, or \
                         `--seconds` was passed"
                            .to_owned())
                } else {
                    Ok(true)
                }
            };
        if let Some(depth) = lookahead_depth {
            confirm_bound_is_none(&bound)?;
            bound = Some(LookaheadBound::Depth(depth, lookahead_extension));
        }
        if let Some(sequence_depiction) = lookahead_depth_sequence {
            confirm_bound_is_none(&bound)?;
            bound = Some(LookaheadBound::new_from_sequence_depiction(
                &sequence_depiction));
        }
        if let Some(seconds) = lookahead_seconds {
            confirm_bound_is_none(&bound)?;
            bound = Some(LookaheadBound::Seconds(seconds));
        }
        Ok(bound)
    }
}

fn forecast(world: WorldState, bound: LookaheadBound, déjà_vu_bound: f32)
            -> (Vec<(Commit, f32, Variation)>, u8, Duration) {
    let start_thinking = get_time();
    let forecasts;
    let depth;
    match bound {
        LookaheadBound::Depth(ds, es) => {
            forecasts = kickoff(&world, ds, es, false, déjà_vu_bound);
            depth = ds;
        },
        LookaheadBound::DepthSequence(ds) => {
            depth = *ds.last().unwrap();
            forecasts = fixed_depth_sequence_kickoff(
                &world, ds, false, déjà_vu_bound);
            // XXX TODO: if we're just returning a number, it should be the
            // lowest depth, but we should really report all of them
        },
        LookaheadBound::Seconds(_) => {
            let (fs, ds) = iterative_deepening_kickoff(
                &world, bound.duration(), false, déjà_vu_bound);
            forecasts = fs;
            depth = ds;
        }
    }
    let stop_thinking = get_time();
    let thinking_time = stop_thinking - start_thinking;
    (forecasts, depth, thinking_time)
}

#[derive(RustcEncodable, RustcDecodable)]
struct Postcard {
    world: String,
    patch: Patch,
    hospitalization: Option<Agent>,
    thinking_time: u64,
    depth: u8,
    counterreplies: Vec<Patch>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct LastMissive {
    the_triumphant: Option<Team>,
}

#[allow(collapsible_if)]
fn correspondence(reminder: &str, bound: LookaheadBound, déjà_vu_bound: f32)
                  -> String {
    let in_medias_res = WorldState::reconstruct(reminder);
    let (mut forecasts, depth, sidereal) = forecast(in_medias_res,
                                                    bound,
                                                    déjà_vu_bound);

    if !forecasts.is_empty() {
        let (determination, _karma, _variation) = forecasts.swap_remove(0);
        // XXX TODO FIXME: this doesn't distinguish amongst ascensions
        // (and we can imagine somewhat contrived situations where only
        // some of them are admissible movements)
        let counterreplies = determination.tree
                                          .lookahead()
                                          .iter()
                                          .map(|c| c.patch)
                                          .collect::<Vec<_>>();
        if counterreplies.is_empty() {
            if determination.tree.in_critical_endangerment(Team::Orange) {
                return json::encode(&LastMissive {
                           the_triumphant: Some(Team::Blue),
                       })
                           .unwrap();
            } else {
                return json::encode(&LastMissive { the_triumphant: None }).unwrap();
            }
        }
        let postcard = Postcard {
            world: determination.tree.preserve(),
            patch: determination.patch,
            hospitalization: determination.hospitalization,
            thinking_time: sidereal.num_milliseconds() as u64,
            depth,
            counterreplies,
        };
        json::encode(&postcard).unwrap()
    } else {
        if in_medias_res.in_critical_endangerment(Team::Blue) {
            json::encode(&LastMissive { the_triumphant: Some(Team::Orange) })
                .unwrap()
        } else {
            json::encode(&LastMissive { the_triumphant: None }).unwrap()
        }
    }
}

fn the_end() {
    println!("THE END");
    process::exit(0);
}

fn main() {
    // Does argparse not offer an analogue of Python's argparse's
    // `add_mutually_exclusive_group`
    // (https://docs.python.org/3/library/argparse.html#mutual-exclusion)?
    // Contribution opportunity if so??
    let mut lookahead_depth: Option<u8> = None;
    let mut lookahead_extension: Option<u8> = None;
    // TODO CONSIDER: would argparse's Collect action be cleaner?
    let mut lookahead_depth_sequence: Option<String> = None;
    let mut lookahead_seconds: Option<u8> = None;
    let mut from_runes: Option<String> = None;
    let mut correspond: bool = false;
    let mut uci_dæmon: bool = false;
    let mut déjà_vu_bound: f32 = 2.0;
    let mut debug_logging: bool = false;
    {
        let mut parser = ArgumentParser::new();
        parser.set_description("Leafline: an oppositional strategy game engine");
        parser.refer(&mut lookahead_depth).add_option(
            &["--depth"],
            StoreOption,
            "rank moves using AI minimax lookahead this deep");
        parser.refer(&mut lookahead_extension).add_option(
            &["--quiet"],
            StoreOption,
            "search with quietness extension this deep");
        parser.refer(&mut lookahead_depth_sequence).add_option(
            &["--depth-sequence"],
            StoreOption,
            "rank moves using AI minimax lookahead to these depths");
        parser.refer(&mut lookahead_seconds).add_option(
            &["--seconds"],
            StoreOption,
            "rank moves using AI minimax for about this many seconds");
        parser.refer(&mut correspond).add_option(
            &["--correspond"],
            StoreTrue,
            "just output the serialization of the AI's top response and \
             legal replies thereto");
        parser.refer(&mut uci_dæmon).add_option(
            &["--uci", "--deamon", "--dæmon"],
            StoreTrue,
            "run Unlikely Command Integration dæmon for external driver play");
        parser.refer(&mut from_runes).add_option(
            &["--from"],
            StoreOption,
            "start a game from the given book of preservation runes");
        parser.refer(&mut déjà_vu_bound).add_option(
            &["--déjà-vu-bound", "--deja-vu-bound"],
            Store,
            "try to not store more entries in the déjà vu table than fit in \
             this many GiB of memory"
        );
        parser.refer(&mut debug_logging).add_option(
            &["--debug"],
            StoreTrue,
            "run with debug logging to file"
        );
        parser.add_option(&["--version", "-v"],
            Print(env!("CARGO_PKG_VERSION").to_owned()), "diplay the version");
        parser.parse_args_or_exit();
    }

    if debug_logging {
        DebugLogger::init().expect("couldn't initialize logging?!")
    }

    if correspond {
        let bound_maybe_result = LookaheadBound::from_args(lookahead_depth,
                                                           lookahead_extension,
                                                           lookahead_depth_sequence,
                                                           lookahead_seconds);
        let bound = match bound_maybe_result {
            Ok(bound_maybe) => {
                match bound_maybe {
                    Some(bound) => bound,
                    None => {
                        moral_panic!("`--correspond` passed without exactly one \
                                      of `--depth`, `--depth-sequence`, or \
                                      `--seconds`")
                    }
                }
            }
            Err(error) => {
                moral_panic!(error)
            }
        };
        let from = from_runes.expect("`--correspond` requires `--from`");
        println!("{}", correspondence(&from, bound, déjà_vu_bound));
        process::exit(0);
    }

    if uci_dæmon {
        uci::dæmon();
        // ↑ dæmon will loop
        process::exit(0);
    }

    println!("Welcome to Leafline v. {}!", env!("CARGO_PKG_VERSION"));
    match memory_free() {
        Some(bytes) => {
            println!("Leafline substrate accountant detected {:.3} \
                     GiB of free memory.",
                     bytes.in_gib());
        },
        None => {
            println!("Could not detect amount of free memory! \
                      It is possible \
                      that you are struggling with an inferior nonfree \
                      operating system forced on you by your masters in \
                      Cupertino or Redmond");
        }
    }

    let mut world = match from_runes {
        Some(runes) => WorldState::reconstruct(&runes),
        None => WorldState::new(),
    };
    let mut premonitions: Vec<Commit>;
    let bound_maybe = LookaheadBound::from_args(lookahead_depth,
                                                lookahead_extension,
                                                lookahead_depth_sequence,
                                                lookahead_seconds)
                          .unwrap();
    loop {
        match bound_maybe {
            None => {
                premonitions = world.lookahead();
                if premonitions.is_empty() {
                    // XXX TODO distinguish between deadlock and
                    // ultimate endangerment
                    the_end();
                }
                println!("{}", world);
                for (index, premonition) in premonitions.iter().enumerate() {
                    println!("{:>2}. {}", index, premonition)
                }
            }
            Some(ref bound) => {
                let (our_forecasts, depth, thinking_time) =
                    forecast(world, bound.clone(), déjà_vu_bound);
                let forecasts = our_forecasts;
                println!("{}", world);
                let depth_report = match *bound {
                    LookaheadBound::Depth(standard, extension) => {
                        match extension {
                            Some(quietening) => format!(
                                "at least {} and up to {}",
                                standard,
                                standard + quietening),
                            None => format!("{}", depth)
                        }
                    }
                    _ => format!("{}", depth),
                };
                println!("(scoring alternatives {} levels deep took {} ms)",
                         depth_report,
                         thinking_time.num_milliseconds());
                premonitions = Vec::new();
                for (index, sight) in forecasts.into_iter().enumerate() {
                    let (commit, score, variation) = sight;
                    println!("{:>2}: {} — score {} ‣ representative variation: {}",
                             index,
                             commit,
                             Color::Purple.bold()
                                          .paint(&format!("{:.1}", score)),
                             pagan_variation_format(&variation));
                    premonitions.push(commit);
                }

                if premonitions.is_empty() {
                    the_end();
                }
            }
        }

        loop {
            print!("\nSelect a move>> ");
            io::stdout().flush().expect("couldn't flush stdout");
            let mut input_buffer = String::new();
            io::stdin()
                .read_line(&mut input_buffer)
                .expect("couldn't read input");

            if input_buffer.trim() == "quit" {
                the_end();
            }

            let choice: usize = match input_buffer.trim().parse() {
                Ok(i) => i,
                Err(e) => {
                    println!("Error parsing choice: {:?}. Try again.", e);
                    continue;
                }
            };
            if choice < premonitions.len() {
                world = premonitions[choice].tree;
                break;
            } else {
                println!("{} isn't among the choices. Try again.", choice);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{LookaheadBound, correspondence};

    #[test]
    fn concerning_correspondence_victory_conditions() {
        let blue_concession = correspondence("R6k/6pp/8/8/8/8/8/8 b - -",
                                             LookaheadBound::Depth(2, None),
                                             1.0);
        assert_eq!("{\"the_triumphant\":\"Orange\"}".to_owned(),
                   blue_concession);
    }

}
<commit_msg>Adding a rosetta stone for pagan consumers<commit_after>#![feature(non_ascii_idents, plugin, test)]

#![allow(unknown_lints)]

#![allow(if_not_else, unused_features, clone_on_ref_ptr, unreadable_literal)]
#![warn(missing_debug_implementations, missing_copy_implementations,
        trivial_casts, trivial_numeric_casts,
        unused_import_braces, unused_qualifications)]

extern crate argparse;
extern crate ansi_term;
#[cfg_attr(test, macro_use)] extern crate itertools;
#[macro_use] extern crate log;
extern crate lru_cache;
extern crate parking_lot;
extern crate rustc_serialize;
extern crate time;
extern crate twox_hash;

#[macro_use] mod sorceries;
mod space;
mod identity;
mod motion;
mod landmark;
mod life;
mod mind;
mod substrate;
mod uci; // Unlikely Command Integration
mod test_landmark;

use std::fs::OpenOptions;
use std::io;
use std::io::Write;
use std::process;

use ansi_term::Colour as Color;
use argparse::{ArgumentParser, Print, Store, StoreOption, StoreTrue};
use log::{LogLevelFilter, LogMetadata, LogRecord, SetLoggerError};
use rustc_serialize::json;
use time::{Duration, get_time};

use identity::{Agent, Team};
use life::{Commit, Patch, WorldState};
use mind::{Variation, fixed_depth_sequence_kickoff, iterative_deepening_kickoff,
           kickoff, pagan_variation_format};
use substrate::memory_free;

struct DebugLogger;

impl DebugLogger {
    pub fn init() -> Result<(), SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(LogLevelFilter::Debug);
            Box::new(DebugLogger)
        })
    }
}

impl log::Log for DebugLogger {
    fn enabled(&self, _metadata: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        // XXX: can't the open file handle live inside the DebugLogger struct?!
        let mut log_file = OpenOptions::new()
                               .write(true)
                               .append(true)
                               .create(true)
                               .open("leafline.log")
                               .expect("couldn't open log file?!");
        let log_message = format!("[{}] {}\n",
                                  time::now()
                                      .strftime("%Y-%m-%d %H:%M:%S.%f")
                                      .unwrap(),
                                  record.args());
        log_file.write_all(&log_message.into_bytes())
                .expect("couldn't write to log file?!");
    }
}

#[derive(Debug, Clone)]
enum LookaheadBound {
    Depth(u8, Option<u8>),
    DepthSequence(Vec<u8>),
    Seconds(u8),
}

impl LookaheadBound {
    pub fn duration(&self) -> Duration {
        match *self {
            LookaheadBound::Seconds(secs) => Duration::seconds(i64::from(secs)),
            _ => {
                moral_panic!("`duration()` called on non-Seconds LookaheadBound \
                              variant")
            }
        }
    }

    pub fn new_from_sequence_depiction(depiction: &str) -> Self {
        let depth_runes = depiction.split(',');
        let depth_sequence = depth_runes.map(|dd| {
                                            dd.parse::<u8>()
                                              .expect("couldn't parse depth \
                                                       sequence")
                                        })
                                        .collect::<Vec<_>>();
        LookaheadBound::DepthSequence(depth_sequence)
    }

    pub fn from_args(lookahead_depth: Option<u8>,
                     lookahead_extension: Option<u8>,
        lookahead_depth_sequence: Option<String>,
        lookahead_seconds: Option<u8>)
                     -> Result<Option<Self>, String> {
        let mut bound = None;
        let confirm_bound_is_none =
            |b: &Option<LookaheadBound>| -> Result<bool, String> {
                if b.is_some() {
                    Err("more than one of `--depth`, `--depth-sequence`, or \
                         `--seconds` was passed"
                            .to_owned())
                } else {
                    Ok(true)
                }
            };
        if let Some(depth) = lookahead_depth {
            confirm_bound_is_none(&bound)?;
            bound = Some(LookaheadBound::Depth(depth, lookahead_extension));
        }
        if let Some(sequence_depiction) = lookahead_depth_sequence {
            confirm_bound_is_none(&bound)?;
            bound = Some(LookaheadBound::new_from_sequence_depiction(
                &sequence_depiction));
        }
        if let Some(seconds) = lookahead_seconds {
            confirm_bound_is_none(&bound)?;
            bound = Some(LookaheadBound::Seconds(seconds));
        }
        Ok(bound)
    }
}

fn forecast(world: WorldState, bound: LookaheadBound, déjà_vu_bound: f32)
            -> (Vec<(Commit, f32, Variation)>, u8, Duration) {
    let start_thinking = get_time();
    let forecasts;
    let depth;
    match bound {
        LookaheadBound::Depth(ds, es) => {
            forecasts = kickoff(&world, ds, es, false, déjà_vu_bound);
            depth = ds;
        },
        LookaheadBound::DepthSequence(ds) => {
            depth = *ds.last().unwrap();
            forecasts = fixed_depth_sequence_kickoff(
                &world, ds, false, déjà_vu_bound);
            // XXX TODO: if we're just returning a number, it should be the
            // lowest depth, but we should really report all of them
        },
        LookaheadBound::Seconds(_) => {
            let (fs, ds) = iterative_deepening_kickoff(
                &world, bound.duration(), false, déjà_vu_bound);
            forecasts = fs;
            depth = ds;
        }
    }
    let stop_thinking = get_time();
    let thinking_time = stop_thinking - start_thinking;
    (forecasts, depth, thinking_time)
}

#[derive(RustcEncodable, RustcDecodable)]
struct Postcard {
    world: String,
    patch: Patch,
    hospitalization: Option<Agent>,
    thinking_time: u64,
    depth: u8,
    counterreplies: Vec<Patch>,
    rosetta_stone: String,
}

#[derive(RustcEncodable, RustcDecodable)]
struct LastMissive {
    the_triumphant: Option<Team>,
}

#[allow(collapsible_if)]
fn correspondence(reminder: &str, bound: LookaheadBound, déjà_vu_bound: f32)
                  -> String {
    let in_medias_res = WorldState::reconstruct(reminder);
    let (mut forecasts, depth, sidereal) = forecast(in_medias_res,
                                                    bound,
                                                    déjà_vu_bound);

    if !forecasts.is_empty() {
        let (determination, _karma, _variation) = forecasts.swap_remove(0);
        // XXX TODO FIXME: this doesn't distinguish amongst ascensions
        // (and we can imagine somewhat contrived situations where only
        // some of them are admissible movements)
        let counterreplies = determination.tree
                                          .lookahead()
                                          .iter()
                                          .map(|c| c.patch)
                                          .collect::<Vec<_>>();
        if counterreplies.is_empty() {
            if determination.tree.in_critical_endangerment(Team::Orange) {
                return json::encode(&LastMissive {
                           the_triumphant: Some(Team::Blue),
                       })
                           .unwrap();
            } else {
                return json::encode(&LastMissive { the_triumphant: None }).unwrap();
            }
        }
        let postcard = Postcard {
            world: determination.tree.preserve(),
            patch: determination.patch,
            hospitalization: determination.hospitalization,
            thinking_time: sidereal.num_milliseconds() as u64,
            depth,
            counterreplies,
            rosetta_stone: determination.patch.abbreviated_pagan_movement_rune(),
        };
        json::encode(&postcard).unwrap()
    } else {
        if in_medias_res.in_critical_endangerment(Team::Blue) {
            json::encode(&LastMissive { the_triumphant: Some(Team::Orange) })
                .unwrap()
        } else {
            json::encode(&LastMissive { the_triumphant: None }).unwrap()
        }
    }
}

fn the_end() {
    println!("THE END");
    process::exit(0);
}

fn main() {
    // Does argparse not offer an analogue of Python's argparse's
    // `add_mutually_exclusive_group`
    // (https://docs.python.org/3/library/argparse.html#mutual-exclusion)?
    // Contribution opportunity if so??
    let mut lookahead_depth: Option<u8> = None;
    let mut lookahead_extension: Option<u8> = None;
    // TODO CONSIDER: would argparse's Collect action be cleaner?
    let mut lookahead_depth_sequence: Option<String> = None;
    let mut lookahead_seconds: Option<u8> = None;
    let mut from_runes: Option<String> = None;
    let mut correspond: bool = false;
    let mut uci_dæmon: bool = false;
    let mut déjà_vu_bound: f32 = 2.0;
    let mut debug_logging: bool = false;
    {
        let mut parser = ArgumentParser::new();
        parser.set_description("Leafline: an oppositional strategy game engine");
        parser.refer(&mut lookahead_depth).add_option(
            &["--depth"],
            StoreOption,
            "rank moves using AI minimax lookahead this deep");
        parser.refer(&mut lookahead_extension).add_option(
            &["--quiet"],
            StoreOption,
            "search with quietness extension this deep");
        parser.refer(&mut lookahead_depth_sequence).add_option(
            &["--depth-sequence"],
            StoreOption,
            "rank moves using AI minimax lookahead to these depths");
        parser.refer(&mut lookahead_seconds).add_option(
            &["--seconds"],
            StoreOption,
            "rank moves using AI minimax for about this many seconds");
        parser.refer(&mut correspond).add_option(
            &["--correspond"],
            StoreTrue,
            "just output the serialization of the AI's top response and \
             legal replies thereto");
        parser.refer(&mut uci_dæmon).add_option(
            &["--uci", "--deamon", "--dæmon"],
            StoreTrue,
            "run Unlikely Command Integration dæmon for external driver play");
        parser.refer(&mut from_runes).add_option(
            &["--from"],
            StoreOption,
            "start a game from the given book of preservation runes");
        parser.refer(&mut déjà_vu_bound).add_option(
            &["--déjà-vu-bound", "--deja-vu-bound"],
            Store,
            "try to not store more entries in the déjà vu table than fit in \
             this many GiB of memory"
        );
        parser.refer(&mut debug_logging).add_option(
            &["--debug"],
            StoreTrue,
            "run with debug logging to file"
        );
        parser.add_option(&["--version", "-v"],
            Print(env!("CARGO_PKG_VERSION").to_owned()), "diplay the version");
        parser.parse_args_or_exit();
    }

    if debug_logging {
        DebugLogger::init().expect("couldn't initialize logging?!")
    }

    if correspond {
        let bound_maybe_result = LookaheadBound::from_args(lookahead_depth,
                                                           lookahead_extension,
                                                           lookahead_depth_sequence,
                                                           lookahead_seconds);
        let bound = match bound_maybe_result {
            Ok(bound_maybe) => {
                match bound_maybe {
                    Some(bound) => bound,
                    None => {
                        moral_panic!("`--correspond` passed without exactly one \
                                      of `--depth`, `--depth-sequence`, or \
                                      `--seconds`")
                    }
                }
            }
            Err(error) => {
                moral_panic!(error)
            }
        };
        let from = from_runes.expect("`--correspond` requires `--from`");
        println!("{}", correspondence(&from, bound, déjà_vu_bound));
        process::exit(0);
    }

    if uci_dæmon {
        uci::dæmon();
        // ↑ dæmon will loop
        process::exit(0);
    }

    println!("Welcome to Leafline v. {}!", env!("CARGO_PKG_VERSION"));
    match memory_free() {
        Some(bytes) => {
            println!("Leafline substrate accountant detected {:.3} \
                     GiB of free memory.",
                     bytes.in_gib());
        },
        None => {
            println!("Could not detect amount of free memory! \
                      It is possible \
                      that you are struggling with an inferior nonfree \
                      operating system forced on you by your masters in \
                      Cupertino or Redmond");
        }
    }

    let mut world = match from_runes {
        Some(runes) => WorldState::reconstruct(&runes),
        None => WorldState::new(),
    };
    let mut premonitions: Vec<Commit>;
    let bound_maybe = LookaheadBound::from_args(lookahead_depth,
                                                lookahead_extension,
                                                lookahead_depth_sequence,
                                                lookahead_seconds)
                          .unwrap();
    loop {
        match bound_maybe {
            None => {
                premonitions = world.lookahead();
                if premonitions.is_empty() {
                    // XXX TODO distinguish between deadlock and
                    // ultimate endangerment
                    the_end();
                }
                println!("{}", world);
                for (index, premonition) in premonitions.iter().enumerate() {
                    println!("{:>2}. {}", index, premonition)
                }
            }
            Some(ref bound) => {
                let (our_forecasts, depth, thinking_time) =
                    forecast(world, bound.clone(), déjà_vu_bound);
                let forecasts = our_forecasts;
                println!("{}", world);
                let depth_report = match *bound {
                    LookaheadBound::Depth(standard, extension) => {
                        match extension {
                            Some(quietening) => format!(
                                "at least {} and up to {}",
                                standard,
                                standard + quietening),
                            None => format!("{}", depth)
                        }
                    }
                    _ => format!("{}", depth),
                };
                println!("(scoring alternatives {} levels deep took {} ms)",
                         depth_report,
                         thinking_time.num_milliseconds());
                premonitions = Vec::new();
                for (index, sight) in forecasts.into_iter().enumerate() {
                    let (commit, score, variation) = sight;
                    println!("{:>2}: {} — score {} ‣ representative variation: {}",
                             index,
                             commit,
                             Color::Purple.bold()
                                          .paint(&format!("{:.1}", score)),
                             pagan_variation_format(&variation));
                    premonitions.push(commit);
                }

                if premonitions.is_empty() {
                    the_end();
                }
            }
        }

        loop {
            print!("\nSelect a move>> ");
            io::stdout().flush().expect("couldn't flush stdout");
            let mut input_buffer = String::new();
            io::stdin()
                .read_line(&mut input_buffer)
                .expect("couldn't read input");

            if input_buffer.trim() == "quit" {
                the_end();
            }

            let choice: usize = match input_buffer.trim().parse() {
                Ok(i) => i,
                Err(e) => {
                    println!("Error parsing choice: {:?}. Try again.", e);
                    continue;
                }
            };
            if choice < premonitions.len() {
                world = premonitions[choice].tree;
                break;
            } else {
                println!("{} isn't among the choices. Try again.", choice);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{LookaheadBound, correspondence};

    #[test]
    fn concerning_correspondence_victory_conditions() {
        let blue_concession = correspondence("R6k/6pp/8/8/8/8/8/8 b - -",
                                             LookaheadBound::Depth(2, None),
                                             1.0);
        assert_eq!("{\"the_triumphant\":\"Orange\"}".to_owned(),
                   blue_concession);
    }

}
<|endoftext|>


<commit_before>extern crate "rustc-serialize" as serialize;
extern crate handlebars;

use std::old_io as io;
use serialize::json::Json;
use handlebars::Handlebars;

fn main() {
   let input = if io::stdio::stdin_raw().isatty() {
     io::stdin().read_line().ok().expect("json expected")
   } else {
     "{}".to_string()
   };
   let json = Json::from_str(&input).ok().expect("malformed json");
   let mut handlebars = Handlebars::new();
   handlebars.register_template_string("t","hello {{foo}}".to_string()).ok().expect("expected template");
   println!("{}", handlebars.render("t", &json).ok().unwrap());
}
<commit_msg>read template from a file<commit_after>extern crate "rustc-serialize" as serialize;
extern crate handlebars;

use handlebars::Handlebars;
use serialize::json::Json;
use std::old_io as io;
use std::os;

fn main() {
   let template = match os::args().as_slice() {
      [_, ref bars] => io::File::open(&Path::new(&bars)).ok().expect("invalid path").read_to_string().ok().expect("invalid file"),
           _ => "{{.}}".to_string()
   };
   let input = if io::stdio::stdin_raw().isatty() {
     "{}".to_string()
   } else {
     io::stdin().read_line().ok().expect("json expected")
   };
   let json = Json::from_str(&input).ok().expect("malformed json");
   let mut handlebars = Handlebars::new();
   handlebars.register_template_string("t", template).ok().expect("expected template");
   println!("{}", handlebars.render("t", &json).ok().unwrap());
}
<|endoftext|>


<commit_before>use std::env;

const HELP_MESSAGE: &str = r#"
Usage:
  table -h | --help

Options:
  -h --help  Print help message
"#;

fn main() {
    for arg in env::args() {
        match &*arg {
            "-h" | "--help" => println!("{}", HELP_MESSAGE.trim()),
            _ => println!("TODO: implement other flags"),
        }
    }
}
<commit_msg>Print version with -v or --version flag<commit_after>use std::env;

const HELP_MESSAGE: &str = r#"
Usage:
  table -h | --help
  table -v | --version

Options:
  -h --help     Print help message
  -v --version  Print version
"#;

const VERSION: &str = "0.3.0";

fn main() {
    for arg in env::args() {
        match &*arg {
            "-h" | "--help" => println!("{}", HELP_MESSAGE.trim()),
            "-v" | "--version" => println!("{}", VERSION),
            _ => println!("TODO: implement other flags"),
        }
    }
}
<|endoftext|>


<commit_before>extern crate orbclient;

use orbclient::BmpFile;

use super::{CloneCell, Color, CopyCell, Event, Place, Point, Rect, Renderer, Widget, Window};
use super::callback::Click;

pub struct Menu {
    rect: CopyCell<Rect>,
    text: CloneCell<String>,
    fg: Color,
    entries: Vec<Box<Entry>>,
    activated: CopyCell<bool>,
}

pub struct Action {
    rect: CopyCell<Rect>,
    text: CloneCell<String>,
    icon: Option<BmpFile>,
    bg_up: Color,
    bg_down: Color,
    click_callback: Option<Arc<Fn()>>,
    pressed: CopyCell<bool>,
}

pub struct Separator;

pub trait Entry {
    fn text<S: AsRef<str>>(&mut self) -> S;
}

impl Menu {
    pub fn new(name: &str) -> Self {
        Menu {
            rect: Rect::default(),
            text: CloneCell::new(name.to_owned()),
            fg: Color::rgb(0, 0, 0),
            entries: Vec::with_capacity(10),
            activated: false,
        }
    }

    pub fn add_entry<E: Entry + Place>(mut self, mut entry: E) -> Self {
        {
            let entry_text_len = entry.text().len();
            if self.rect.width < entry_text_len {
                // TODO: consider the icon width and some padding
                self.rect.width = entry_text_len;
            }
        }

        if entry.text() == "separator" {
            self.entries.insert(entry.size(self.rect.width, 10));
        } else {
            self.entries.insert(entry.size(self.rect.width, 30));
        }

        self
    }
}

impl Widget for Menu {
    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();

        if self.activated.get() {
            renderer.rect(rect, self.fg);
        }
    }
}

impl Action {
    pub fn new(text: &str) -> Self {
        Action {
            rect: Rect::default(),
            text: CloneCell::new(name.to_owned()),
            icon: None,
            bg_up: Color::rgb(220, 222, 227),
            bg_down: Color::rgb(203, 205, 210),
            click_callback: None,
            pressed: CopyCell::new(false),
        }
    }

    pub fn add_icon(mut self, icon: BmpFile) -> Self {
        self.icon = Some(icon);
        self
    }
}

impl Place for Action {
    fn position(self, x: i32, y: i32) -> Self {
        let mut rect = self.rect.get();
        rect.x = x;
        rect.y = y;
        self.rect.set(rect);

        self
    }

    fn size(self, width: u32, height: u32) -> Self {
        let mut rect = self.rect.get();
        rect.width = width;
        rect.height = height;
        self.rect.set(rect);

        self
    }
}

impl Widget for Action {
    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();

        if self.pressed.get() {
            renderer.rect(rect, self.bg_down);
        } else {
            renderer.rect(rect, self.bg_up);
        }

        //let text = self.text.borrow();

        //let mut x = 0;
        //let mut y = 0;
        //for c in text.chars() {
        //    if c == '\n' {
        //        x = 0;
        //        y += 16;
        //    } else {
        //        if x + 8 <= rect.width as i32 && y + 16 <= rect.height as i32 {
        //            renderer.char(Point::new(x, y) + rect.point(), c, self.fg);
        //        }
        //        x += 8;
        //    }
        //}
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;
                let rect = self.rect.get();

                if rect.contains(point) {
                    if left_button && self.pressed.check_set(true) {
                        *redraw = true;
                    } else if self.pressed.check_set(false) {
                        *click = true;
                        redraw = true;
                    }
                } else if !left_button && self.pressed.check_set(false) {
                    *redraw = true;
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
        }
    }
}

impl Entry for Menu {
    fn text<S: AsRef<str>>(&mut self) -> S {
        self.text.get()
    }
}

impl Entry for Action {
    fn text<S: AsRef<str>>(&mut self) -> S {
        self.text.get()
    }
}

impl Entry for Separator {
    fn text<S: AsRef<str>>(&mut self) -> S {
        "separator"
    }
}
<commit_msg>Update `Place` impl for `Action` and `Menu`<commit_after>extern crate orbclient;

use orbclient::BmpFile;

use super::{CloneCell, Color, CopyCell, Event, Place, Point, Rect, Renderer, Widget, Window};
use super::callback::Click;

pub struct Menu {
    rect: CopyCell<Rect>,
    text: CloneCell<String>,
    fg: Color,
    entries: Vec<Box<Entry>>,
    activated: CopyCell<bool>,
}

pub struct Action {
    rect: CopyCell<Rect>,
    text: CloneCell<String>,
    icon: Option<BmpFile>,
    bg_up: Color,
    bg_down: Color,
    click_callback: Option<Arc<Fn()>>,
    pressed: CopyCell<bool>,
}

pub struct Separator;

pub trait Entry {
    fn text<S: AsRef<str>>(&mut self) -> S;
}

impl Menu {
    pub fn new(name: &str) -> Self {
        Menu {
            rect: Rect::default(),
            text: CloneCell::new(name.to_owned()),
            fg: Color::rgb(0, 0, 0),
            entries: Vec::with_capacity(10),
            activated: false,
        }
    }

    pub fn add_entry<E: Entry + Place>(mut self, mut entry: E) -> Self {
        {
            let entry_text_len = entry.text().len();
            if self.rect.width < entry_text_len {
                // TODO: consider the icon width and some padding
                self.rect.width = entry_text_len;
            }
        }

        if entry.text() == "separator" {
            self.entries.insert(entry.size(self.rect.width, 10));
        } else {
            self.entries.insert(entry.size(self.rect.width, 30));
        }

        self
    }
}

impl Widget for Menu {
    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();

        if self.activated.get() {
            renderer.rect(rect, self.fg);
        }
    }
}

impl Place for Menu {
    fn rect(&self) -> &CopyCell<Rect> {
        &self.rect
    }
}

impl Action {
    pub fn new(text: &str) -> Self {
        Action {
            rect: Rect::default(),
            text: CloneCell::new(name.to_owned()),
            icon: None,
            bg_up: Color::rgb(220, 222, 227),
            bg_down: Color::rgb(203, 205, 210),
            click_callback: None,
            pressed: CopyCell::new(false),
        }
    }

    pub fn add_icon(mut self, icon: BmpFile) -> Self {
        self.icon = Some(icon);
        self
    }
}

impl Place for Action {
    fn rect(&self) -> &CopyCell<Rect> {
        &self.rect
    }
}

impl Widget for Action {
    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();

        if self.pressed.get() {
            renderer.rect(rect, self.bg_down);
        } else {
            renderer.rect(rect, self.bg_up);
        }

        //let text = self.text.borrow();

        //let mut x = 0;
        //let mut y = 0;
        //for c in text.chars() {
        //    if c == '\n' {
        //        x = 0;
        //        y += 16;
        //    } else {
        //        if x + 8 <= rect.width as i32 && y + 16 <= rect.height as i32 {
        //            renderer.char(Point::new(x, y) + rect.point(), c, self.fg);
        //        }
        //        x += 8;
        //    }
        //}
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;
                let rect = self.rect.get();

                if rect.contains(point) {
                    if left_button && self.pressed.check_set(true) {
                        *redraw = true;
                    } else if self.pressed.check_set(false) {
                        *click = true;
                        redraw = true;
                    }
                } else if !left_button && self.pressed.check_set(false) {
                    *redraw = true;
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
        }
    }
}

impl Entry for Menu {
    fn text<S: AsRef<str>>(&mut self) -> S {
        self.text.get()
    }
}

impl Entry for Action {
    fn text<S: AsRef<str>>(&mut self) -> S {
        self.text.get()
    }
}

impl Entry for Separator {
    fn text<S: AsRef<str>>(&mut self) -> S {
        "separator"
    }
}
<|endoftext|>


<commit_before>use std::f32::{NEG_INFINITY, INFINITY};
use std::cmp::Ordering;

use identity::{Team, JobDescription, Agent};
use life::{Commit, WorldState};

pub fn orientation(team: Team) -> f32 {
    match team {
        Team::Orange => 1.0,
        Team::Blue => -1.0
    }
}

pub fn figurine_valuation(agent: Agent) -> f32 {
    let Agent { team: team, job_description: job_description } = agent;
    let value = match job_description {
        // en.wikipedia.org/wiki/
        // Chess_piece_relative_value#Hans_Berliner.27s_system
        JobDescription::Servant => 1.0,
        JobDescription::Pony => 3.2,
        JobDescription::Scholar => 3.3,
        JobDescription::Cop => 5.1,
        JobDescription::Princess => 8.8,
        JobDescription::Figurehead => 20000.0
    };
    orientation(team) * value
}

pub fn score(world: WorldState) -> f32 {
    let mut valuation = 0.0;
    for team in Team::league().into_iter() {
        for agent in Agent::dramatis_personae(team).into_iter() {
            valuation += world.agent_to_pinfield_ref(
                agent).pincount() as f32 * figurine_valuation(agent);
        }
    }
    valuation
}

pub fn negamax_search(world: WorldState, depth: u8) -> (Option<Commit>, f32) {
    let team = world.to_move;
    let premonitions = world.reckless_lookahead();
    if depth == 0 || premonitions.is_empty() {
        return (None, orientation(team) * score(world))
    }
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let (_after, mut value) = negamax_search(premonition.tree, depth-1);
        value = -value;
        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
    }
    (optimand, optimum)
}

// _very_ tempted to switch to developing on Nightly just so we can
// ungate non_ascii_idents and call this α_β_negamax_search
pub fn alpha_beta_negamax_search(world: WorldState, depth: u8,
                                 alpha: f32, beta: f32) -> (Option<Commit>,
                                                            f32) {
    // RESEARCH: I don't really care that much right now, but can you
    // mutate (reassign) an argument name, and if so, what is the syntax?
    let mut experienced_alpha = alpha;
    let team = world.to_move;
    let premonitions = world.reckless_lookahead();
    if depth == 0 || premonitions.is_empty() {
        return (None, orientation(team) * score(world))
    };
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let (_after, mut value) = alpha_beta_negamax_search(
            premonition.tree, depth-1, -beta, -experienced_alpha);
        value = -value;
        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
        if value > experienced_alpha {
            experienced_alpha = value;
        }
        if experienced_alpha >= beta {
            break;
        }
    }
    (optimand, optimum)
}

// The vision here is that for the turn I'm immediately going to take,
// I want a report of all possible moves ranked by negamax-computed
// optimality, but that we don't want to bother with all that bookkeeping
// for subsequent levels of the game tree; minimax is expensive enough
// already!!
pub fn kickoff(world: WorldState, depth: u8) -> Vec<(Commit, f32)> {
    let team = world.to_move;
    let premonitions = world.lookahead();
    let mut forecasts = Vec::new();
    for premonition in premonitions.into_iter() {
        let (_grandchild, mut value) = alpha_beta_negamax_search(
            premonition.tree, depth-1, NEG_INFINITY, INFINITY);
        value = -value;
        forecasts.push((premonition, value));
    }
    // The circumlocution (thanks to
    // https://www.reddit.com/r/rust/comments/29kia3/no_ord_for_f32/ for
    // the suggestion) is because Rust is sanctimonious about IEEE 754
    // floats not being totally ordered
    forecasts.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
    forecasts
}

#[cfg(test)]
mod test {
    use time;

    use super::{negamax_search, kickoff, score};
    use space::Locale;
    use life::WorldState;

    #[test]
    fn concerning_fairness_of_the_initial_position() {
        assert_eq!(0.0, score(WorldState::new()));
    }

    #[test]
    fn experimentally_about_kickoff() {
        let mut world = WorldState::new_except_empty();
        // SCENARIO: let's imagine Orange (to move) has separate attacks against
        // Blue's pony and servant, against which Blue has no defense but
        // to run away. We predict that Orange will take the pony, and
        // then Blue will move the servant out of the way.

        // scholar endangers pony
        world.blue_ponies = world.blue_ponies.alight(
            Locale { rank: 0, file: 0 }
        );
        world.orange_scholars = world.orange_scholars.alight(
            Locale { rank: 2, file: 2 }
        );

        // pony endangers servant
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 7, file: 1 }
        );
        world.orange_ponies = world.orange_ponies.alight(
            Locale { rank: 5, file: 2 }
        );

        // Blue has another servant sitting nowhere interesting
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 3, file: 6 }
        );

        let depth = 2;
        let start = time::get_time();
        let advisory = kickoff(world, depth);
        let end = time:: get_time();

        // (you can see this if you run the tests with `-- --nocapture`)
        println!("negamax kickoff: evaluating {} possible choices to \
                  depth {} took {:?}", advisory.len(), depth, end-start);
        for item in advisory.iter() {
            println!("{:?}", item);
        }

        // taking the pony is the right thing to do
        assert_eq!(Locale { rank: 0, file: 0 }, advisory[0].0.patch.whither);

        // And, furthermore, the answer should be the same if we face the
        // same situation with the colors reversed
        //
        // XXX this would be tidier and less copy-pastey if I had more
        // general figurine-placing functions that were three rather than
        // two levels of abstraction above twiddling bits on an unsigned
        // int ... oh, well
        let mut negaworld = WorldState::new_except_empty();

        // scholar endangers pony
        negaworld.orange_ponies = negaworld.orange_ponies.alight(
            Locale { rank: 0, file: 0 }
        );
        negaworld.blue_scholars = negaworld.blue_scholars.alight(
            Locale { rank: 2, file: 2 }
        );

        // pony endanger servant
        negaworld.orange_servants = negaworld.orange_servants.alight(
            Locale { rank: 7, file: 1 }
        );
        negaworld.blue_ponies = negaworld.blue_ponies.alight(
            Locale { rank: 5, file: 2 }
        );

        // Orange has another servant sitting nowhere interesting
        negaworld.orange_servants = world.orange_servants.alight(
            Locale { rank: 3, file: 6 }
        );

        let negadvisory = kickoff(negaworld, depth);

        // taking the pony is still the right thing to do, even in the
        // negaworld
        assert_eq!(Locale { rank: 0, file: 0 }, advisory[0].0.patch.whither);
    }

}
<commit_msg>MMV-LVA move-ordering heuristic optimization<commit_after>use std::f32::{NEG_INFINITY, INFINITY};
use std::cmp::Ordering;

use identity::{Team, JobDescription, Agent};
use life::{Commit, WorldState};

pub fn orientation(team: Team) -> f32 {
    match team {
        Team::Orange => 1.0,
        Team::Blue => -1.0
    }
}

pub fn figurine_valuation(agent: Agent) -> f32 {
    let Agent { team: team, job_description: job_description } = agent;
    let value = match job_description {
        // en.wikipedia.org/wiki/
        // Chess_piece_relative_value#Hans_Berliner.27s_system
        JobDescription::Servant => 1.0,
        JobDescription::Pony => 3.2,
        JobDescription::Scholar => 3.3,
        JobDescription::Cop => 5.1,
        JobDescription::Princess => 8.8,
        JobDescription::Figurehead => 20000.0
    };
    orientation(team) * value
}

pub fn score(world: WorldState) -> f32 {
    let mut valuation = 0.0;
    for team in Team::league().into_iter() {
        for agent in Agent::dramatis_personae(team).into_iter() {
            valuation += world.agent_to_pinfield_ref(
                agent).pincount() as f32 * figurine_valuation(agent);
        }
    }
    valuation
}

fn mmv_lva_heuristic(commit: &Commit) -> f32 {
    // https://chessprogramming.wikispaces.com/MVV-LVA
    match commit.hospitalization {
        Some(patient) => (figurine_valuation(patient) -
                          figurine_valuation(commit.patch.star)),
        None => 0.0
    }
}

fn order_moves(commits: &mut Vec<Commit>) {
    commits.sort_by(|a, b| mmv_lva_heuristic(
        &b).partial_cmp(&mmv_lva_heuristic(&a)).unwrap_or(Ordering::Equal));
}

pub fn negamax_search(world: WorldState, depth: u8) -> (Option<Commit>, f32) {
    let team = world.to_move;
    let mut premonitions = world.reckless_lookahead();
    order_moves(&mut premonitions);
    if depth == 0 || premonitions.is_empty() {
        return (None, orientation(team) * score(world))
    }
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let (_after, mut value) = negamax_search(premonition.tree, depth-1);
        value = -value;
        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
    }
    (optimand, optimum)
}

// _very_ tempted to switch to developing on Nightly just so we can
// ungate non_ascii_idents and call this α_β_negamax_search
pub fn alpha_beta_negamax_search(world: WorldState, depth: u8,
                                 alpha: f32, beta: f32) -> (Option<Commit>,
                                                            f32) {
    // RESEARCH: I don't really care that much right now, but can you
    // mutate (reassign) an argument name, and if so, what is the syntax?
    let mut experienced_alpha = alpha;
    let team = world.to_move;
    let mut premonitions = world.reckless_lookahead();
    order_moves(&mut premonitions);
    if depth == 0 || premonitions.is_empty() {
        return (None, orientation(team) * score(world))
    };
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let (_after, mut value) = alpha_beta_negamax_search(
            premonition.tree, depth-1, -beta, -experienced_alpha);
        value = -value;
        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
        if value > experienced_alpha {
            experienced_alpha = value;
        }
        if experienced_alpha >= beta {
            break;
        }
    }
    (optimand, optimum)
}

// The vision here is that for the turn I'm immediately going to take,
// I want a report of all possible moves ranked by negamax-computed
// optimality, but that we don't want to bother with all that bookkeeping
// for subsequent levels of the game tree; minimax is expensive enough
// already!!
pub fn kickoff(world: WorldState, depth: u8) -> Vec<(Commit, f32)> {
    let team = world.to_move;
    let mut premonitions = world.lookahead();
    order_moves(&mut premonitions);
    let mut forecasts = Vec::new();
    for premonition in premonitions.into_iter() {
        let (_grandchild, mut value) = alpha_beta_negamax_search(
            premonition.tree, depth-1, NEG_INFINITY, INFINITY);
        value = -value;
        forecasts.push((premonition, value));
    }
    // The circumlocution (thanks to
    // https://www.reddit.com/r/rust/comments/29kia3/no_ord_for_f32/ for
    // the suggestion) is because Rust is sanctimonious about IEEE 754
    // floats not being totally ordered
    forecasts.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
    forecasts
}

#[cfg(test)]
mod test {
    use time;

    use super::{negamax_search, kickoff, score};
    use space::Locale;
    use life::WorldState;

    #[test]
    fn concerning_fairness_of_the_initial_position() {
        assert_eq!(0.0, score(WorldState::new()));
    }

    #[test]
    fn experimentally_about_kickoff() {
        let mut world = WorldState::new_except_empty();
        // SCENARIO: let's imagine Orange (to move) has separate attacks against
        // Blue's pony and servant, against which Blue has no defense but
        // to run away. We predict that Orange will take the pony, and
        // then Blue will move the servant out of the way.

        // scholar endangers pony
        world.blue_ponies = world.blue_ponies.alight(
            Locale { rank: 0, file: 0 }
        );
        world.orange_scholars = world.orange_scholars.alight(
            Locale { rank: 2, file: 2 }
        );

        // pony endangers servant
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 7, file: 1 }
        );
        world.orange_ponies = world.orange_ponies.alight(
            Locale { rank: 5, file: 2 }
        );

        // Blue has another servant sitting nowhere interesting
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 3, file: 6 }
        );

        let depth = 2;
        let start = time::get_time();
        let advisory = kickoff(world, depth);
        let end = time:: get_time();

        // (you can see this if you run the tests with `-- --nocapture`)
        println!("negamax kickoff: evaluating {} possible choices to \
                  depth {} took {:?}", advisory.len(), depth, end-start);
        for item in advisory.iter() {
            println!("{:?}", item);
        }

        // taking the pony is the right thing to do
        assert_eq!(Locale { rank: 0, file: 0 }, advisory[0].0.patch.whither);

        // And, furthermore, the answer should be the same if we face the
        // same situation with the colors reversed
        //
        // XXX this would be tidier and less copy-pastey if I had more
        // general figurine-placing functions that were three rather than
        // two levels of abstraction above twiddling bits on an unsigned
        // int ... oh, well
        let mut negaworld = WorldState::new_except_empty();

        // scholar endangers pony
        negaworld.orange_ponies = negaworld.orange_ponies.alight(
            Locale { rank: 0, file: 0 }
        );
        negaworld.blue_scholars = negaworld.blue_scholars.alight(
            Locale { rank: 2, file: 2 }
        );

        // pony endanger servant
        negaworld.orange_servants = negaworld.orange_servants.alight(
            Locale { rank: 7, file: 1 }
        );
        negaworld.blue_ponies = negaworld.blue_ponies.alight(
            Locale { rank: 5, file: 2 }
        );

        // Orange has another servant sitting nowhere interesting
        negaworld.orange_servants = world.orange_servants.alight(
            Locale { rank: 3, file: 6 }
        );

        let negadvisory = kickoff(negaworld, depth);

        // taking the pony is still the right thing to do, even in the
        // negaworld
        assert_eq!(Locale { rank: 0, file: 0 }, advisory[0].0.patch.whither);
    }

}
<|endoftext|>


<commit_before>// Copyright (c) 2017 The Noise-rs Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
// or http://opensource.org/licenses/MIT>, at your option. All files in the
// project carrying such notice may not be copied, modified, or distributed
// except according to those terms.

use math::{Point2, Point3, Point4};
use noise_fns::{Fbm, MultiFractal, NoiseFn, Seedable};

/// Default seed for the Turbulence noise function.
pub const DEFAULT_TURBULENCE_SEED: u32 = 0;
/// Default frequency for the Turbulence noise function.
pub const DEFAULT_TURBULENCE_FREQUENCY: f64 = 1.0;
/// Default power for the turbulence noise function.
pub const DEFAULT_TURBULENCE_POWER: f64 = 1.0;
/// Default roughness for the Turbulence noise function.
pub const DEFAULT_TURBULENCE_ROUGHNESS: usize = 3;

/// Noise function that randomly displaces the input value before returning the
/// output value from the source function.
///
/// _Turbulence_ is the pseudo-random displacement of the input value. The
/// get() method randomly displaces the coordinates of the input value before
/// retrieving the output value from the source function. To control the
/// turbulence, an application can modify its frequency, its power, and its
/// roughness.
pub struct Turbulence<Source> {
    /// Source function that outputs a value.
    pub source: Source,

    /// Frequency value for the Turbulence function.
    pub frequency: f64,

    /// Controls the strength of the turbulence by affecting how much each
    /// point is moved.
    pub power: f64,

    /// Affects the roughness of the turbulence. Higher values are rougher.
    pub roughness: usize,

    seed: u32,
    x_distort_function: Fbm,
    y_distort_function: Fbm,
    z_distort_function: Fbm,
    u_distort_function: Fbm,
}

impl<Source> Turbulence<Source> {
    pub fn new(source: Source) -> Turbulence<Source> {
        Turbulence {
            source: source,
            seed: DEFAULT_TURBULENCE_SEED,
            frequency: DEFAULT_TURBULENCE_FREQUENCY,
            power: DEFAULT_TURBULENCE_POWER,
            roughness: DEFAULT_TURBULENCE_ROUGHNESS,
            x_distort_function: Fbm::new()
                .set_seed(DEFAULT_TURBULENCE_SEED)
                .set_octaves(DEFAULT_TURBULENCE_ROUGHNESS)
                .set_frequency(DEFAULT_TURBULENCE_FREQUENCY),
            y_distort_function: Fbm::new()
                .set_seed(DEFAULT_TURBULENCE_SEED + 1)
                .set_octaves(DEFAULT_TURBULENCE_ROUGHNESS)
                .set_frequency(DEFAULT_TURBULENCE_FREQUENCY),
            z_distort_function: Fbm::new()
                .set_seed(DEFAULT_TURBULENCE_SEED + 2)
                .set_octaves(DEFAULT_TURBULENCE_ROUGHNESS)
                .set_frequency(DEFAULT_TURBULENCE_FREQUENCY),
            u_distort_function: Fbm::new()
                .set_seed(DEFAULT_TURBULENCE_SEED + 3)
                .set_octaves(DEFAULT_TURBULENCE_ROUGHNESS)
                .set_frequency(DEFAULT_TURBULENCE_FREQUENCY),
        }
    }

    pub fn set_frequency(self, frequency: f64) -> Turbulence<Source> {
        Turbulence {
            frequency: frequency,
            x_distort_function: self.x_distort_function.set_frequency(frequency),
            y_distort_function: self.y_distort_function.set_frequency(frequency),
            z_distort_function: self.z_distort_function.set_frequency(frequency),
            u_distort_function: self.u_distort_function.set_frequency(frequency),
            ..self
        }
    }

    pub fn set_power(self, power: f64) -> Turbulence<Source> {
        Turbulence {
            power: power,
            ..self
        }
    }

    pub fn set_roughness(self, roughness: usize) -> Turbulence<Source> {
        Turbulence {
            roughness: roughness,
            x_distort_function: self.x_distort_function.set_octaves(roughness),
            y_distort_function: self.y_distort_function.set_octaves(roughness),
            z_distort_function: self.z_distort_function.set_octaves(roughness),
            u_distort_function: self.u_distort_function.set_octaves(roughness),
            ..self
        }
    }
}

impl<Source> Seedable for Turbulence<Source> {
    fn set_seed(self, seed: u32) -> Turbulence<Source> {
        Turbulence {
            seed: seed,
            x_distort_function: self.x_distort_function.set_seed(seed),
            y_distort_function: self.y_distort_function.set_seed(seed + 1),
            z_distort_function: self.z_distort_function.set_seed(seed + 2),
            u_distort_function: self.u_distort_function.set_seed(seed + 3),
            ..self
        }
    }

    fn seed(&self) -> u32 {
        self.seed
    }
}

impl<Source> NoiseFn<Point2<f64>> for Turbulence<Source>
where
    Source: NoiseFn<Point2<f64>>,
{
    fn get(&self, point: Point2<f64>) -> f64 {
        // First, create offsets based on the input values to keep the sampled
        // points from being near a integer boundary. This is a result of
        // using perlin noise, which returns zero at integer boundaries.
        let x0 = point[0] + 12414.0 / 65536.0;
        let y0 = point[1] + 65124.0 / 65536.0;

        let x1 = point[0] + 26519.0 / 65536.0;
        let y1 = point[1] + 18128.0 / 65536.0;

        let x_distort = point[0] + (self.x_distort_function.get([x0, y0]) * self.power);
        let y_distort = point[1] + (self.y_distort_function.get([x1, y1]) * self.power);

        self.source.get([x_distort, y_distort])
    }
}

impl<Source> NoiseFn<Point3<f64>> for Turbulence<Source>
where
    Source: NoiseFn<Point3<f64>>,
{
    fn get(&self, point: Point3<f64>) -> f64 {
        // First, create offsets based on the input values to keep the sampled
        // points from being near a integer boundary. This is a result of
        // using perlin noise, which returns zero at integer boundaries.
        let x0 = point[0] + 12414.0 / 65536.0;
        let y0 = point[1] + 65124.0 / 65536.0;
        let z0 = point[2] + 31337.0 / 65536.0;

        let x1 = point[0] + 26519.0 / 65536.0;
        let y1 = point[1] + 18128.0 / 65536.0;
        let z1 = point[2] + 60943.0 / 65536.0;

        let x2 = point[0] + 53820.0 / 65536.0;
        let y2 = point[1] + 11213.0 / 65536.0;
        let z2 = point[2] + 44845.0 / 65536.0;

        let x_distort = point[0] + (self.x_distort_function.get([x0, y0, z0]) * self.power);
        let y_distort = point[1] + (self.y_distort_function.get([x1, y1, z1]) * self.power);
        let z_distort = point[2] + (self.z_distort_function.get([x2, y2, z2]) * self.power);

        self.source.get([x_distort, y_distort, z_distort])
    }
}

impl<Source> NoiseFn<Point4<f64>> for Turbulence<Source>
where
    Source: NoiseFn<Point4<f64>>,
{
    fn get(&self, point: Point4<f64>) -> f64 {
        // First, create offsets based on the input values to keep the sampled
        // points from being near a integer boundary. This is a result of
        // using perlin noise, which returns zero at integer boundaries.
        let x0 = point[0] + 12414.0 / 65536.0;
        let y0 = point[1] + 65124.0 / 65536.0;
        let z0 = point[2] + 31337.0 / 65536.0;
        let u0 = point[3] + 57948.0 / 65536.0;

        let x1 = point[0] + 26519.0 / 65536.0;
        let y1 = point[1] + 18128.0 / 65536.0;
        let z1 = point[2] + 60943.0 / 65536.0;
        let u1 = point[3] + 48513.0 / 65536.0;

        let x2 = point[0] + 53820.0 / 65536.0;
        let y2 = point[1] + 11213.0 / 65536.0;
        let z2 = point[2] + 44845.0 / 65536.0;
        let u2 = point[3] + 39357.0 / 65536.0;

        let x3 = point[0] + 18128.0 / 65536.0;
        let y3 = point[1] + 44845.0 / 65536.0;
        let z3 = point[2] + 12414.0 / 65536.0;
        let u3 = point[3] + 60943.0 / 65536.0;

        let x_distort = point[0] + (self.x_distort_function.get([x0, y0, z0, u0]) * self.power);
        let y_distort = point[1] + (self.y_distort_function.get([x1, y1, z1, u1]) * self.power);
        let z_distort = point[2] + (self.z_distort_function.get([x2, y2, z2, u2]) * self.power);
        let u_distort = point[3] + (self.u_distort_function.get([x3, y3, z3, u3]) * self.power);

        self.source
            .get([x_distort, y_distort, z_distort, u_distort])
    }
}
<commit_msg>Derive Clone and Debug for Turbulence<commit_after>// Copyright (c) 2017 The Noise-rs Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
// or http://opensource.org/licenses/MIT>, at your option. All files in the
// project carrying such notice may not be copied, modified, or distributed
// except according to those terms.

use math::{Point2, Point3, Point4};
use noise_fns::{Fbm, MultiFractal, NoiseFn, Seedable};

/// Default seed for the Turbulence noise function.
pub const DEFAULT_TURBULENCE_SEED: u32 = 0;
/// Default frequency for the Turbulence noise function.
pub const DEFAULT_TURBULENCE_FREQUENCY: f64 = 1.0;
/// Default power for the turbulence noise function.
pub const DEFAULT_TURBULENCE_POWER: f64 = 1.0;
/// Default roughness for the Turbulence noise function.
pub const DEFAULT_TURBULENCE_ROUGHNESS: usize = 3;

/// Noise function that randomly displaces the input value before returning the
/// output value from the source function.
///
/// _Turbulence_ is the pseudo-random displacement of the input value. The
/// get() method randomly displaces the coordinates of the input value before
/// retrieving the output value from the source function. To control the
/// turbulence, an application can modify its frequency, its power, and its
/// roughness.
#[derive(Clone, Debug)]
pub struct Turbulence<Source> {
    /// Source function that outputs a value.
    pub source: Source,

    /// Frequency value for the Turbulence function.
    pub frequency: f64,

    /// Controls the strength of the turbulence by affecting how much each
    /// point is moved.
    pub power: f64,

    /// Affects the roughness of the turbulence. Higher values are rougher.
    pub roughness: usize,

    seed: u32,
    x_distort_function: Fbm,
    y_distort_function: Fbm,
    z_distort_function: Fbm,
    u_distort_function: Fbm,
}

impl<Source> Turbulence<Source> {
    pub fn new(source: Source) -> Turbulence<Source> {
        Turbulence {
            source: source,
            seed: DEFAULT_TURBULENCE_SEED,
            frequency: DEFAULT_TURBULENCE_FREQUENCY,
            power: DEFAULT_TURBULENCE_POWER,
            roughness: DEFAULT_TURBULENCE_ROUGHNESS,
            x_distort_function: Fbm::new()
                .set_seed(DEFAULT_TURBULENCE_SEED)
                .set_octaves(DEFAULT_TURBULENCE_ROUGHNESS)
                .set_frequency(DEFAULT_TURBULENCE_FREQUENCY),
            y_distort_function: Fbm::new()
                .set_seed(DEFAULT_TURBULENCE_SEED + 1)
                .set_octaves(DEFAULT_TURBULENCE_ROUGHNESS)
                .set_frequency(DEFAULT_TURBULENCE_FREQUENCY),
            z_distort_function: Fbm::new()
                .set_seed(DEFAULT_TURBULENCE_SEED + 2)
                .set_octaves(DEFAULT_TURBULENCE_ROUGHNESS)
                .set_frequency(DEFAULT_TURBULENCE_FREQUENCY),
            u_distort_function: Fbm::new()
                .set_seed(DEFAULT_TURBULENCE_SEED + 3)
                .set_octaves(DEFAULT_TURBULENCE_ROUGHNESS)
                .set_frequency(DEFAULT_TURBULENCE_FREQUENCY),
        }
    }

    pub fn set_frequency(self, frequency: f64) -> Turbulence<Source> {
        Turbulence {
            frequency: frequency,
            x_distort_function: self.x_distort_function.set_frequency(frequency),
            y_distort_function: self.y_distort_function.set_frequency(frequency),
            z_distort_function: self.z_distort_function.set_frequency(frequency),
            u_distort_function: self.u_distort_function.set_frequency(frequency),
            ..self
        }
    }

    pub fn set_power(self, power: f64) -> Turbulence<Source> {
        Turbulence {
            power: power,
            ..self
        }
    }

    pub fn set_roughness(self, roughness: usize) -> Turbulence<Source> {
        Turbulence {
            roughness: roughness,
            x_distort_function: self.x_distort_function.set_octaves(roughness),
            y_distort_function: self.y_distort_function.set_octaves(roughness),
            z_distort_function: self.z_distort_function.set_octaves(roughness),
            u_distort_function: self.u_distort_function.set_octaves(roughness),
            ..self
        }
    }
}

impl<Source> Seedable for Turbulence<Source> {
    fn set_seed(self, seed: u32) -> Turbulence<Source> {
        Turbulence {
            seed: seed,
            x_distort_function: self.x_distort_function.set_seed(seed),
            y_distort_function: self.y_distort_function.set_seed(seed + 1),
            z_distort_function: self.z_distort_function.set_seed(seed + 2),
            u_distort_function: self.u_distort_function.set_seed(seed + 3),
            ..self
        }
    }

    fn seed(&self) -> u32 {
        self.seed
    }
}

impl<Source> NoiseFn<Point2<f64>> for Turbulence<Source>
where
    Source: NoiseFn<Point2<f64>>,
{
    fn get(&self, point: Point2<f64>) -> f64 {
        // First, create offsets based on the input values to keep the sampled
        // points from being near a integer boundary. This is a result of
        // using perlin noise, which returns zero at integer boundaries.
        let x0 = point[0] + 12414.0 / 65536.0;
        let y0 = point[1] + 65124.0 / 65536.0;

        let x1 = point[0] + 26519.0 / 65536.0;
        let y1 = point[1] + 18128.0 / 65536.0;

        let x_distort = point[0] + (self.x_distort_function.get([x0, y0]) * self.power);
        let y_distort = point[1] + (self.y_distort_function.get([x1, y1]) * self.power);

        self.source.get([x_distort, y_distort])
    }
}

impl<Source> NoiseFn<Point3<f64>> for Turbulence<Source>
where
    Source: NoiseFn<Point3<f64>>,
{
    fn get(&self, point: Point3<f64>) -> f64 {
        // First, create offsets based on the input values to keep the sampled
        // points from being near a integer boundary. This is a result of
        // using perlin noise, which returns zero at integer boundaries.
        let x0 = point[0] + 12414.0 / 65536.0;
        let y0 = point[1] + 65124.0 / 65536.0;
        let z0 = point[2] + 31337.0 / 65536.0;

        let x1 = point[0] + 26519.0 / 65536.0;
        let y1 = point[1] + 18128.0 / 65536.0;
        let z1 = point[2] + 60943.0 / 65536.0;

        let x2 = point[0] + 53820.0 / 65536.0;
        let y2 = point[1] + 11213.0 / 65536.0;
        let z2 = point[2] + 44845.0 / 65536.0;

        let x_distort = point[0] + (self.x_distort_function.get([x0, y0, z0]) * self.power);
        let y_distort = point[1] + (self.y_distort_function.get([x1, y1, z1]) * self.power);
        let z_distort = point[2] + (self.z_distort_function.get([x2, y2, z2]) * self.power);

        self.source.get([x_distort, y_distort, z_distort])
    }
}

impl<Source> NoiseFn<Point4<f64>> for Turbulence<Source>
where
    Source: NoiseFn<Point4<f64>>,
{
    fn get(&self, point: Point4<f64>) -> f64 {
        // First, create offsets based on the input values to keep the sampled
        // points from being near a integer boundary. This is a result of
        // using perlin noise, which returns zero at integer boundaries.
        let x0 = point[0] + 12414.0 / 65536.0;
        let y0 = point[1] + 65124.0 / 65536.0;
        let z0 = point[2] + 31337.0 / 65536.0;
        let u0 = point[3] + 57948.0 / 65536.0;

        let x1 = point[0] + 26519.0 / 65536.0;
        let y1 = point[1] + 18128.0 / 65536.0;
        let z1 = point[2] + 60943.0 / 65536.0;
        let u1 = point[3] + 48513.0 / 65536.0;

        let x2 = point[0] + 53820.0 / 65536.0;
        let y2 = point[1] + 11213.0 / 65536.0;
        let z2 = point[2] + 44845.0 / 65536.0;
        let u2 = point[3] + 39357.0 / 65536.0;

        let x3 = point[0] + 18128.0 / 65536.0;
        let y3 = point[1] + 44845.0 / 65536.0;
        let z3 = point[2] + 12414.0 / 65536.0;
        let u3 = point[3] + 60943.0 / 65536.0;

        let x_distort = point[0] + (self.x_distort_function.get([x0, y0, z0, u0]) * self.power);
        let y_distort = point[1] + (self.y_distort_function.get([x1, y1, z1, u1]) * self.power);
        let z_distort = point[2] + (self.z_distort_function.get([x2, y2, z2, u2]) * self.power);
        let u_distort = point[3] + (self.u_distort_function.get([x3, y3, z3, u3]) * self.power);

        self.source
            .get([x_distort, y_distort, z_distort, u_distort])
    }
}
<|endoftext|>


<commit_before>use num::BigUint;
use num::traits::{Zero, One, FromPrimitive};
use digit_slice::{DigitSlice, FromDigits};
use {Shape, Noun, NockError, NockResult};

/// Evaluate the nock `*[subject formula]`
pub fn nock_on(mut subject: Noun, mut formula: Noun) -> NockResult {
    loop {
        if let Shape::Cell(ops, tail) = formula.clone().get() {
            match ops.as_u32() {
                // Axis
                Some(0) => return get_axis(&tail, &subject),

                // Just
                Some(1) => return Ok(tail.clone()),

                // Fire
                Some(2) => {
                    match tail.get() {
                        Shape::Cell(ref b, ref c) => {
                            let p = try!(nock_on(subject.clone(),
                                                 (*b).clone()));
                            let q = try!(nock_on(subject, (*c).clone()));
                            subject = p;
                            formula = q;
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Depth
                Some(3) => {
                    let p = try!(nock_on(subject.clone(), (*tail).clone()));
                    return match p.get() {
                        Shape::Cell(_, _) => Ok(Noun::from(0u32)),
                        _ => Ok(Noun::from(1u32)),
                    };
                }

                // Bump
                Some(4) => {
                    let p = try!(nock_on(subject.clone(), (*tail).clone()));
                    return match p.get() {
                        Shape::Atom(ref x) => {
                            // TODO: Non-bignum optimization
                            Ok(Noun::from(BigUint::from_digits(x).unwrap() +
                                          BigUint::one()))
                        }
                        _ => Err(NockError),
                    };
                }

                // Same
                Some(5) => {
                    let p = try!(nock_on(subject.clone(), (*tail).clone()));
                    return match p.get() {
                        Shape::Cell(ref a, ref b) => {
                            if a == b {
                                // Yes.
                                return Ok(Noun::from(0u32));
                            } else {
                                // No.
                                return Ok(Noun::from(1u32));
                            }
                        }
                        _ => return Err(NockError),
                    };
                }

                // If
                Some(6) => {
                    if let Some((b, c, d)) = tail.get_122() {
                        let p = try!(nock_on(subject.clone(), (*b).clone()));
                        match p.get() {
                            Shape::Atom(ref x) => {
                                if x == &0u32.as_digits() {
                                    formula = (*c).clone();
                                } else if x == &1u32.as_digits() {
                                    formula = (*d).clone();
                                } else {
                                    return Err(NockError);
                                }
                            }
                            _ => return Err(NockError),
                        }
                        continue;
                    } else {
                        return Err(NockError);
                    }
                }

                // Compose
                Some(7) => {
                    match tail.get() {
                        Shape::Cell(ref b, ref c) => {
                            let p = try!(nock_on(subject.clone(),
                                                 (*b).clone()));
                            subject = p;
                            formula = (*c).clone();
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Push
                Some(8) => {
                    match tail.get() {
                        Shape::Cell(ref b, ref c) => {
                            let p = try!(nock_on(subject.clone(),
                                                 (*b).clone()));
                            subject = Noun::cell(p, subject);
                            formula = (*c).clone();
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Call
                Some(9) => {
                    match tail.get() {
                        Shape::Cell(ref axis, ref c) => {
                            // Construct core.
                            subject = try!(nock_on(subject.clone(),
                                                   (*c).clone()));
                            // Fetch from core using axis.
                            formula = try!(get_axis(axis, &subject));
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Hint
                Some(10) => {
                    match tail.get() {
                        Shape::Cell(ref hint, ref c) => {
                            // Throw away hint.

                            // TODO: Check if hint is a cell and fail if it would
                            // crash.
                            formula = (*c).clone();
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Unhandled opcode
                Some(_) => {
                    return Err(NockError);
                }

                None => {
                    if let Shape::Cell(_, _) = ops.get() {
                        // Autocons
                        let a = try!(nock_on(subject.clone(), ops.clone()));
                        let b = try!(nock_on(subject, tail.clone()));
                        return Ok(Noun::cell(a, b));
                    } else {
                        return Err(NockError);
                    }
                }
            }
        } else {
            return Err(NockError);
        }
    }
}

fn get_axis(axis: &Noun, subject: &Noun) -> NockResult {
    // TODO: Optimize for small atoms.
    fn fas(x: BigUint, n: &Noun) -> NockResult {
        let two = BigUint::from_u32(2).unwrap();
        let three = BigUint::from_u32(3).unwrap();
        if x == BigUint::zero() {
            return Err(NockError);
        }
        if x == BigUint::one() {
            return Ok(n.clone());
        }
        if let Shape::Cell(ref a, ref b) = n.get() {
            if x == two {
                return Ok((*a).clone());
            } else if x == three {
                return Ok((*b).clone());
            } else {
                let half = x.clone() >> 1;
                let p = try!(fas(half, n));

                if x % two.clone() == BigUint::zero() {
                    fas(two, &p)
                } else {
                    fas(three, &p)
                }
            }
        } else {
            return Err(NockError);
        }
    }

    match axis.get() {
        Shape::Atom(ref x) => fas(BigUint::from_digits(x).unwrap(), subject),
        _ => Err(NockError)
    }
}
<commit_msg>Hint parsing<commit_after>use num::BigUint;
use num::traits::{Zero, One, FromPrimitive};
use digit_slice::{DigitSlice, FromDigits};
use {Shape, Noun, NockError, NockResult, FromNoun};

/// Evaluate the nock `*[subject formula]`
pub fn nock_on(mut subject: Noun, mut formula: Noun) -> NockResult {
    loop {
        if let Shape::Cell(ops, tail) = formula.clone().get() {
            match ops.as_u32() {
                // Axis
                Some(0) => return get_axis(&tail, &subject),

                // Just
                Some(1) => return Ok(tail.clone()),

                // Fire
                Some(2) => {
                    match tail.get() {
                        Shape::Cell(ref b, ref c) => {
                            let p = try!(nock_on(subject.clone(),
                                                 (*b).clone()));
                            let q = try!(nock_on(subject, (*c).clone()));
                            subject = p;
                            formula = q;
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Depth
                Some(3) => {
                    let p = try!(nock_on(subject.clone(), (*tail).clone()));
                    return match p.get() {
                        Shape::Cell(_, _) => Ok(Noun::from(0u32)),
                        _ => Ok(Noun::from(1u32)),
                    };
                }

                // Bump
                Some(4) => {
                    let p = try!(nock_on(subject.clone(), (*tail).clone()));
                    return match p.get() {
                        Shape::Atom(ref x) => {
                            // TODO: Non-bignum optimization
                            Ok(Noun::from(BigUint::from_digits(x).unwrap() +
                                          BigUint::one()))
                        }
                        _ => Err(NockError),
                    };
                }

                // Same
                Some(5) => {
                    let p = try!(nock_on(subject.clone(), (*tail).clone()));
                    return match p.get() {
                        Shape::Cell(ref a, ref b) => {
                            if a == b {
                                // Yes.
                                return Ok(Noun::from(0u32));
                            } else {
                                // No.
                                return Ok(Noun::from(1u32));
                            }
                        }
                        _ => return Err(NockError),
                    };
                }

                // If
                Some(6) => {
                    if let Some((b, c, d)) = tail.get_122() {
                        let p = try!(nock_on(subject.clone(), (*b).clone()));
                        match p.get() {
                            Shape::Atom(ref x) => {
                                if x == &0u32.as_digits() {
                                    formula = (*c).clone();
                                } else if x == &1u32.as_digits() {
                                    formula = (*d).clone();
                                } else {
                                    return Err(NockError);
                                }
                            }
                            _ => return Err(NockError),
                        }
                        continue;
                    } else {
                        return Err(NockError);
                    }
                }

                // Compose
                Some(7) => {
                    match tail.get() {
                        Shape::Cell(ref b, ref c) => {
                            let p = try!(nock_on(subject.clone(),
                                                 (*b).clone()));
                            subject = p;
                            formula = (*c).clone();
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Push
                Some(8) => {
                    match tail.get() {
                        Shape::Cell(ref b, ref c) => {
                            let p = try!(nock_on(subject.clone(),
                                                 (*b).clone()));
                            subject = Noun::cell(p, subject);
                            formula = (*c).clone();
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Call
                Some(9) => {
                    match tail.get() {
                        Shape::Cell(ref axis, ref c) => {
                            // Construct core.
                            subject = try!(nock_on(subject.clone(),
                                                   (*c).clone()));
                            // Fetch from core using axis.
                            formula = try!(get_axis(axis, &subject));
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Hint
                Some(10) => {
                    match tail.get() {
                        Shape::Cell(ref hint, ref c) => {
                            let (id, clue) = match hint.get() {
                                Shape::Cell(ref p, ref q) => {
                                    (p.clone(), try!(nock_on(subject.clone(), (*q).clone())))
                                }
                                Shape::Atom(_) => {
                                    (hint.clone(), Noun::from(0u32))
                                }
                            };

                            // TODO: Handle other hint types than %fast.
                            if String::from_noun(id).unwrap() == "fast" {
                                let core = try!(nock_on(subject.clone(), (*c).clone()));
                                let stuff = parse_fast_clue(&clue);

                                // TODO: Register core with info

                                return Ok(core);
                            }

                            formula = (*c).clone();
                            continue;
                        }
                        _ => return Err(NockError),
                    }
                }

                // Unhandled opcode
                Some(_) => {
                    return Err(NockError);
                }

                None => {
                    if let Shape::Cell(_, _) = ops.get() {
                        // Autocons
                        let a = try!(nock_on(subject.clone(), ops.clone()));
                        let b = try!(nock_on(subject, tail.clone()));
                        return Ok(Noun::cell(a, b));
                    } else {
                        return Err(NockError);
                    }
                }
            }
        } else {
            return Err(NockError);
        }
    }
}

fn get_axis(axis: &Noun, subject: &Noun) -> NockResult {
    // TODO: Optimize for small atoms.
    fn fas(x: BigUint, n: &Noun) -> NockResult {
        let two = BigUint::from_u32(2).unwrap();
        let three = BigUint::from_u32(3).unwrap();
        if x == BigUint::zero() {
            return Err(NockError);
        }
        if x == BigUint::one() {
            return Ok(n.clone());
        }
        if let Shape::Cell(ref a, ref b) = n.get() {
            if x == two {
                return Ok((*a).clone());
            } else if x == three {
                return Ok((*b).clone());
            } else {
                let half = x.clone() >> 1;
                let p = try!(fas(half, n));

                if x % two.clone() == BigUint::zero() {
                    fas(two, &p)
                } else {
                    fas(three, &p)
                }
            }
        } else {
            return Err(NockError);
        }
    }

    match axis.get() {
        Shape::Atom(ref x) => fas(BigUint::from_digits(x).unwrap(), subject),
        _ => Err(NockError)
    }
}

fn parse_fast_clue(clue: &Noun) -> Result<(String, u32, Vec<(String, Noun)>), NockError> {
    if let Some((ref name, ref axis_formula, ref hooks)) = clue.get_122() {
        let chum = try!(String::from_noun(name).map_err(|_| NockError));

        let axis = if let Shape::Cell(ref a, ref b) = axis_formula.get() {
            if let (Some(1), Some(0)) = (a.as_u32(), b.as_u32()) {
                0
            } else if let (Some(0), Some(axis)) = (a.as_u32(), b.as_u32()) {
                axis
            } else {
                return Err(NockError);
            }
        } else {
            return Err(NockError);
        };

        let hooks: Vec<(String, Noun)> = try!(FromNoun::from_noun(hooks).map_err(|_| NockError));

        Ok((chum, axis, hooks))
    } else {
        Err(NockError)
    }
}
<|endoftext|>


<commit_before>use core::usize;

use rng::Rng;

// https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
#[cfg(target_pointer_width = "32")]
pub static OFFSET: usize = 1013904223;
#[cfg(target_pointer_width = "32")]
pub static MULTIPLIER: usize = 1664525;

#[cfg(target_pointer_width = "64")]
pub static OFFSET: usize = 1442695040888963407;
#[cfg(target_pointer_width = "64")]
pub static MULTIPLIER: usize = 6364136223846793005;

pub static MAX: usize = usize::MAX;
pub static MAX_F32: f32 = usize::MAX as f32;
pub static MAX_F64: f64 = usize::MAX as f64;

pub struct Prng {
    seed: usize,
}

impl Prng {

    pub fn new() -> Self {
        Prng {
            // get a value for initial seed
            seed: &0usize as *const usize as usize,
        }
    }

    pub fn seed(&self) -> usize {
        self.seed
    }

    pub fn set_seed(&mut self, seed: usize) {
        self.seed = seed;
    }
}

impl Rng for Prng {
    // http://indiegamr.com/generate-repeatable-random-numbers-in-js/
    fn next(&mut self) -> usize {
        self.seed = ((MULTIPLIER.wrapping_mul(self.seed)).wrapping_add(OFFSET)) % MAX;
        self.seed
    }

    fn next_f32(&mut self) -> f32 { self.next() as f32 / MAX_F32 }

    fn next_f64(&mut self) -> f64 { self.next() as f64 / MAX_F64 }
}
<commit_msg>change value for getting initial seed<commit_after>use core::usize;

use rng::Rng;

// https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
#[cfg(target_pointer_width = "32")]
pub static OFFSET: usize = 1013904223;
#[cfg(target_pointer_width = "32")]
pub static MULTIPLIER: usize = 1664525;

#[cfg(target_pointer_width = "64")]
pub static OFFSET: usize = 1442695040888963407;
#[cfg(target_pointer_width = "64")]
pub static MULTIPLIER: usize = 6364136223846793005;

pub static MAX: usize = usize::MAX;
pub static MAX_F32: f32 = usize::MAX as f32;
pub static MAX_F64: f64 = usize::MAX as f64;

pub struct Prng {
    seed: usize,
}

impl Prng {

    pub fn new() -> Self {
        Prng {
            // get a value for initial seed
            seed: &false as *const _ as usize,
        }
    }

    pub fn seed(&self) -> usize {
        self.seed
    }

    pub fn set_seed(&mut self, seed: usize) {
        self.seed = seed;
    }
}

impl Rng for Prng {
    // http://indiegamr.com/generate-repeatable-random-numbers-in-js/
    fn next(&mut self) -> usize {
        self.seed = ((MULTIPLIER.wrapping_mul(self.seed)).wrapping_add(OFFSET)) % MAX;
        self.seed
    }

    fn next_f32(&mut self) -> f32 { self.next() as f32 / MAX_F32 }

    fn next_f64(&mut self) -> f64 { self.next() as f64 / MAX_F64 }
}
<|endoftext|>


<commit_before>use std::iter::{FromIterator, Extend};

use gset::GSet;

#[derive(Clone, Hash, Debug)]
pub struct PSet<T> {
    add_set:    GSet<T>,
    remove_set: GSet<T>,
}

impl<T: Ord + Clone> PSet<T> {
    pub fn new() -> PSet<T> {
        PSet {
            add_set:    GSet::new(),
            remove_set: GSet::new()
        }
    }

    pub fn insert(&mut self, value: T) -> bool {
        self.add_set.insert(value)
    }

    pub fn remove(&mut self, value: T) -> bool {
        if self.add_set.contains(&value) {
            self.remove_set.insert(value);
        }
        return false
    }

    pub fn contains(&mut self, value: &T) -> bool {
        self.add_set.contains(&value) & !self.remove_set.contains(&value)
    }
}

impl<T: Ord + Clone> FromIterator<T> for PSet<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> PSet<T> {
        let mut set = PSet::new();
        set.extend(iter);
        set
    }
}

impl<T: Ord + Clone> Extend<T> for PSet<T> {
    #[inline]
    fn extend<Iter: IntoIterator<Item = T>>(&mut self, iter: Iter) {
        for elem in iter {
            self.insert(elem);
        }
    }
}
<commit_msg>Implement union for PSet<commit_after>use std::iter::{FromIterator, Extend};

use gset::GSet;

#[derive(Clone, Hash, Debug)]
pub struct PSet<T> {
    add_set:    GSet<T>,
    remove_set: GSet<T>,
}

impl<T: Ord + Clone> PSet<T> {
    pub fn new() -> PSet<T> {
        PSet {
            add_set:    GSet::new(),
            remove_set: GSet::new()
        }
    }

    pub fn insert(&mut self, value: T) -> bool {
        self.add_set.insert(value)
    }

    pub fn remove(&mut self, value: T) -> bool {
        if self.add_set.contains(&value) {
            self.remove_set.insert(value);
        }
        return false
    }

    pub fn contains(&mut self, value: &T) -> bool {
        self.add_set.contains(&value) & !self.remove_set.contains(&value)
    }

    pub fn union(&mut self, other: &PSet<T>) -> PSet<T> {
        PSet {
            add_set:    self.add_set.union(&other.add_set),
            remove_set: self.remove_set.union(&other.remove_set)
        }
    }
}

impl<T: Ord + Clone> FromIterator<T> for PSet<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> PSet<T> {
        let mut set = PSet::new();
        set.extend(iter);
        set
    }
}

impl<T: Ord + Clone> Extend<T> for PSet<T> {
    #[inline]
    fn extend<Iter: IntoIterator<Item = T>>(&mut self, iter: Iter) {
        for elem in iter {
            self.insert(elem);
        }
    }
}
<|endoftext|>