<commit_before>//! Conversion between units

use std::ops::Add;
use std::ops::Sub;
use std::ops::Mul;
use std::ops::Div;
use std::cmp::PartialEq;

use ::si_units::*;

macro_rules! convert_unit {
    // Convert from $i1 to $i2 using closure $e1
    // Convert from $i2 to $i1 using closure $e2
    ($i1:ident, $i2:ident, $e1:expr, $e2:expr) => {
        impl From<$i1> for $i2 {
            fn from($i1(value): $i1) -> Self {
                $i2($e1(value))
            }
        }

        impl From<$i2> for $i1 {
            fn from($i2(value): $i2) -> Self {
                $i1($e2(value))
            }
        }
    };

    // Convert from $i1 to $i2 using factor $i3
    // $i2 = $i1 * $i3
    ($i1:ident, $i2:ident, $i3:ident) => {
        impl From<$i1> for $i2 {
            fn from($i1(value): $i1) -> Self {
                $i2(value * $i3)
            }
        }

        impl From<$i2> for $i1 {
            fn from($i2(value): $i2) -> Self {
                $i1(value / $i3)
            }
        }
    };

}

// TODO: move all constants to https://github.com/willi-kappler/natural_constants

const METER_TO_FOOT: f64 = 3.28084;
const METER_TO_YARD: f64 = 1.09361;
const METER_TO_MILE: f64 = 0.000621371;

const METER_TO_KILOMETER: f64 = 1.0e-3;
const METER_TO_MILIMETER: f64 = 1.0e+3;
const METER_TO_MICROMETER: f64 = 1.0e+6;
const METER_TO_NANOMETER: f64 = 1.0e+9;
const METER_TO_ASTRONOMICALUNIT: f64 = 1.0 / 149597870700.0;
const METER_TO_LIGHTYEAR: f64 =  1.0 / 9.4607e15;
const METER_TO_PARSEC: f64 = 1.0 / 3.08567758149137e16;

const SECOND_TO_MINUTE: f64 = 60.0;
const SECOND_TO_HOUR: f64 = 60.0 * 60.0;
const SECOND_TO_DAY: f64 = 60.0 * 60.0 * 24.0;
const SECOND_TO_WEEK: f64 = SECOND_TO_DAY * 7.0;
const SECOND_TO_MONTH: f64 = SECOND_TO_DAY * 30.0; // simple
const SECOND_TO_MONTH_AVG: f64 = SECOND_TO_DAY * 30.42; // average
const SECOND_TO_MONTH_AVG_LEAP: f64 = SECOND_TO_DAY * 30.50; // average leap year
const SECOND_TO_MONTH_SOLAR: f64 = SECOND_TO_DAY * 30.44; // solar calendar
const SECOND_TO_MONTH_LUNAR: f64 = SECOND_TO_DAY * 29.53; // lunar month

const SECOND_TO_YEAR: f64 = SECOND_TO_DAY * 365.25; // on average
const SECOND_TO_MIL_YEAR: f64 = SECOND_TO_YEAR * 1.0e6;

const DEGC_TO_KELVIN: f64 = 273.15;

init_unit!(Foot);
init_unit!(Yard);
init_unit!(Mile);

convert_unit!(Meter, Foot, METER_TO_FOOT);
convert_unit!(Meter, Yard, METER_TO_YARD);
convert_unit!(Meter, Mile, METER_TO_MILE);

init_unit!(Kilometer);
init_unit!(Milimeter);
init_unit!(Micrometer);
init_unit!(Nanometer);

convert_unit!(Meter, Kilometer, METER_TO_KILOMETER);
convert_unit!(Meter, Milimeter, METER_TO_MILIMETER);
convert_unit!(Meter, Micrometer, METER_TO_MICROMETER);
convert_unit!(Meter, Nanometer, METER_TO_NANOMETER);

init_unit!(AstronomicalUnit);
init_unit!(LightYear);
init_unit!(Parsec);

convert_unit!(Meter, AstronomicalUnit, METER_TO_ASTRONOMICALUNIT);
convert_unit!(Meter, LightYear, METER_TO_LIGHTYEAR);
convert_unit!(Meter, Parsec, METER_TO_PARSEC);

init_unit!(Minute);
init_unit!(Hour);
init_unit!(Day);
init_unit!(Week);
init_unit!(Month);
init_unit!(Year);
init_unit!(MilYear);

convert_unit!(Second, Minute, SECOND_TO_MINUTE);
convert_unit!(Second, Hour, SECOND_TO_HOUR);
convert_unit!(Second, Day, SECOND_TO_DAY);
convert_unit!(Second, Week, SECOND_TO_WEEK);
convert_unit!(Second, Month, SECOND_TO_MONTH);
convert_unit!(Second, Year, SECOND_TO_YEAR);
convert_unit!(Second, MilYear, SECOND_TO_MIL_YEAR);

init_unit!(Fahrenheit);

convert_unit!(DegC, Kelvin, |value| {value + DEGC_TO_KELVIN}, |value| {value - DEGC_TO_KELVIN});
convert_unit!(DegC, Fahrenheit, |value| {(value * 1.8) + 32.0}, |value| {(value - 32.0) / 1.8});
<commit_msg>add documentation comments for macro<commit_after>//! Conversion between units

use std::ops::Add;
use std::ops::Sub;
use std::ops::Mul;
use std::ops::Div;
use std::cmp::PartialEq;

use ::si_units::*;

/// This macro implements *From* for the given units.
/// It either uses a factor or a closure to calculate the conversion
///
/// #Example:
///
/// ```
/// convert_unit!(Second, Minute, 60.0);
/// ```
#[macro_export] macro_rules! convert_unit {
    // Convert from $i1 to $i2 using closure $e1
    // Convert from $i2 to $i1 using closure $e2
    ($i1:ident, $i2:ident, $e1:expr, $e2:expr) => {
        impl From<$i1> for $i2 {
            fn from($i1(value): $i1) -> Self {
                $i2($e1(value))
            }
        }

        impl From<$i2> for $i1 {
            fn from($i2(value): $i2) -> Self {
                $i1($e2(value))
            }
        }
    };

    // Convert from $i1 to $i2 using factor $i3
    // $i2 = $i1 * $i3
    ($i1:ident, $i2:ident, $i3:ident) => {
        impl From<$i1> for $i2 {
            fn from($i1(value): $i1) -> Self {
                $i2(value * $i3)
            }
        }

        impl From<$i2> for $i1 {
            fn from($i2(value): $i2) -> Self {
                $i1(value / $i3)
            }
        }
    };

}

// TODO: move all constants to https://github.com/willi-kappler/natural_constants

const METER_TO_FOOT: f64 = 3.28084;
const METER_TO_YARD: f64 = 1.09361;
const METER_TO_MILE: f64 = 0.000621371;

const METER_TO_KILOMETER: f64 = 1.0e-3;
const METER_TO_MILIMETER: f64 = 1.0e+3;
const METER_TO_MICROMETER: f64 = 1.0e+6;
const METER_TO_NANOMETER: f64 = 1.0e+9;
const METER_TO_ASTRONOMICALUNIT: f64 = 1.0 / 149597870700.0;
const METER_TO_LIGHTYEAR: f64 =  1.0 / 9.4607e15;
const METER_TO_PARSEC: f64 = 1.0 / 3.08567758149137e16;

const SECOND_TO_MINUTE: f64 = 60.0;
const SECOND_TO_HOUR: f64 = 60.0 * 60.0;
const SECOND_TO_DAY: f64 = 60.0 * 60.0 * 24.0;
const SECOND_TO_WEEK: f64 = SECOND_TO_DAY * 7.0;
const SECOND_TO_MONTH: f64 = SECOND_TO_DAY * 30.0; // simple
const SECOND_TO_MONTH_AVG: f64 = SECOND_TO_DAY * 30.42; // average
const SECOND_TO_MONTH_AVG_LEAP: f64 = SECOND_TO_DAY * 30.50; // average leap year
const SECOND_TO_MONTH_SOLAR: f64 = SECOND_TO_DAY * 30.44; // solar calendar
const SECOND_TO_MONTH_LUNAR: f64 = SECOND_TO_DAY * 29.53; // lunar month

const SECOND_TO_YEAR: f64 = SECOND_TO_DAY * 365.25; // on average
const SECOND_TO_MIL_YEAR: f64 = SECOND_TO_YEAR * 1.0e6;

const DEGC_TO_KELVIN: f64 = 273.15;

init_unit!(Foot);
init_unit!(Yard);
init_unit!(Mile);

convert_unit!(Meter, Foot, METER_TO_FOOT);
convert_unit!(Meter, Yard, METER_TO_YARD);
convert_unit!(Meter, Mile, METER_TO_MILE);

init_unit!(Kilometer);
init_unit!(Milimeter);
init_unit!(Micrometer);
init_unit!(Nanometer);

convert_unit!(Meter, Kilometer, METER_TO_KILOMETER);
convert_unit!(Meter, Milimeter, METER_TO_MILIMETER);
convert_unit!(Meter, Micrometer, METER_TO_MICROMETER);
convert_unit!(Meter, Nanometer, METER_TO_NANOMETER);

init_unit!(AstronomicalUnit);
init_unit!(LightYear);
init_unit!(Parsec);

convert_unit!(Meter, AstronomicalUnit, METER_TO_ASTRONOMICALUNIT);
convert_unit!(Meter, LightYear, METER_TO_LIGHTYEAR);
convert_unit!(Meter, Parsec, METER_TO_PARSEC);

init_unit!(Minute);
init_unit!(Hour);
init_unit!(Day);
init_unit!(Week);
init_unit!(Month);
init_unit!(Year);
init_unit!(MilYear);

convert_unit!(Second, Minute, SECOND_TO_MINUTE);
convert_unit!(Second, Hour, SECOND_TO_HOUR);
convert_unit!(Second, Day, SECOND_TO_DAY);
convert_unit!(Second, Week, SECOND_TO_WEEK);
convert_unit!(Second, Month, SECOND_TO_MONTH);
convert_unit!(Second, Year, SECOND_TO_YEAR);
convert_unit!(Second, MilYear, SECOND_TO_MIL_YEAR);

init_unit!(Fahrenheit);

convert_unit!(DegC, Kelvin, |value| {value + DEGC_TO_KELVIN}, |value| {value - DEGC_TO_KELVIN});
convert_unit!(DegC, Fahrenheit, |value| {(value * 1.8) + 32.0}, |value| {(value - 32.0) / 1.8});
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::collections::HashSet;

struct Santa {
    x: i32,
    y: i32
}

fn step(c: char, s: &mut Santa) {
    match c {
        '>' => s.x += 1,
        '<' => s.x -= 1,
        '^' => s.y += 1,
        'v' => s.y -= 1,
        _ => panic!()
    }
}

pub fn main() {
    let mut p1_santa = Santa {x: 0, y: 0};
    let mut p2_santa = Santa {x: 0, y: 0};
    let mut p2_robo  = Santa {x: 0, y: 0};

    let mut part1_visited = HashSet::new();
    let mut part2_visited = HashSet::new();

    part1_visited.insert((0,0));
    part2_visited.insert((0,0));

    let mut roboturn: bool = false;

    let stdin = io::stdin();
    for line in stdin.lock().lines() {
      let line = line.unwrap();
      for c in line.chars() {
            step(c, &mut p1_santa);
            if roboturn {
                step(c, &mut p2_robo);
            } else {
                step(c, &mut p2_santa);
            }
            roboturn = !roboturn;
            part1_visited.insert((p1_santa.x, p1_santa.y));
            part2_visited.insert((p2_santa.x, p2_santa.y));
            part2_visited.insert((p2_robo.x, p2_robo.y));
      }
    }
    println!("Part 1: {}", part1_visited.len());
    println!("Part 2: {}", part2_visited.len());
}<commit_msg>simplify day 03 rust code<commit_after>use std::io;
use std::io::prelude::*;
use std::collections::HashSet;
use std::mem;

struct Santa {
    x: i32,
    y: i32
}

struct Day03 {
    part1: Santa,
    part2: Santa,
    temp:  Santa
}

impl Santa {
    fn step(&mut self, c: char) {
        match c {
            '>' => self.x += 1,
            '<' => self.x -= 1,
            '^' => self.y += 1,
            'v' => self.y -= 1,
            _ => panic!()
        }
    }
}

pub fn main() {
    let mut day03 = Day03 {
        part1: Santa {x: 0, y: 0},
        part2: Santa {x: 0, y: 0},
        temp:  Santa {x: 0, y: 0}
    };

    let mut part1_visited = HashSet::new();
    let mut part2_visited = HashSet::new();

    part1_visited.insert((0,0));
    part2_visited.insert((0,0));

    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        let line = line.unwrap();
        for c in line.chars() {
              day03.part1.step(c);
              day03.part2.step(c);
              part1_visited.insert((day03.part1.x, day03.part1.y));
              part2_visited.insert((day03.part2.x, day03.part2.y));
              mem::swap(&mut day03.part2, &mut day03.temp)
        }
    }
    println!("Part 1: {}", part1_visited.len());
    println!("Part 2: {}", part2_visited.len());
}<|endoftext|>


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

#[macro_use]
extern crate log;
extern crate chrono;
extern crate fern;

extern crate dirs;

extern crate xi_core_lib;
extern crate xi_rpc;

use std::collections::HashMap;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

use xi_core_lib::XiCore;
use xi_rpc::RpcLoop;

const XI_LOG_DIR: &str = "xi-core";
const XI_LOG_FILE: &str = "xi-core.log";

fn get_logging_directory_path<P: AsRef<Path>>(directory: P) -> Result<PathBuf, io::Error> {
    match dirs::data_local_dir() {
        Some(mut log_dir) => {
            log_dir.push(directory);
            Ok(log_dir)
        }
        None => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "No standard logging directory known for this platform",
        )),
    }
}

fn setup_logging(logging_path_result: Option<PathBuf>) -> Result<(), fern::InitError> {
    let level_filter = match std::env::var("XI_LOG") {
        Ok(level) => match level.to_lowercase().as_ref() {
            "trace" => log::LevelFilter::Trace,
            "debug" => log::LevelFilter::Debug,
            _ => log::LevelFilter::Info,
        },
        // Default to info
        Err(_) => log::LevelFilter::Info,
    };

    let mut fern_dispatch = fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{}[{}][{}] {}",
                chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
                record.target(),
                record.level(),
                message,
            ))
        }).level(level_filter)
        .chain(io::stderr());

    if let Some(logging_file_path) = &logging_path_result {
        // Ensure the logging directory is created
        let parent_path = logging_file_path.parent().ok_or(io::Error::new(
            io::ErrorKind::InvalidInput,
            format!(
                "Unable to get the parent of the following Path: {}",
                logging_file_path.display(),
            ),
        ))?;
        fs::create_dir_all(parent_path)?;
        // Attach it to fern
        fern_dispatch = fern_dispatch.chain(fern::log_file(logging_file_path)?);
    };

    // Start fern
    fern_dispatch.apply()?;
    info!("Logging with fern is setup");

    // Log details of the logging_file_path result using fern/log
    // Either logging the path fern is outputting to or the error from obtaining the path
    match &logging_path_result {
        Some(logging_file_path) => info!(
            "Logging to the following file: {}",
            logging_file_path.display()
        ),
        None => warn!("There was no path supplied for the log file, falling back to stderr."),
    }
    Ok(())
}

fn generate_logging_path(logfile_config: LogfileConfig) -> Result<PathBuf, io::Error> {
    // Use the file name set in logfile_config or fallback to the default
    let logfile_file_name = match logfile_config.file {
        Some(file_name) => file_name,
        None => PathBuf::from(XI_LOG_FILE),
    };
    // Use the directory name set in logfile_config or fallback to the default
    let logfile_directory_name = match logfile_config.directory {
        Some(dir) => dir,
        None => PathBuf::from(XI_LOG_DIR),
    };

    let mut logging_directory_path = get_logging_directory_path(logfile_directory_name)?;

    // Add the file name & return the full path
    logging_directory_path.push(logfile_file_name);
    Ok(logging_directory_path)
}

fn get_flags() -> HashMap<String, Option<String>> {
    let mut flags: HashMap<String, Option<String>> = HashMap::new();

    let flag_prefix = "-";
    let mut args_itterator = std::env::args().peekable();
    while let Some(arg) = args_itterator.next() {
        if arg.starts_with(flag_prefix) {
            let key = arg.trim_left_matches(flag_prefix).to_string();

            // Check the next argument doesn't start with the flag prefix
            // map_or accounts for peek returning an Option
            let next_arg_not_a_flag: bool = args_itterator
                .peek()
                .map_or(false, |val| !val.starts_with(flag_prefix));
            if next_arg_not_a_flag {
                flags.insert(key, args_itterator.next());
            }
        }
    }
    flags
}

struct EnvFlagConfig {
    env_name: &'static str,
    flag_name: &'static str,
}

/// Extracts a value from the flags and the env.
///
/// In this order: `String` from the flags, then `String` from the env, then `None`
fn extract_env_or_flag(
    flags: &HashMap<String, Option<String>>,
    conf: EnvFlagConfig,
) -> Option<String> {
    flags
        .get(conf.flag_name)
        .cloned()
        .unwrap_or(std::env::var(conf.env_name).ok())
}

struct LogfileConfig {
    directory: Option<PathBuf>,
    file: Option<PathBuf>,
}

fn generate_logfile_config(flags: &HashMap<String, Option<String>>) -> LogfileConfig {
    // If the key is set, get the Option within
    let log_dir_env_flag = EnvFlagConfig {
        env_name: "XI_LOG_DIR",
        flag_name: "log-dir",
    };
    let log_file_env_flag = EnvFlagConfig {
        env_name: "XI_LOG_FILE",
        flag_name: "log-file",
    };
    let log_dir_flag_option = extract_env_or_flag(&flags, log_dir_env_flag).map(PathBuf::from);

    let log_file_flag_option = extract_env_or_flag(&flags, log_file_env_flag).map(PathBuf::from);

    LogfileConfig {
        directory: log_dir_flag_option,
        file: log_file_flag_option,
    }
}

fn main() {
    let mut state = XiCore::new();
    let stdin = io::stdin();
    let stdout = io::stdout();
    let mut rpc_looper = RpcLoop::new(stdout);

    let flags: HashMap<String, Option<String>> = get_flags();

    let logfile_config = generate_logfile_config(&flags);

    let logging_path_result: Result<PathBuf, io::Error> = generate_logging_path(logfile_config);

    let mut logging_path: Option<PathBuf> = None;
    let mut logging_error: Option<io::Error> = None;
    match logging_path_result {
        Ok(val) => logging_path = Some(val),
        Err(e) => logging_error = Some(e),
    };
    if let Err(e) = setup_logging(logging_path) {
        eprintln!(
            "[ERROR] setup_logging returned error, logging disabled: {:?}",
            e
        );
    }
    if let Some(e) = logging_error {
        warn!("Unable to successfully generate the logging path: {:?}", e)
    }

    match rpc_looper.mainloop(|| stdin.lock(), &mut state) {
        Ok(_) => (),
        Err(err) => error!("xi-core exited with error:\n{:?}", err),
    }
}
<commit_msg>Made the wording of the logging setup failure message clearer<commit_after>// Copyright 2016 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[macro_use]
extern crate log;
extern crate chrono;
extern crate fern;

extern crate dirs;

extern crate xi_core_lib;
extern crate xi_rpc;

use std::collections::HashMap;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

use xi_core_lib::XiCore;
use xi_rpc::RpcLoop;

const XI_LOG_DIR: &str = "xi-core";
const XI_LOG_FILE: &str = "xi-core.log";

fn get_logging_directory_path<P: AsRef<Path>>(directory: P) -> Result<PathBuf, io::Error> {
    match dirs::data_local_dir() {
        Some(mut log_dir) => {
            log_dir.push(directory);
            Ok(log_dir)
        }
        None => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "No standard logging directory known for this platform",
        )),
    }
}

fn setup_logging(logging_path_result: Option<PathBuf>) -> Result<(), fern::InitError> {
    let level_filter = match std::env::var("XI_LOG") {
        Ok(level) => match level.to_lowercase().as_ref() {
            "trace" => log::LevelFilter::Trace,
            "debug" => log::LevelFilter::Debug,
            _ => log::LevelFilter::Info,
        },
        // Default to info
        Err(_) => log::LevelFilter::Info,
    };

    let mut fern_dispatch = fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{}[{}][{}] {}",
                chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
                record.target(),
                record.level(),
                message,
            ))
        }).level(level_filter)
        .chain(io::stderr());

    if let Some(logging_file_path) = &logging_path_result {
        // Ensure the logging directory is created
        let parent_path = logging_file_path.parent().ok_or(io::Error::new(
            io::ErrorKind::InvalidInput,
            format!(
                "Unable to get the parent of the following Path: {}",
                logging_file_path.display(),
            ),
        ))?;
        fs::create_dir_all(parent_path)?;
        // Attach it to fern
        fern_dispatch = fern_dispatch.chain(fern::log_file(logging_file_path)?);
    };

    // Start fern
    fern_dispatch.apply()?;
    info!("Logging with fern is setup");

    // Log details of the logging_file_path result using fern/log
    // Either logging the path fern is outputting to or the error from obtaining the path
    match &logging_path_result {
        Some(logging_file_path) => info!(
            "Logging to the following file: {}",
            logging_file_path.display()
        ),
        None => warn!("There was no path supplied for the log file, falling back to stderr."),
    }
    Ok(())
}

fn generate_logging_path(logfile_config: LogfileConfig) -> Result<PathBuf, io::Error> {
    // Use the file name set in logfile_config or fallback to the default
    let logfile_file_name = match logfile_config.file {
        Some(file_name) => file_name,
        None => PathBuf::from(XI_LOG_FILE),
    };
    // Use the directory name set in logfile_config or fallback to the default
    let logfile_directory_name = match logfile_config.directory {
        Some(dir) => dir,
        None => PathBuf::from(XI_LOG_DIR),
    };

    let mut logging_directory_path = get_logging_directory_path(logfile_directory_name)?;

    // Add the file name & return the full path
    logging_directory_path.push(logfile_file_name);
    Ok(logging_directory_path)
}

fn get_flags() -> HashMap<String, Option<String>> {
    let mut flags: HashMap<String, Option<String>> = HashMap::new();

    let flag_prefix = "-";
    let mut args_itterator = std::env::args().peekable();
    while let Some(arg) = args_itterator.next() {
        if arg.starts_with(flag_prefix) {
            let key = arg.trim_left_matches(flag_prefix).to_string();

            // Check the next argument doesn't start with the flag prefix
            // map_or accounts for peek returning an Option
            let next_arg_not_a_flag: bool = args_itterator
                .peek()
                .map_or(false, |val| !val.starts_with(flag_prefix));
            if next_arg_not_a_flag {
                flags.insert(key, args_itterator.next());
            }
        }
    }
    flags
}

struct EnvFlagConfig {
    env_name: &'static str,
    flag_name: &'static str,
}

/// Extracts a value from the flags and the env.
///
/// In this order: `String` from the flags, then `String` from the env, then `None`
fn extract_env_or_flag(
    flags: &HashMap<String, Option<String>>,
    conf: EnvFlagConfig,
) -> Option<String> {
    flags
        .get(conf.flag_name)
        .cloned()
        .unwrap_or(std::env::var(conf.env_name).ok())
}

struct LogfileConfig {
    directory: Option<PathBuf>,
    file: Option<PathBuf>,
}

fn generate_logfile_config(flags: &HashMap<String, Option<String>>) -> LogfileConfig {
    // If the key is set, get the Option within
    let log_dir_env_flag = EnvFlagConfig {
        env_name: "XI_LOG_DIR",
        flag_name: "log-dir",
    };
    let log_file_env_flag = EnvFlagConfig {
        env_name: "XI_LOG_FILE",
        flag_name: "log-file",
    };
    let log_dir_flag_option = extract_env_or_flag(&flags, log_dir_env_flag).map(PathBuf::from);

    let log_file_flag_option = extract_env_or_flag(&flags, log_file_env_flag).map(PathBuf::from);

    LogfileConfig {
        directory: log_dir_flag_option,
        file: log_file_flag_option,
    }
}

fn main() {
    let mut state = XiCore::new();
    let stdin = io::stdin();
    let stdout = io::stdout();
    let mut rpc_looper = RpcLoop::new(stdout);

    let flags: HashMap<String, Option<String>> = get_flags();

    let logfile_config = generate_logfile_config(&flags);

    let logging_path_result: Result<PathBuf, io::Error> = generate_logging_path(logfile_config);

    let mut logging_path: Option<PathBuf> = None;
    let mut logging_error: Option<io::Error> = None;
    match logging_path_result {
        Ok(val) => logging_path = Some(val),
        Err(e) => logging_error = Some(e),
    };
    if let Err(e) = setup_logging(logging_path) {
        eprintln!(
            "[ERROR] setup_logging returned error, logging not enabled: {:?}",
            e
        );
    }
    if let Some(e) = logging_error {
        warn!("Unable to successfully generate the logging path: {:?}", e)
    }

    match rpc_looper.mainloop(|| stdin.lock(), &mut state) {
        Ok(_) => (),
        Err(err) => error!("xi-core exited with error:\n{:?}", err),
    }
}
<|endoftext|>


<commit_before>use ::libc;
use ::cssparser;
use ::glib::translate::*;

use std::cell::Cell;

use color::*;
use drawing_ctx;
use drawing_ctx::*;
use error::*;
use handle::RsvgHandle;
use length::*;
use node::*;
use opacity::*;
use property_bag;
use property_bag::*;
use state::RsvgState;

pub struct NodeStop {
    offset: Cell<f64>,
    rgba: Cell<u32>
}

impl NodeStop {
    fn new () -> NodeStop {
        NodeStop {
            offset: Cell::new (0.0),
            rgba: Cell::new (0)
        }
    }

    pub fn get_offset (&self) -> f64 {
        self.offset.get ()
    }

    pub fn get_rgba (&self) -> u32 {
        self.rgba.get ()
    }
}

impl NodeTrait for NodeStop {
    fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) -> NodeResult {
        let offset_length: RsvgLength = property_bag::parse_or_default (pbag, "offset", LengthDir::Both, None)?;
        match offset_length.unit {
            LengthUnit::Default |
            LengthUnit::Percent => {
                let mut offset = offset_length.length;

                if offset < 0.0 {
                    offset = 0.0;
                } else if offset > 1.0 {
                    offset = 1.0;
                }

                self.offset.set (offset);
            },

            _ => {
                return Err (NodeError::value_error ("offset", "stop offset must be in default or percent units"));
            }
        }

        let state = node.get_state ();

        // FIXME: this is the only place where rsvg_parse_style() and
        // rsvg_parse_style_pairs() are called outside of the
        // rsvg-base.c machinery.  That one indirectly calls them via
        // rsvg_parse_style_attrs().
        //
        // Should we resolve the stop-color / stop-opacity at
        // rendering time?

        if let Some (v) = property_bag::lookup (pbag, "style") {
            unsafe {
                rsvg_parse_style (handle, state, v.to_glib_none ().0);
            }
        }

        unsafe {
            rsvg_parse_style_pairs (state, pbag);
        }

        let inherited_state = drawing_ctx::state_new ();
        let boxed_node = box_node (node.clone ());
        drawing_ctx::state_reconstruct (inherited_state, boxed_node);
        rsvg_node_unref (boxed_node);

        let mut color_rgba: cssparser::RGBA;

        let stop_color = drawing_ctx::state_get_stop_color (state)
            .map_err (|e| NodeError::attribute_error ("stop-color", e))?;

        match stop_color {
            None => color_rgba = cssparser::RGBA::transparent (),

            Some (Color::Inherit) => {
                let inherited_stop_color = drawing_ctx::state_get_stop_color (inherited_state)
                    .map_err (|e| NodeError::attribute_error ("stop-color", e))?;

                match inherited_stop_color {
                    None => unreachable! (),

                    Some (Color::Inherit) => color_rgba = cssparser::RGBA::transparent (),
                    Some (Color::CurrentColor) => {
                        let color = drawing_ctx::state_get_current_color (inherited_state);
                        match color {
                            Color::RGBA (rgba) => color_rgba = rgba,
                            _ => unreachable! ()
                        }
                    },

                    Some (Color::RGBA (rgba)) => color_rgba = rgba
                }
            },

            Some (Color::CurrentColor) => {
                let color = drawing_ctx::state_get_current_color (inherited_state);
                match color {
                    Color::RGBA (rgba) => color_rgba = rgba,
                    _ => unreachable! ()
                }
            }

            Some (Color::RGBA (rgba)) => color_rgba = rgba
        }

        let stop_opacity = drawing_ctx::state_get_stop_opacity (state)
            .map_err (|e| NodeError::attribute_error ("stop-opacity", e))?;

        match stop_opacity {
            None => color_rgba.alpha = 0xff,

            Some (Opacity::Inherit) => {
                let inherited_opacity = drawing_ctx::state_get_stop_opacity (inherited_state)
                    .map_err (|e| NodeError::attribute_error ("stop-opacity", e))?;

                match inherited_opacity {
                    Some (Opacity::Specified (opacity)) => color_rgba.alpha = opacity_to_u8 (opacity),
                    _ => color_rgba.alpha = 0xff
                }
            },

            Some (Opacity::Specified (opacity)) => color_rgba.alpha = opacity_to_u8 (opacity)
        }

        self.rgba.set (u32_from_rgba (color_rgba));

        drawing_ctx::state_free (inherited_state);

        Ok (())
    }

    fn draw (&self, _: &RsvgNode, _: *const RsvgDrawingCtx, _: i32) {
        // nothing; paint servers are handled specially
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

fn u32_from_rgba (rgba: cssparser::RGBA) -> u32 {
    ((rgba.red as u32) << 24) |
    ((rgba.green as u32) << 16) |
    ((rgba.blue as u32) << 8) |
    (rgba.alpha as u32)
}

extern "C" {
    fn rsvg_parse_style_pairs (state: *mut RsvgState, pbag: *const RsvgPropertyBag);
    fn rsvg_parse_style (handle: *const RsvgHandle, state: *mut RsvgState, string: *const libc::c_char);
}

#[no_mangle]
pub extern fn rsvg_node_stop_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Stop,
                    raw_parent,
                    Box::new (NodeStop::new ()))
}
<commit_msg>NodeStop: validate "offset" using a validation function<commit_after>use ::libc;
use ::cssparser;
use ::glib::translate::*;

use std::cell::Cell;

use color::*;
use drawing_ctx;
use drawing_ctx::*;
use error::*;
use handle::RsvgHandle;
use length::*;
use node::*;
use opacity::*;
use parsers::Parse;
use property_bag;
use property_bag::*;
use state::RsvgState;

pub struct NodeStop {
    offset: Cell<f64>,
    rgba: Cell<u32>
}

impl NodeStop {
    fn new () -> NodeStop {
        NodeStop {
            offset: Cell::new (0.0),
            rgba: Cell::new (0)
        }
    }

    pub fn get_offset (&self) -> f64 {
        self.offset.get ()
    }

    pub fn get_rgba (&self) -> u32 {
        self.rgba.get ()
    }
}

fn validate_offset(length: RsvgLength) -> Result<RsvgLength, AttributeError> {
    match length.unit {
        LengthUnit::Default |
        LengthUnit::Percent => {
            let mut offset = length.length;

            if offset < 0.0 {
                offset = 0.0;
            } else if offset > 1.0 {
                offset = 1.0;
            }

            Ok(RsvgLength::new(offset, LengthUnit::Default, LengthDir::Both))
        },

        _ => {
            Err (AttributeError::Value ("stop offset must be in default or percent units".to_string()))
        }
    }
}

impl NodeTrait for NodeStop {
    fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) -> NodeResult {
        let length = property_bag::parse_or_default (pbag, "offset", LengthDir::Both,
                                                     Some(validate_offset))?;
        assert! (length.unit == LengthUnit::Default || length.unit == LengthUnit::Percent);
        self.offset.set (length.length);

        let state = node.get_state ();

        // FIXME: this is the only place where rsvg_parse_style() and
        // rsvg_parse_style_pairs() are called outside of the
        // rsvg-base.c machinery.  That one indirectly calls them via
        // rsvg_parse_style_attrs().
        //
        // Should we resolve the stop-color / stop-opacity at
        // rendering time?

        if let Some (v) = property_bag::lookup (pbag, "style") {
            unsafe {
                rsvg_parse_style (handle, state, v.to_glib_none ().0);
            }
        }

        unsafe {
            rsvg_parse_style_pairs (state, pbag);
        }

        let inherited_state = drawing_ctx::state_new ();
        let boxed_node = box_node (node.clone ());
        drawing_ctx::state_reconstruct (inherited_state, boxed_node);
        rsvg_node_unref (boxed_node);

        let mut color_rgba: cssparser::RGBA;

        let stop_color = drawing_ctx::state_get_stop_color (state)
            .map_err (|e| NodeError::attribute_error ("stop-color", e))?;

        match stop_color {
            None => color_rgba = cssparser::RGBA::transparent (),

            Some (Color::Inherit) => {
                let inherited_stop_color = drawing_ctx::state_get_stop_color (inherited_state)
                    .map_err (|e| NodeError::attribute_error ("stop-color", e))?;

                match inherited_stop_color {
                    None => unreachable! (),

                    Some (Color::Inherit) => color_rgba = cssparser::RGBA::transparent (),
                    Some (Color::CurrentColor) => {
                        let color = drawing_ctx::state_get_current_color (inherited_state);
                        match color {
                            Color::RGBA (rgba) => color_rgba = rgba,
                            _ => unreachable! ()
                        }
                    },

                    Some (Color::RGBA (rgba)) => color_rgba = rgba
                }
            },

            Some (Color::CurrentColor) => {
                let color = drawing_ctx::state_get_current_color (inherited_state);
                match color {
                    Color::RGBA (rgba) => color_rgba = rgba,
                    _ => unreachable! ()
                }
            }

            Some (Color::RGBA (rgba)) => color_rgba = rgba
        }

        let stop_opacity = drawing_ctx::state_get_stop_opacity (state)
            .map_err (|e| NodeError::attribute_error ("stop-opacity", e))?;

        match stop_opacity {
            None => color_rgba.alpha = 0xff,

            Some (Opacity::Inherit) => {
                let inherited_opacity = drawing_ctx::state_get_stop_opacity (inherited_state)
                    .map_err (|e| NodeError::attribute_error ("stop-opacity", e))?;

                match inherited_opacity {
                    Some (Opacity::Specified (opacity)) => color_rgba.alpha = opacity_to_u8 (opacity),
                    _ => color_rgba.alpha = 0xff
                }
            },

            Some (Opacity::Specified (opacity)) => color_rgba.alpha = opacity_to_u8 (opacity)
        }

        self.rgba.set (u32_from_rgba (color_rgba));

        drawing_ctx::state_free (inherited_state);

        Ok (())
    }

    fn draw (&self, _: &RsvgNode, _: *const RsvgDrawingCtx, _: i32) {
        // nothing; paint servers are handled specially
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        unreachable! ();
    }
}

fn u32_from_rgba (rgba: cssparser::RGBA) -> u32 {
    ((rgba.red as u32) << 24) |
    ((rgba.green as u32) << 16) |
    ((rgba.blue as u32) << 8) |
    (rgba.alpha as u32)
}

extern "C" {
    fn rsvg_parse_style_pairs (state: *mut RsvgState, pbag: *const RsvgPropertyBag);
    fn rsvg_parse_style (handle: *const RsvgHandle, state: *mut RsvgState, string: *const libc::c_char);
}

#[no_mangle]
pub extern fn rsvg_node_stop_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const RsvgNode {
    boxed_node_new (NodeType::Stop,
                    raw_parent,
                    Box::new (NodeStop::new ()))
}
<|endoftext|>


<commit_before>use sodiumoxide::crypto::stream;
use blob::{ChunkRef, Kind};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

pub struct PlainText(Vec<u8>);
pub struct CipherText(Vec<u8>);
pub struct CipherTextRef<'a>(&'a [u8]);

impl PlainText {
    pub fn new(bytes: Vec<u8>) -> PlainText {
        PlainText(bytes)
    }
    pub fn from_vec(mut bytes: &mut Vec<u8>) -> PlainText {
        let mut pt = PlainText(Vec::with_capacity(bytes.len()));
        pt.0.append(&mut bytes);
        pt
    }
    pub fn append(&mut self, mut other: PlainText) {
        self.0.append(&mut other.0);
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn into_vec(self) -> Vec<u8> {
        self.0
    }
    pub fn as_ref(&self) -> &[u8] {
        &self.0[..]
    }
}

impl CipherText {
    pub fn new(ct: Vec<u8>) -> CipherText {
        CipherText(ct)
    }
    pub fn from(mut other_ct: &mut CipherText) -> CipherText {
        let mut ct = CipherText::new(Vec::with_capacity(other_ct.len()));
        ct.append(&mut other_ct);
        ct
    }
    pub fn append(&mut self, other: &mut CipherText) {
        self.0.append(&mut other.0);
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn empty_into(&mut self, out: &mut CipherText) {
        out.0.append(&mut self.0)
    }
    pub fn into_vec(self) -> Vec<u8> {
        self.0
    }
    pub fn random_pad_upto(&mut self, final_size: usize) {
        let size = self.len();
        if final_size > size {
            self.0.resize(final_size, 0);
            let key = stream::salsa20::gen_key();
            let nonce = stream::salsa20::gen_nonce();
            stream::salsa20::stream_xor_inplace(&mut self.0[size..], &nonce, &key);
        }
    }
    pub fn as_ref(&self) -> CipherTextRef {
        CipherTextRef(self.0.as_ref())
    }
}

impl<'a> CipherTextRef<'a> {
    pub fn new(bytes: &'a [u8]) -> CipherTextRef<'a> {
        CipherTextRef(bytes)
    }
    pub fn slice(&self, from: usize, to: usize) -> CipherTextRef<'a> {
        CipherTextRef(&self.0[from..to])
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn split_from_right(&self, len: usize) -> (CipherTextRef<'a>, CipherTextRef<'a>) {
        assert!(self.len() >= len);
        (self.slice(0, self.len() - len), self.slice(self.len() - len, self.len()))
    }
}

pub struct RefKey {}

impl RefKey {
    pub fn add_sealed_size(size: usize) -> usize {
        size + 0  // TODO: should be len of seal(.., vec[0; size])
    }
    pub fn seal(cref: &mut ChunkRef, pt: PlainText) -> CipherText {
        // TODO(jos): WIP: Plug in encryption/crypto here.
        // Update cref with key.
        cref.length = pt.len();
        CipherText(pt.0)
    }

    pub fn unseal(cref: &ChunkRef, ct: CipherTextRef) -> PlainText {
        PlainText(ct.slice(cref.offset, cref.offset + cref.length).0.to_vec())
    }
}

pub struct FixedKey {
    key: Vec<u8>,
}

impl FixedKey {
    pub fn new(key: Vec<u8>) -> FixedKey {
        FixedKey { key: key }
    }

    pub fn seal(&self, pt: PlainText) -> CipherText {
        // TODO(jos): WIP: Plug in encryption/crypto here.
        // Seal with our fixed key.
        let nonce = stream::xsalsa20::gen_nonce();
        let mut ct = CipherText(pt.0);

        // Serialize ciphertext length.
        let mut ct_len = vec![];
        ct_len.write_u64::<LittleEndian>(ct.len() as u64).unwrap();
        assert_eq!(ct_len.len(), 8);

        // Add nonce and ciphertext length.
        ct.0.extend_from_slice(nonce.as_ref());
        ct.0.extend_from_slice(&ct_len[..]);
        // Add version.
        ct.0.extend_from_slice(&[1]);
        ct
    }

    fn version<'a>(ct: CipherTextRef<'a>) -> (CipherTextRef<'a>, CipherTextRef<'a>) {
        let (rest, version) = ct.split_from_right(1);
        assert_eq!(1, version.0[0]);
        (rest, version)
    }

    fn ciphertext_and_nonce<'a>(ct: CipherTextRef<'a>)
                                -> (CipherTextRef<'a>, CipherTextRef<'a>, &'a [u8]) {
        let (rest, mut ct_len_ref) = ct.split_from_right(8);
        let ct_len = ct_len_ref.0.read_u64::<LittleEndian>().unwrap();
        let (rest, nonce) = rest.split_from_right(stream::xsalsa20::NONCEBYTES);
        let (rest, ct) = rest.split_from_right(ct_len as usize);
        (rest, ct, nonce.0)
    }

    pub fn unseal<'a, 'b>(&'a self, ct: CipherTextRef<'b>) -> (CipherTextRef<'b>, PlainText) {
        // TODO(jos): WIP: Plug in encryption/crypto here.
        // Unseal with our fixed key.

        // Recover version with size of ciphertext.
        let (rest, _) = Self::version(ct);
        let (rest, ct, nonce) = Self::ciphertext_and_nonce(rest);
        let nonce = stream::xsalsa20::Nonce::from_slice(nonce);
        // TODO(jos): WIP: Use nonce to decrypt ct.
        drop(nonce);

        (rest, PlainText(ct.0.to_vec()))
    }
}
<commit_msg>Remove unused methods.<commit_after>use sodiumoxide::crypto::stream;
use blob::{ChunkRef, Kind};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

pub struct PlainText(Vec<u8>);
pub struct CipherText(Vec<u8>);
pub struct CipherTextRef<'a>(&'a [u8]);

impl PlainText {
    pub fn new(bytes: Vec<u8>) -> PlainText {
        PlainText(bytes)
    }
    pub fn from_vec(mut bytes: &mut Vec<u8>) -> PlainText {
        let mut pt = PlainText(Vec::with_capacity(bytes.len()));
        pt.0.append(&mut bytes);
        pt
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn as_ref(&self) -> &[u8] {
        &self.0[..]
    }
}

impl CipherText {
    pub fn new(ct: Vec<u8>) -> CipherText {
        CipherText(ct)
    }
    pub fn from(mut other_ct: &mut CipherText) -> CipherText {
        let mut ct = CipherText::new(Vec::with_capacity(other_ct.len()));
        ct.append(&mut other_ct);
        ct
    }
    pub fn append(&mut self, other: &mut CipherText) {
        self.0.append(&mut other.0);
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn empty_into(&mut self, out: &mut CipherText) {
        out.0.append(&mut self.0)
    }
    pub fn random_pad_upto(&mut self, final_size: usize) {
        let size = self.len();
        if final_size > size {
            self.0.resize(final_size, 0);
            let key = stream::salsa20::gen_key();
            let nonce = stream::salsa20::gen_nonce();
            stream::salsa20::stream_xor_inplace(&mut self.0[size..], &nonce, &key);
        }
    }
    pub fn into_vec(self) -> Vec<u8> {
        self.0
    }
}

impl<'a> CipherTextRef<'a> {
    pub fn new(bytes: &'a [u8]) -> CipherTextRef<'a> {
        CipherTextRef(bytes)
    }
    pub fn slice(&self, from: usize, to: usize) -> CipherTextRef<'a> {
        CipherTextRef(&self.0[from..to])
    }
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn split_from_right(&self, len: usize) -> (CipherTextRef<'a>, CipherTextRef<'a>) {
        assert!(self.len() >= len);
        (self.slice(0, self.len() - len), self.slice(self.len() - len, self.len()))
    }
}

pub struct RefKey {}

impl RefKey {
    pub fn add_sealed_size(size: usize) -> usize {
        size + 0  // TODO: should be len of seal(.., vec[0; size])
    }
    pub fn seal(cref: &mut ChunkRef, pt: PlainText) -> CipherText {
        // TODO(jos): WIP: Plug in encryption/crypto here.
        // Update cref with key.
        cref.length = pt.len();
        CipherText(pt.0)
    }

    pub fn unseal(cref: &ChunkRef, ct: CipherTextRef) -> PlainText {
        PlainText(ct.slice(cref.offset, cref.offset + cref.length).0.to_vec())
    }
}

pub struct FixedKey {
    key: Vec<u8>,
}

impl FixedKey {
    pub fn new(key: Vec<u8>) -> FixedKey {
        FixedKey { key: key }
    }

    pub fn seal(&self, pt: PlainText) -> CipherText {
        // TODO(jos): WIP: Plug in encryption/crypto here.
        // Seal with our fixed key.
        let nonce = stream::xsalsa20::gen_nonce();
        let mut ct = CipherText(pt.0);

        // Serialize ciphertext length.
        let mut ct_len = vec![];
        ct_len.write_u64::<LittleEndian>(ct.len() as u64).unwrap();
        assert_eq!(ct_len.len(), 8);

        // Add nonce and ciphertext length.
        ct.0.extend_from_slice(nonce.as_ref());
        ct.0.extend_from_slice(&ct_len[..]);
        // Add version.
        ct.0.extend_from_slice(&[1]);
        ct
    }

    fn version<'a>(ct: CipherTextRef<'a>) -> (CipherTextRef<'a>, CipherTextRef<'a>) {
        let (rest, version) = ct.split_from_right(1);
        assert_eq!(1, version.0[0]);
        (rest, version)
    }

    fn ciphertext_and_nonce<'a>(ct: CipherTextRef<'a>)
                                -> (CipherTextRef<'a>, CipherTextRef<'a>, &'a [u8]) {
        let (rest, mut ct_len_ref) = ct.split_from_right(8);
        let ct_len = ct_len_ref.0.read_u64::<LittleEndian>().unwrap();
        let (rest, nonce) = rest.split_from_right(stream::xsalsa20::NONCEBYTES);
        let (rest, ct) = rest.split_from_right(ct_len as usize);
        (rest, ct, nonce.0)
    }

    pub fn unseal<'a, 'b>(&'a self, ct: CipherTextRef<'b>) -> (CipherTextRef<'b>, PlainText) {
        // TODO(jos): WIP: Plug in encryption/crypto here.
        // Unseal with our fixed key.

        // Recover version with size of ciphertext.
        let (rest, _) = Self::version(ct);
        let (rest, ct, nonce) = Self::ciphertext_and_nonce(rest);
        let nonce = stream::xsalsa20::Nonce::from_slice(nonce);
        // TODO(jos): WIP: Use nonce to decrypt ct.
        drop(nonce);

        (rest, PlainText(ct.0.to_vec()))
    }
}
<|endoftext|>


<commit_before>pub mod search;

use std::thread;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::TranspositionTable;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    multi_pv: usize,
    is_thinking: bool,
    is_pondering: bool,
    replies: Vec<EngineReply>,
    infinite: bool,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    started_thinking_at: SystemTime,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            multi_pv: 1,
            is_thinking: false,
            is_pondering: false,
            replies: vec![],
            infinite: false,
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            started_thinking_at: SystemTime::now(),
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // We do not support re-sizing of the transposition table
            // once the engine had started, so we do nothing.
            "Hash" => (),

            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // Tells the engine to output multiple best lines.
            // the default value is `1`.)
            "MultiPV" => {
                self.multi_pv = value.parse::<usize>().ok().unwrap_or(1);
            }

            // An invalid option.
            _ => {
                self.replies.push(EngineReply::Info(vec![("string".to_string(),
                                                          format!("Invalid option \"{}\"", name))]));
            }
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            self.commands
                .send(search::Command::Search {
                    search_id: 0,
                    position: self.position.clone(),
                    depth: 128,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
            self.is_thinking = true;
            self.is_pondering = ponder;
            self.infinite = infinite;
            self.started_thinking_at = SystemTime::now();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement real time management here.
        if !self.is_pondering && !self.infinite &&
           self.started_thinking_at.elapsed().unwrap().as_secs() > 5 {
            self.stop();
        }

        self.replies.pop()
    }
}

impl Engine {
    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::from_u32(0);
            let mut v = Vec::with_capacity(128);
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.move16();
        }
        if m != 0 {
            let move_type = ((m & 0b1100000000000000) >> 14) as MoveType;
            let orig_square = ((m & 0b0011111100000000) >> 8) as Square;
            let dest_square = ((m & 0b0000000011111100) >> 2) as Square;
            let promoted_piece = match m & 0b11 {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
            ("MultiPV".to_string(), OptionDescription::Spin { min: 1, max: 32, default: 1 }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}
<commit_msg>flush the reports queue<commit_after>pub mod search;

use std::thread;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::TranspositionTable;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    multi_pv: usize,
    is_thinking: bool,
    is_pondering: bool,
    replies: Vec<EngineReply>,
    infinite: bool,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    started_thinking_at: SystemTime,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            multi_pv: 1,
            is_thinking: false,
            is_pondering: false,
            replies: vec![],
            infinite: false,
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            started_thinking_at: SystemTime::now(),
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // We do not support re-sizing of the transposition table
            // once the engine had started, so we do nothing.
            "Hash" => (),

            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // Tells the engine to output multiple best lines.
            // the default value is `1`.)
            "MultiPV" => {
                self.multi_pv = value.parse::<usize>().ok().unwrap_or(1);
            }

            // An invalid option.
            _ => {
                self.replies.push(EngineReply::Info(vec![("string".to_string(),
                                                          format!("Invalid option \"{}\"", name))]));
            }
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            self.commands
                .send(search::Command::Search {
                    search_id: 0,
                    position: self.position.clone(),
                    depth: 128,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
            self.is_thinking = true;
            self.is_pondering = ponder;
            self.infinite = infinite;
            self.started_thinking_at = SystemTime::now();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement real time management here.
        if !self.is_pondering && !self.infinite &&
           self.started_thinking_at.elapsed().unwrap().as_secs() > 3 {
            self.stop();
        }

        // Empty the reports queue.
        while let Ok(_) = self.reports.try_recv() {}

        self.replies.pop()
    }
}

impl Engine {
    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::from_u32(0);
            let mut v = Vec::with_capacity(128);
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.move16();
        }
        if m != 0 {
            let move_type = ((m & 0b1100000000000000) >> 14) as MoveType;
            let orig_square = ((m & 0b0011111100000000) >> 8) as Square;
            let dest_square = ((m & 0b0000000011111100) >> 2) as Square;
            let promoted_piece = match m & 0b11 {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
            ("MultiPV".to_string(), OptionDescription::Spin { min: 1, max: 32, default: 1 }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}
<|endoftext|>


<commit_before>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    curr_depth: u8,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    search_id: usize,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    stop_when: TimeManagement,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            curr_depth: 0,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.curr_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.curr_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        // Report search progress.
                        let thinking_duration = self.thinking_since.elapsed().unwrap();
                        self.searched_nodes = searched_nodes;
                        self.searched_time = 1000 * thinking_duration.as_secs() +
                                             (thinking_duration.subsec_nanos() / 1000000) as u64;
                        if self.curr_depth < depth {
                            self.report_pv();
                            self.curr_depth = depth;
                        }
                    }
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        // Terminate the search if not pondering of infinite.
                        if !self.is_pondering {
                            self.stop_when = if let TimeManagement::Infinite = self.stop_when {
                                TimeManagement::Infinite
                            } else {
                                TimeManagement::MoveTime(0)
                            };
                        }
                    }

                    // Stale reports from stopped searches.
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    // A helper method. It extracts the primary variation (PV) from
    // the transposition table (TT) and sends it to the GUI.
    fn report_pv(&mut self) {
        let mut prev_move = None;
        let mut p = self.position.clone();

        // Extract the PV, the value, and the bound from the TT.
        let mut pv = Vec::new();
        let mut value = -20000;
        let mut bound = BOUND_LOWER;
        while let Some(entry) = self.tt.probe(p.hash()) {
            if pv.len() < self.curr_depth as usize && entry.bound() != BOUND_NONE {
                if let Some(m) = prev_move {
                    pv.push(m);
                }
                if pv.len() & 1 == 0 {
                    value = entry.value();
                    bound = entry.bound();
                } else {
                    // In this case the value stored in `entry` is
                    // from other side's perspective.
                    value = -entry.value();
                    bound = match entry.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        x => x,
                    };
                }
                if bound == BOUND_EXACT {
                    if let Some(m) = p.try_move_digest(entry.move16()) {
                        if p.do_move(m) && !p.is_repeated() {
                            // Try to extend the PV.
                            prev_move = Some(m);
                            continue;
                        }
                    }
                }
            }
            break;
        }

        // Send the extracted info to the GUI.
        let value_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let nps = if self.searched_time == 0 {
            0
        } else {
            1000 * self.searched_nodes / self.searched_time
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.curr_depth)),
            ("score".to_string(), format!("cp {}{}", value, value_suffix)),
            ("time".to_string(), format!("{}", self.searched_time)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), pv_string),
        ]));
    }

    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<commit_msg>added periodic reports<commit_after>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    curr_depth: u8,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    search_id: usize,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    stop_when: TimeManagement,
    no_report_since: SystemTime,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            curr_depth: 0,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            stop_when: TimeManagement::Infinite,
            no_report_since: SystemTime::now(),
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.no_report_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.curr_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.curr_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        // Report search progress.
                        let thinking_duration = self.thinking_since.elapsed().unwrap();
                        self.searched_nodes = searched_nodes;
                        self.searched_time = 1000 * thinking_duration.as_secs() +
                                             (thinking_duration.subsec_nanos() / 1000000) as u64;
                        if self.curr_depth < depth {
                            self.report_pv();
                            self.curr_depth = depth;
                            self.no_report_since = SystemTime::now();
                        }
                        if self.no_report_since.elapsed().unwrap().as_secs() > 30 {
                            self.report_progress();
                            self.no_report_since = SystemTime::now();
                        }
                    }
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        // Terminate the search if not pondering of infinite.
                        if !self.is_pondering {
                            self.stop_when = if let TimeManagement::Infinite = self.stop_when {
                                TimeManagement::Infinite
                            } else {
                                TimeManagement::MoveTime(0)
                            };
                        }
                    }

                    // Stale reports from stopped searches.
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    // A helper method. It extracts the primary variation (PV) from
    // the transposition table (TT) and sends it to the GUI.
    fn report_pv(&mut self) {
        let mut prev_move = None;
        let mut p = self.position.clone();

        // Extract the PV, the value, and the bound from the TT.
        let mut pv = Vec::new();
        let mut value = -20000;
        let mut bound = BOUND_LOWER;
        while let Some(entry) = self.tt.probe(p.hash()) {
            if pv.len() < self.curr_depth as usize && entry.bound() != BOUND_NONE {
                if let Some(m) = prev_move {
                    pv.push(m);
                }
                if pv.len() & 1 == 0 {
                    value = entry.value();
                    bound = entry.bound();
                } else {
                    // In this case the value stored in `entry` is
                    // from other side's perspective.
                    value = -entry.value();
                    bound = match entry.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        x => x,
                    };
                }
                if bound == BOUND_EXACT {
                    if let Some(m) = p.try_move_digest(entry.move16()) {
                        if p.do_move(m) && !p.is_repeated() {
                            // Try to extend the PV.
                            prev_move = Some(m);
                            continue;
                        }
                    }
                }
            }
            break;
        }

        // Send the extracted info to the GUI.
        let value_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let nps = if self.searched_time == 0 {
            0
        } else {
            1000 * self.searched_nodes / self.searched_time
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.curr_depth)),
            ("score".to_string(), format!("cp {}{}", value, value_suffix)),
            ("time".to_string(), format!("{}", self.searched_time)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), pv_string),
        ]));
    }

    // A helper method. It reports the depth, the node count, and
    // nodes per second to the GUI.
    fn report_progress(&mut self) {
        let nps = if self.searched_time == 0 {
            0
        } else {
            1000 * self.searched_nodes / self.searched_time
        };
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.curr_depth)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
        ]));
    }

    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<|endoftext|>


<commit_before>use time::Tm;
use serde::ser::{Serialize, Serializer};
use byteorder::{BigEndian, WriteBytesExt};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EventTime {
    time: Tm,
}

impl EventTime {
    pub fn new(time: Tm) -> EventTime {
        EventTime { time: time }
    }

    pub fn get_time(&self) -> &Tm {
        &self.time
    }
}

impl Serialize for EventTime {
    // The signature of a serialize_with function must follow the pattern:
    //
    //    fn serialize<S>(&T, S) -> Result<S::Ok, S::Error> where S: Serializer
    //
    // although it may also be generic over the input types T.
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        use serde::ser::Error;
        let mut buf = vec![];
        buf.write_u8(0xd7).map_err(Error::custom)?;
        buf.write_u8(0x00).map_err(Error::custom)?;
        buf.write_i32::<BigEndian>(self.clone().time.clone().to_timespec().sec as i32)
            .map_err(Error::custom)?;
        buf.write_i32::<BigEndian>(self.clone().time.clone().to_timespec().nsec as i32)
            .map_err(Error::custom)?;
        serializer.serialize_bytes(&buf)
    }
}
<commit_msg>Add testcase for EventTime serialize format<commit_after>use time::Tm;
use serde::ser::{Serialize, Serializer};
use byteorder::{BigEndian, WriteBytesExt};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EventTime {
    time: Tm,
}

impl EventTime {
    pub fn new(time: Tm) -> EventTime {
        EventTime { time: time }
    }

    pub fn get_time(&self) -> &Tm {
        &self.time
    }
}

impl Serialize for EventTime {
    // The signature of a serialize_with function must follow the pattern:
    //
    //    fn serialize<S>(&T, S) -> Result<S::Ok, S::Error> where S: Serializer
    //
    // although it may also be generic over the input types T.
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        use serde::ser::Error;
        let mut buf = vec![];
        buf.write_u8(0xd7).map_err(Error::custom)?;
        buf.write_u8(0x00).map_err(Error::custom)?;
        buf.write_i32::<BigEndian>(self.clone().time.clone().to_timespec().sec as i32)
            .map_err(Error::custom)?;
        buf.write_i32::<BigEndian>(self.clone().time.clone().to_timespec().nsec as i32)
            .map_err(Error::custom)?;
        serializer.serialize_bytes(&buf)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use time;
    use time::Timespec;
    use rmp_serde::encode::Serializer;

    #[test]
    fn test_event_time_format() {
        let timespec = Timespec::new(1494245571, 0);
        let time = time::at(timespec);
        let event_time = EventTime::new(time);
        let mut buf = vec![];
        let _ = event_time.serialize(&mut Serializer::new(&mut buf)).unwrap();
        assert_eq!(vec![0xc4, 0x0a, 0xd7, 0x00, 0x59, 0x10, 0x60, 0xc3, 0x00, 0x00, 0x00, 0x00],
                   buf);
    }
}
<|endoftext|>


<commit_before>use crate::{
    Angle, Canvas, Center, Ellipse, FlatIterPath, Paint, Rotate, Scale, Translate, P2, V2,
};
use arrayvec::ArrayVec;
use float_ord::FloatOrd;

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Rect {
    pub bottom_left: P2,
    pub width: f32,
    pub height: f32,
}

impl Paint for Rect {
    fn paint(&self, c: &mut Canvas) {
        c.paint(FlatIterPath::new(self.vertices(), /*closed=*/ true));
    }
}

impl Rect {
    /// Returns a `Rect` containing all given vertices.
    pub fn extent(vertices: impl Iterator<Item = P2>) -> Self {
        let mut min_x = FloatOrd(std::f32::MAX);
        let mut min_y = FloatOrd(std::f32::MAX);
        let mut max_y = FloatOrd(0.);
        let mut max_x = FloatOrd(0.);

        for vertex in vertices {
            min_x = std::cmp::min(min_x, FloatOrd(vertex.x));
            min_y = std::cmp::min(min_y, FloatOrd(vertex.y));
            max_x = std::cmp::max(max_x, FloatOrd(vertex.x));
            max_y = std::cmp::max(max_y, FloatOrd(vertex.y));
        }

        Self {
            bottom_left: P2::new(min_x.0, min_y.0),
            width: max_x.0 - min_x.0,
            height: max_y.0 - min_y.0,
        }
    }

    pub fn vertices(&self) -> impl DoubleEndedIterator<Item = P2> + Clone {
        ArrayVec::from([
            self.bottom_left,
            self.bottom_left.translate(V2::new(0., self.height)),
            self.bottom_left.translate(V2::new(self.width, self.height)),
            self.bottom_left.translate(V2::new(self.width, 0.)),
        ])
        .into_iter()
    }
}

impl Center for Rect {
    fn center(&self) -> P2 {
        self.bottom_left
            .translate(V2::new(self.width / 2., self.height / 2.))
    }
}

impl Scale for Rect {
    fn scale(self, factor: f32) -> Self {
        let center = self.center();
        let distance = (center - self.bottom_left).length();
        let phase = Ellipse::circle(center, distance).circumphase(&self.bottom_left);
        Self {
            bottom_left: Ellipse::circle(center, distance * factor).circumpoint(phase),
            height: self.height * factor,
            width: self.width * factor,
        }
    }
}

impl Translate for Rect {
    fn translate(self, offset: V2) -> Self {
        Self {
            bottom_left: self.bottom_left.translate(offset),
            ..self
        }
    }
}
<commit_msg>Apply cargo fix<commit_after>use crate::{Canvas, Center, Ellipse, FlatIterPath, Paint, Scale, Translate, P2, V2};
use arrayvec::ArrayVec;
use float_ord::FloatOrd;

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Rect {
    pub bottom_left: P2,
    pub width: f32,
    pub height: f32,
}

impl Paint for Rect {
    fn paint(&self, c: &mut Canvas) {
        c.paint(FlatIterPath::new(self.vertices(), /*closed=*/ true));
    }
}

impl Rect {
    /// Returns a `Rect` containing all given vertices.
    pub fn extent(vertices: impl Iterator<Item = P2>) -> Self {
        let mut min_x = FloatOrd(std::f32::MAX);
        let mut min_y = FloatOrd(std::f32::MAX);
        let mut max_y = FloatOrd(0.);
        let mut max_x = FloatOrd(0.);

        for vertex in vertices {
            min_x = std::cmp::min(min_x, FloatOrd(vertex.x));
            min_y = std::cmp::min(min_y, FloatOrd(vertex.y));
            max_x = std::cmp::max(max_x, FloatOrd(vertex.x));
            max_y = std::cmp::max(max_y, FloatOrd(vertex.y));
        }

        Self {
            bottom_left: P2::new(min_x.0, min_y.0),
            width: max_x.0 - min_x.0,
            height: max_y.0 - min_y.0,
        }
    }

    pub fn vertices(&self) -> impl DoubleEndedIterator<Item = P2> + Clone {
        ArrayVec::from([
            self.bottom_left,
            self.bottom_left.translate(V2::new(0., self.height)),
            self.bottom_left.translate(V2::new(self.width, self.height)),
            self.bottom_left.translate(V2::new(self.width, 0.)),
        ])
        .into_iter()
    }
}

impl Center for Rect {
    fn center(&self) -> P2 {
        self.bottom_left
            .translate(V2::new(self.width / 2., self.height / 2.))
    }
}

impl Scale for Rect {
    fn scale(self, factor: f32) -> Self {
        let center = self.center();
        let distance = (center - self.bottom_left).length();
        let phase = Ellipse::circle(center, distance).circumphase(&self.bottom_left);
        Self {
            bottom_left: Ellipse::circle(center, distance * factor).circumpoint(phase),
            height: self.height * factor,
            width: self.width * factor,
        }
    }
}

impl Translate for Rect {
    fn translate(self, offset: V2) -> Self {
        Self {
            bottom_left: self.bottom_left.translate(offset),
            ..self
        }
    }
}
<|endoftext|>