<commit_before>//! Structs and methods for working with direct messages.
//!
//! Every method in this module requires that your app has "read, write and direct message" access
//! requested for any account you attempt them with. If your app is read-only or read/write without
//! DM access, the calls will fail and return a permissions error from Twitter.
//!
//! Although the Twitter website and official apps display DMs as threads between the authenticated
//! user and specific other users, the API does not expose them like this. Separate calls to
//! `received` and `sent` are necessary to fully reconstruct a DM thread.

use common::*;

use rustc_serialize::json;
use chrono;

use auth;
use user;
use entities;
use error;
use error::Error::InvalidResponse;

mod fun;

pub use self::fun::*;

///Represents a single direct message.
///
///The structure of a single DM is fairly simple, as it doesn't have the same amount of metadata a
///regular tweet does. This means there are much fewer fields of this struct.
pub struct DirectMessage {
    ///Numeric ID for this DM.
    pub id: i64,
    ///UTC timestamp from when this DM was created.
    pub created_at: chrono::DateTime<chrono::UTC>,
    ///The text of the DM.
    pub text: String,
    ///Link, hashtag, and user mention information parsed out of the DM.
    pub entities: DMEntities,
    ///The screen name of the user who sent the DM.
    pub sender_screen_name: String,
    ///The ID of the user who sent the DM.
    pub sender_id: i64,
    ///Full information of the user who sent the DM.
    pub sender: Box<user::TwitterUser>,
    ///The screen name of the user who received the DM.
    pub recipient_screen_name: String,
    ///The ID of the user who received the DM.
    pub recipient_id: i64,
    ///Full information for the user who received the DM.
    pub recipient: Box<user::TwitterUser>,
}

///Container for URL, hashtag, mention, and media information associated with a direct message.
pub struct DMEntities {
    ///Collection of hashtags parsed from the DM.
    pub hashtags: Vec<entities::HashtagEntity>,
    ///Collection of financial symbols, or "cashtags", parsed from the DM.
    pub symbols: Vec<entities::HashtagEntity>,
    ///Collection of URLs parsed from the DM.
    pub urls: Vec<entities::UrlEntity>,
    ///Collection of user mentions parsed from the DM.
    pub user_mentions: Vec<entities::MentionEntity>,
    ///If the message contains any attached media, this contains a collection of media information
    ///from it.
    pub media: Option<Vec<entities::MediaEntity>>,
}

impl FromJson for DirectMessage {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("DirectMessage received json that wasn't an object",
                                       Some(input.to_string())));
        }

        Ok(DirectMessage {
            id: try!(field(input, "id")),
            created_at: try!(field(input, "created_at")),
            text: try!(field(input, "text")),
            entities: try!(field(input, "entities")),
            sender_screen_name: try!(field(input, "sender_screen_name")),
            sender_id: try!(field(input, "sender_id")),
            sender: try!(field(input, "sender")),
            recipient_screen_name: try!(field(input, "recipient_screen_name")),
            recipient_id: try!(field(input, "recipient_id")),
            recipient: try!(field(input, "recipient")),
        })
    }
}

impl FromJson for DMEntities {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("DMEntities received json that wasn't an object",
                                       Some(input.to_string())));
        }

        Ok(DMEntities {
            hashtags: try!(field(input, "hashtags")),
            symbols: try!(field(input, "symbols")),
            urls: try!(field(input, "urls")),
            user_mentions: try!(field(input, "user_mentions")),
            media: field(input, "media").ok(),
        })
    }
}

/// Helper struct to navigate collections of direct messages by requesting DMs older or newer than
/// certain IDs.
///
/// Using a Timeline to navigate collections of DMs allows you to efficiently cursor through a
/// collection and only load in the messages you need.
///
/// To begin, call a method that returns a `Timeline`, optionally set the page size, and call
/// `start` to load the first page of results:
///
/// ```rust,no_run
/// # let con_token = egg_mode::Token::new("", "");
/// # let access_token = egg_mode::Token::new("", "");
/// let mut timeline = egg_mode::direct::received(&con_token, &access_token)
///                                .with_page_size(10);
///
/// for dm in &timeline.start().unwrap().response {
///     println!("<@{}> {}", dm.sender_screen_name, dm.text);
/// }
/// ```
///
/// If you need to load the next set of messages, call `older`, which will automatically update the
/// IDs it tracks:
///
/// ```rust,no_run
/// # let con_token = egg_mode::Token::new("", "");
/// # let access_token = egg_mode::Token::new("", "");
/// # let mut timeline = egg_mode::direct::received(&con_token, &access_token);
/// # timeline.start().unwrap();
/// for dm in &timeline.older(None).unwrap().response {
///     println!("<@{}> {}", dm.sender_screen_name, dm.text);
/// }
/// ```
///
/// ...and similarly for `newer`, which operates in a similar fashion.
///
/// If you want to start afresh and reload the newest set of DMs again, you can call `start` again,
/// which will clear the tracked IDs before loading the newest set of messages. However, if you've
/// been storing these messages as you go, and already know the newest ID you have on hand, you can
/// load only those messages you need like this:
///
/// ```rust,no_run
/// # let con_token = egg_mode::Token::new("", "");
/// # let access_token = egg_mode::Token::new("", "");
/// let mut timeline = egg_mode::direct::received(&con_token, &access_token)
///                                .with_page_size(10);
///
/// timeline.start().unwrap();
///
/// //keep the max_id for later
/// let reload_id = timeline.max_id.unwrap();
///
/// //simulate scrolling down a little bit
/// timeline.older(None).unwrap();
/// timeline.older(None).unwrap();
///
/// //reload the timeline with only what's new
/// timeline.reset();
/// timeline.older(Some(reload_id)).unwrap();
/// ```
///
/// Here, the argument to `older` means "older than what I just returned, but newer than the given
/// ID". Since we cleared the tracked IDs with `reset`, that turns into "the newest DMs available
/// that were sent after the given ID". The earlier invocations of `older` with `None` do not place
/// a bound on the DMs it loads. `newer` operates in a similar fashion with its argument, saying
/// "newer than what I just returned, but not newer than this given ID". When called like this,
/// it's possible for these methods to return nothing, which will also clear the `Timeline`'s
/// tracked IDs.
///
/// If you want to manually pull messages between certain IDs, the baseline `call` function can do
/// that for you. Keep in mind, though, that `call` doesn't update the `min_id` or `max_id` fields,
/// so you'll have to set those yourself if you want to follow up with `older` or `newer`.
pub struct Timeline<'a> {
    ///The URL to request DMs from.
    link: &'static str,
    ///The consumer token to authenticate requests with.
    con_token: &'a auth::Token<'a>,
    ///The access token to authenticate requests with.
    access_token: &'a auth::Token<'a>,
    ///Optional set of params to include prior to adding lifetime navigation parameters.
    params_base: Option<ParamList<'a>>,
    ///The maximum number of messages to return in a single call. Twitter doesn't guarantee
    ///returning exactly this number, as suspended or deleted content is removed after retrieving
    ///the initial collection of messages.
    pub count: i32,
    ///The largest/most recent DM ID returned in the last call to `start`, `older`, or `newer`.
    pub max_id: Option<i64>,
    ///The smallest/oldest DM ID returned in the last call to `start`, `older`, or `newer`.
    pub min_id: Option<i64>,
}

impl<'a> Timeline<'a> {
    ///Clear the saved IDs on this timeline.
    pub fn reset(&mut self) {
        self.max_id = None;
        self.min_id = None;
    }

    ///Clear the saved IDs on this timeline, and return the most recent set of messages.
    pub fn start(&mut self) -> WebResponse<Vec<DirectMessage>> {
        self.reset();

        self.older(None)
    }

    ///Return the set of DMs older than the last set pulled, optionally placing a minimum DM ID to
    ///bound with.
    pub fn older(&mut self, since_id: Option<i64>) -> WebResponse<Vec<DirectMessage>> {
        let resp = try!(self.call(since_id, self.min_id.map(|id| id - 1)));

        self.map_ids(&resp.response);

        Ok(resp)
    }

    ///Return the set of DMs newer than the last set pulled, optionally placing a maximum DM ID to
    ///bound with.
    pub fn newer(&mut self, max_id: Option<i64>) -> WebResponse<Vec<DirectMessage>> {
        let resp = try!(self.call(self.max_id, max_id));

        self.map_ids(&resp.response);

        Ok(resp)
    }

    ///Return the set of DMs between the IDs given.
    ///
    ///Note that the range is not fully inclusive; the message ID given by `since_id` will not be
    ///returned, but the message with `max_id` will be returned.
    ///
    ///If the range of DMs given by the IDs would return more than `self.count`, the newest set
    ///of messages will be returned.
    pub fn call(&self, since_id: Option<i64>, max_id: Option<i64>) -> WebResponse<Vec<DirectMessage>> {
        let mut params = self.params_base.as_ref().cloned().unwrap_or_default();
        add_param(&mut params, "count", self.count.to_string());

        if let Some(id) = since_id {
            add_param(&mut params, "since_id", id.to_string());
        }

        if let Some(id) = max_id {
            add_param(&mut params, "max_id", id.to_string());
        }

        let mut resp = try!(auth::get(self.link, self.con_token, self.access_token, Some(&params)));

        parse_response(&mut resp)
    }

    ///Helper builder function to set the page size.
    pub fn with_page_size(self, page_size: i32) -> Self {
        Timeline {
            count: page_size,
            ..self
        }
    }

    ///With the returned slice of DMs, set the min_id and max_id on self.
    fn map_ids(&mut self, resp: &[DirectMessage]) {
        self.max_id = resp.first().map(|status| status.id);
        self.min_id = resp.last().map(|status| status.id);
    }

    ///Create an instance of `Timeline` with the given link and tokens.
    fn new(link: &'static str, params_base: Option<ParamList<'a>>,
               con_token: &'a auth::Token, access_token: &'a auth::Token) -> Self {
        Timeline {
            link: link,
            con_token: con_token,
            access_token: access_token,
            params_base: params_base,
            count: 20,
            max_id: None,
            min_id: None,
        }
    }
}
<commit_msg>extra docs for the direct module<commit_after>//! Structs and methods for working with direct messages.
//!
//! Note that direct message access requires a special permissions level above regular read/write
//! access. Your app must be configured to have "read, write, and direct message" access to use any
//! function in this module, even the read-only ones.
//!
//! Although the Twitter website and official apps display DMs as threads between the authenticated
//! user and specific other users, the API does not expose them like this. Separate calls to
//! `received` and `sent` are necessary to fully reconstruct a DM thread. You can partition on
//! `sender_id`/`receiver_id` and sort by their `created_at` to weave the streams together.
//!
//! ## Types
//!
//! * `DirectMessage`/`DMEntities`: A single DM and its associated entities. The `DMEntities`
//!   struct contains information about URLs, user mentions, and hashtags in the DM.
//! * `Timeline`: Effectively the same as `tweet::Timeline`, but returns `DirectMessage`s instead.
//!   Returned by functions that traverse collections of DMs.
//!
//! ## Functions
//!
//! ### Lookup
//!
//! These functions pull a user's DMs for viewing. `sent` and `received` can be cursored with
//! sub-views like with tweets, so they return a `Timeline` instance that can be navigated at will.
//!
//! * `sent`
//! * `received`
//! * `show`
//!
//! ### Actions
//!
//! These functions are your basic write access to DMs. As a DM does not carry as much metadata as
//! a tweet, the `send` action does not go through a builder struct like with `DraftTweet`.
//!
//! * `send`
//! * `delete`

use common::*;

use rustc_serialize::json;
use chrono;

use auth;
use user;
use entities;
use error;
use error::Error::InvalidResponse;

mod fun;

pub use self::fun::*;

///Represents a single direct message.
///
///As a DM has far less metadata than a regular tweet, the structure consequently contains far
///fewer fields. The basic fields are `id`, `text`, `entities`, and `created_at`; everything else
///either refers to the sender or receiver in some manner.
pub struct DirectMessage {
    ///Numeric ID for this DM.
    pub id: i64,
    ///UTC timestamp from when this DM was created.
    pub created_at: chrono::DateTime<chrono::UTC>,
    ///The text of the DM.
    pub text: String,
    ///Link, hashtag, and user mention information parsed out of the DM.
    pub entities: DMEntities,
    ///The screen name of the user who sent the DM.
    pub sender_screen_name: String,
    ///The ID of the user who sent the DM.
    pub sender_id: i64,
    ///Full information of the user who sent the DM.
    pub sender: Box<user::TwitterUser>,
    ///The screen name of the user who received the DM.
    pub recipient_screen_name: String,
    ///The ID of the user who received the DM.
    pub recipient_id: i64,
    ///Full information for the user who received the DM.
    pub recipient: Box<user::TwitterUser>,
}

///Container for URL, hashtag, mention, and media information associated with a direct message.
///
///As far as entities are concerned, a DM can contain nearly everything a tweet can. The only thing
///that isn't present here is the "extended media" that would be on the tweet's `extended_entities`
///field. A user can attach a single picture to a DM via the Twitter website or official apps, so
///if that is present, it will be available in `media`. (Note that the functionality to send
///pictures through a DM is unavailable on the public API; only viewing them is possible.)
///
///For all other fields, if the message contains no hashtags, financial symbols ("cashtags"),
///links, or mentions, those corresponding fields will still be present, just empty.
pub struct DMEntities {
    ///Collection of hashtags parsed from the DM.
    pub hashtags: Vec<entities::HashtagEntity>,
    ///Collection of financial symbols, or "cashtags", parsed from the DM.
    pub symbols: Vec<entities::HashtagEntity>,
    ///Collection of URLs parsed from the DM.
    pub urls: Vec<entities::UrlEntity>,
    ///Collection of user mentions parsed from the DM.
    pub user_mentions: Vec<entities::MentionEntity>,
    ///If the message contains any attached media, this contains a collection of media information
    ///from it.
    pub media: Option<Vec<entities::MediaEntity>>,
}

impl FromJson for DirectMessage {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("DirectMessage received json that wasn't an object",
                                       Some(input.to_string())));
        }

        Ok(DirectMessage {
            id: try!(field(input, "id")),
            created_at: try!(field(input, "created_at")),
            text: try!(field(input, "text")),
            entities: try!(field(input, "entities")),
            sender_screen_name: try!(field(input, "sender_screen_name")),
            sender_id: try!(field(input, "sender_id")),
            sender: try!(field(input, "sender")),
            recipient_screen_name: try!(field(input, "recipient_screen_name")),
            recipient_id: try!(field(input, "recipient_id")),
            recipient: try!(field(input, "recipient")),
        })
    }
}

impl FromJson for DMEntities {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("DMEntities received json that wasn't an object",
                                       Some(input.to_string())));
        }

        Ok(DMEntities {
            hashtags: try!(field(input, "hashtags")),
            symbols: try!(field(input, "symbols")),
            urls: try!(field(input, "urls")),
            user_mentions: try!(field(input, "user_mentions")),
            media: field(input, "media").ok(),
        })
    }
}

/// Helper struct to navigate collections of direct messages by requesting DMs older or newer than
/// certain IDs.
///
/// Using a Timeline to navigate collections of DMs allows you to efficiently cursor through a
/// collection and only load in the messages you need.
///
/// To begin, call a method that returns a `Timeline`, optionally set the page size, and call
/// `start` to load the first page of results:
///
/// ```rust,no_run
/// # let con_token = egg_mode::Token::new("", "");
/// # let access_token = egg_mode::Token::new("", "");
/// let mut timeline = egg_mode::direct::received(&con_token, &access_token)
///                                .with_page_size(10);
///
/// for dm in &timeline.start().unwrap().response {
///     println!("<@{}> {}", dm.sender_screen_name, dm.text);
/// }
/// ```
///
/// If you need to load the next set of messages, call `older`, which will automatically update the
/// IDs it tracks:
///
/// ```rust,no_run
/// # let con_token = egg_mode::Token::new("", "");
/// # let access_token = egg_mode::Token::new("", "");
/// # let mut timeline = egg_mode::direct::received(&con_token, &access_token);
/// # timeline.start().unwrap();
/// for dm in &timeline.older(None).unwrap().response {
///     println!("<@{}> {}", dm.sender_screen_name, dm.text);
/// }
/// ```
///
/// ...and similarly for `newer`, which operates in a similar fashion.
///
/// If you want to start afresh and reload the newest set of DMs again, you can call `start` again,
/// which will clear the tracked IDs before loading the newest set of messages. However, if you've
/// been storing these messages as you go, and already know the newest ID you have on hand, you can
/// load only those messages you need like this:
///
/// ```rust,no_run
/// # let con_token = egg_mode::Token::new("", "");
/// # let access_token = egg_mode::Token::new("", "");
/// let mut timeline = egg_mode::direct::received(&con_token, &access_token)
///                                .with_page_size(10);
///
/// timeline.start().unwrap();
///
/// //keep the max_id for later
/// let reload_id = timeline.max_id.unwrap();
///
/// //simulate scrolling down a little bit
/// timeline.older(None).unwrap();
/// timeline.older(None).unwrap();
///
/// //reload the timeline with only what's new
/// timeline.reset();
/// timeline.older(Some(reload_id)).unwrap();
/// ```
///
/// Here, the argument to `older` means "older than what I just returned, but newer than the given
/// ID". Since we cleared the tracked IDs with `reset`, that turns into "the newest DMs available
/// that were sent after the given ID". The earlier invocations of `older` with `None` do not place
/// a bound on the DMs it loads. `newer` operates in a similar fashion with its argument, saying
/// "newer than what I just returned, but not newer than this given ID". When called like this,
/// it's possible for these methods to return nothing, which will also clear the `Timeline`'s
/// tracked IDs.
///
/// If you want to manually pull messages between certain IDs, the baseline `call` function can do
/// that for you. Keep in mind, though, that `call` doesn't update the `min_id` or `max_id` fields,
/// so you'll have to set those yourself if you want to follow up with `older` or `newer`.
pub struct Timeline<'a> {
    ///The URL to request DMs from.
    link: &'static str,
    ///The consumer token to authenticate requests with.
    con_token: &'a auth::Token<'a>,
    ///The access token to authenticate requests with.
    access_token: &'a auth::Token<'a>,
    ///Optional set of params to include prior to adding lifetime navigation parameters.
    params_base: Option<ParamList<'a>>,
    ///The maximum number of messages to return in a single call. Twitter doesn't guarantee
    ///returning exactly this number, as suspended or deleted content is removed after retrieving
    ///the initial collection of messages.
    pub count: i32,
    ///The largest/most recent DM ID returned in the last call to `start`, `older`, or `newer`.
    pub max_id: Option<i64>,
    ///The smallest/oldest DM ID returned in the last call to `start`, `older`, or `newer`.
    pub min_id: Option<i64>,
}

impl<'a> Timeline<'a> {
    ///Clear the saved IDs on this timeline.
    pub fn reset(&mut self) {
        self.max_id = None;
        self.min_id = None;
    }

    ///Clear the saved IDs on this timeline, and return the most recent set of messages.
    pub fn start(&mut self) -> WebResponse<Vec<DirectMessage>> {
        self.reset();

        self.older(None)
    }

    ///Return the set of DMs older than the last set pulled, optionally placing a minimum DM ID to
    ///bound with.
    pub fn older(&mut self, since_id: Option<i64>) -> WebResponse<Vec<DirectMessage>> {
        let resp = try!(self.call(since_id, self.min_id.map(|id| id - 1)));

        self.map_ids(&resp.response);

        Ok(resp)
    }

    ///Return the set of DMs newer than the last set pulled, optionally placing a maximum DM ID to
    ///bound with.
    pub fn newer(&mut self, max_id: Option<i64>) -> WebResponse<Vec<DirectMessage>> {
        let resp = try!(self.call(self.max_id, max_id));

        self.map_ids(&resp.response);

        Ok(resp)
    }

    ///Return the set of DMs between the IDs given.
    ///
    ///Note that the range is not fully inclusive; the message ID given by `since_id` will not be
    ///returned, but the message with `max_id` will be returned.
    ///
    ///If the range of DMs given by the IDs would return more than `self.count`, the newest set
    ///of messages will be returned.
    pub fn call(&self, since_id: Option<i64>, max_id: Option<i64>) -> WebResponse<Vec<DirectMessage>> {
        let mut params = self.params_base.as_ref().cloned().unwrap_or_default();
        add_param(&mut params, "count", self.count.to_string());

        if let Some(id) = since_id {
            add_param(&mut params, "since_id", id.to_string());
        }

        if let Some(id) = max_id {
            add_param(&mut params, "max_id", id.to_string());
        }

        let mut resp = try!(auth::get(self.link, self.con_token, self.access_token, Some(&params)));

        parse_response(&mut resp)
    }

    ///Helper builder function to set the page size.
    pub fn with_page_size(self, page_size: i32) -> Self {
        Timeline {
            count: page_size,
            ..self
        }
    }

    ///With the returned slice of DMs, set the min_id and max_id on self.
    fn map_ids(&mut self, resp: &[DirectMessage]) {
        self.max_id = resp.first().map(|status| status.id);
        self.min_id = resp.last().map(|status| status.id);
    }

    ///Create an instance of `Timeline` with the given link and tokens.
    fn new(link: &'static str, params_base: Option<ParamList<'a>>,
               con_token: &'a auth::Token, access_token: &'a auth::Token) -> Self {
        Timeline {
            link: link,
            con_token: con_token,
            access_token: access_token,
            params_base: params_base,
            count: 20,
            max_id: None,
            min_id: None,
        }
    }
}
<|endoftext|>


<commit_before>use std::iter::range_step_inclusive;
use std::num::{min, max, abs, clamp, Round};

use gmath::vectors::Vec2;
use gmath::shapes::Rect;
use game::map::Map;

pub mod player;

static PIXEL_SCALE: f32 = 52.0;
static GRAVITY: f32 = 9.8;

pub struct PhysicalProperties {
    // Air resistance properties
    c_drag        : f32,
    mass          : f32, // (kg)
    acting_area   : f32, // (m^2)
    // Movement properties
    movement_accel: f32, // (m/s^2)
    max_velocity  : f32, // (m/s)
    jump_accel    : f32, // (m/s^2)
    jump_time     : f32, // (secs)
    stopping_bonus: f32,
}

pub trait Entity {
    /// Retuns the acceleration of an entity
    fn acceleration(&self) -> Vec2<f32>;
    /// Sets the acceleration of an entity
    fn set_acceleration(&mut self, new_accel: Vec2<f32>);
    /// Returns the velocity of an entity
    fn velocity(&self) -> Vec2<f32>;
    /// Sets the velocity of an entity
    fn set_velocity(&mut self, new_vel: Vec2<f32>);
    /// Gets the position of an entity
    fn position(&self) -> Vec2<f32>;
    /// Sets the position of an entity
    fn set_position(&mut self, new_pos: Vec2<f32>);
    /// Gets the entites physical properties
    fn physical_properties<'a>(&'a self) -> &'a PhysicalProperties;
    // Returns true if friction should be applied, and false otherwise
    fn apply_friction(&self) -> bool {
        abs(self.acceleration().x) == 0.0 || (self.velocity().x != 0.0 &&
                self.velocity().x.signum() != self.acceleration().x.signum())
    }
    /// Returns true if the entity is on the ground
    fn is_on_ground(&self) -> bool;
    /// Notify the entity if it has hit something in the y direction
    fn hit_y(&mut self, value: bool);
    /// Returns the entity's bounding rectangle
    fn bounds(&self) -> Rect;
}

/// Update entity based on physics
/// # Arguments
/// `entity` - the entity the apply physics to
/// `map` - the map where the entity is
/// `secs` - the number of seconds since the last update
pub fn physics<T: Entity>(entity: &mut T, map: &Map, secs: f32) {
    let mut new_velocity = entity.velocity();
    // Increase velocity due to acceleration
    new_velocity = new_velocity + entity.acceleration().mul(secs);
    // Decrease velocity due to air resistance
    new_velocity = new_velocity + air_resistance(entity).mul(secs);
    // Decrease X velocity due to friction
    if entity.is_on_ground() && entity.apply_friction() {
        let friction = 0.9 * GRAVITY * entity.physical_properties().stopping_bonus * secs;
        new_velocity.x =
            if new_velocity.x < 0.0 {
                min(new_velocity.x + friction, 0.0)
            }
            else {
                max(new_velocity.x - friction, 0.0)
            };
    }

    // Ensure that the velocity doesn't get too hight
    let max_vel = entity.physical_properties().max_velocity;
    new_velocity.x = clamp(new_velocity.x, -max_vel, max_vel);
    entity.set_velocity(new_velocity);

    let mut new_position = entity.position();
    // Calculate the new x position
    let move_x = entity.velocity().x * secs * PIXEL_SCALE;
    let collision_x = map_collision_x(entity, map, move_x);
    if abs(move_x) > abs(collision_x) {
        new_position.x += collision_x;
        new_velocity.x = 0.0;
    }
    else {
        new_position.x += move_x;
    }
    entity.set_position(new_position);

    // Calculate the new y position
    let move_y = new_velocity.y * secs * PIXEL_SCALE;
    let collision_y = map_collision_y(entity, map, move_y);
    if abs(move_y) > abs(collision_y) {
        new_position.y += collision_y;
        new_velocity.y = 0.0;
        entity.hit_y(true);
    }
    else {
        new_position.y += move_y;
        entity.hit_y(false);
    }
    entity.set_position(new_position);

    entity.set_velocity(new_velocity);
}

fn air_resistance<T: Entity>(entity: &T) -> Vec2<f32> {
    static AIR_DENSITY: f32 = 1.2; // (kg/m^3)
    if entity.velocity().length_sqr() > 0.1 {
        let properties = entity.physical_properties();
        // Calulate the force of the air resistance
        // (see: http://en.wikipedia.org/wiki/Drag_(physics))
        let force = 0.5 * AIR_DENSITY * entity.velocity().length_sqr() * properties.c_drag *
                properties.acting_area;
        // Return a vector of the acceleration due to air resistance
        entity.velocity().unit().mul(-force / properties.mass)
    }
    else {
        // Below the threshold velocity the air resistance is 0
        Vec2::<f32>::zero()
    }
}

/// Calculates the maximum distance the entity can travel in the x direction
fn map_collision_x<T: Entity>(entity: &T, map: &Map, max_dist: f32) -> f32 {
    if max_dist != 0.0 {
        let tile_size = map.tile_size() as f32;
        // Map bounds
        let map_top = 0;
        let map_bottom = (map.height() - 1) as int;
        let map_left = 0;
        let map_right = (map.width() - 1) as int;
        // Calculate range of x values to check
        let (start_x, end_x, dir_x) =
            if entity.velocity().x < 0.0 {
                ((entity.bounds().left() / tile_size).floor() as int, map_left, -1)
            }
            else {
                ((entity.bounds().right() / tile_size).ceil() as int, map_right, 1)
            };
        // Calculate the range of y values to check
        let start_y = max(map_top, (entity.bounds().top() / tile_size).floor() as int);
        let end_y = min(map_bottom, (entity.bounds().bottom() / tile_size).ceil() as int);
        // Check the tiles for collision
        let tile = scan_tiles_x(map, start_x, end_x, dir_x, start_y, end_y) as f32;
        // Calculate the new maximum distance
        if entity.velocity().x < 0.0 {
            max((tile + 1.0) * tile_size - entity.bounds().left(), max_dist)
        }
        else {
            min(tile * tile_size - entity.bounds().right(), max_dist)
        }
    }
    else {
        // The entity will not move at all
        0.0
    }
}

/// Scan for solid tiles in the x direction
fn scan_tiles_x(map: &Map, start_x: int, end_x: int, dir_x: int, start_y: int, end_y: int) -> int {
    for x in range_step_inclusive(start_x, end_x, dir_x) {
        for y in range(start_y, end_y) {
            if map.tile_info_at(x as uint, y as uint).solid {
                return x;
            }
        }
    }
    end_x
}

/// Calculates the maximum distance the entity can travel in the x direction
fn map_collision_y<T: Entity>(entity: &T, map: &Map, max_dist: f32) -> f32 {
    if max_dist != 0.0 {
        let tile_size = map.tile_size() as f32;
        // Map bounds
        let map_top = 0;
        let map_bottom = (map.height() - 1) as int;
        let map_left = 0;
        let map_right = (map.width() - 1) as int;
        // Calculate range of y values to check
        let (start_y, end_y, dir_y) =
            if entity.velocity().y < 0.0 {
                ((entity.bounds().top() / tile_size).floor() as int, map_top, -1)
            }
            else {
                ((entity.bounds().bottom() / tile_size).ceil() as int, map_bottom, 1)
            };
        // Calculate the range of y values to check
        let start_x = max(map_left, (entity.bounds().left() / tile_size).floor() as int);
        let end_x = min(map_right, (entity.bounds().right() / tile_size).ceil() as int);
        // Check the tiles for collision
        let tile = scan_tiles_y(map, start_y, end_y, dir_y, start_x, end_x) as f32;
        // Calculate the new maximum distance
        if entity.velocity().y < 0.0 {
            (tile + 1.0) * tile_size - entity.bounds().top()
        }
        else {
            tile * tile_size - entity.bounds().bottom()
        }
    }
    else {
        // The entity will not move at all
        0.0
    }
}

/// Scan for solid tiles in the y direction
fn scan_tiles_y(map: &Map, start_y: int, end_y: int, dir_y: int, start_x: int, end_x: int) -> int {
    for x in range(start_x, end_x) {
        for y in range_step_inclusive(start_y, end_y, dir_y) {
            if map.tile_info_at(x as uint, y as uint).solid {
                return y;
            }
        }
    }
    end_y
}
<commit_msg>Fix major bug with collision<commit_after>use std::iter::range_step_inclusive;
use std::num::{min, max, abs, clamp, Round};

use gmath::vectors::Vec2;
use gmath::shapes::Rect;
use game::map::Map;

pub mod player;

static PIXEL_SCALE: f32 = 52.0;
static GRAVITY: f32 = 9.8;

pub struct PhysicalProperties {
    // Air resistance properties
    c_drag        : f32,
    mass          : f32, // (kg)
    acting_area   : f32, // (m^2)
    // Movement properties
    movement_accel: f32, // (m/s^2)
    max_velocity  : f32, // (m/s)
    jump_accel    : f32, // (m/s^2)
    jump_time     : f32, // (secs)
    stopping_bonus: f32,
}

pub trait Entity {
    /// Retuns the acceleration of an entity
    fn acceleration(&self) -> Vec2<f32>;
    /// Sets the acceleration of an entity
    fn set_acceleration(&mut self, new_accel: Vec2<f32>);
    /// Returns the velocity of an entity
    fn velocity(&self) -> Vec2<f32>;
    /// Sets the velocity of an entity
    fn set_velocity(&mut self, new_vel: Vec2<f32>);
    /// Gets the position of an entity
    fn position(&self) -> Vec2<f32>;
    /// Sets the position of an entity
    fn set_position(&mut self, new_pos: Vec2<f32>);
    /// Gets the entites physical properties
    fn physical_properties<'a>(&'a self) -> &'a PhysicalProperties;
    // Returns true if friction should be applied, and false otherwise
    fn apply_friction(&self) -> bool {
        abs(self.acceleration().x) == 0.0 || (self.velocity().x != 0.0 &&
                self.velocity().x.signum() != self.acceleration().x.signum())
    }
    /// Returns true if the entity is on the ground
    fn is_on_ground(&self) -> bool;
    /// Notify the entity if it has hit something in the y direction
    fn hit_y(&mut self, value: bool);
    /// Returns the entity's bounding rectangle
    fn bounds(&self) -> Rect;
}

/// Update entity based on physics
/// # Arguments
/// `entity` - the entity the apply physics to
/// `map` - the map where the entity is
/// `secs` - the number of seconds since the last update
pub fn physics<T: Entity>(entity: &mut T, map: &Map, secs: f32) {
    let mut new_velocity = entity.velocity();
    // Increase velocity due to acceleration
    new_velocity = new_velocity + entity.acceleration().mul(secs);
    // Decrease velocity due to air resistance
    new_velocity = new_velocity + air_resistance(entity).mul(secs);
    // Decrease X velocity due to friction
    if entity.is_on_ground() && entity.apply_friction() {
        let friction = 0.9 * GRAVITY * entity.physical_properties().stopping_bonus * secs;
        new_velocity.x =
            if new_velocity.x < 0.0 {
                min(new_velocity.x + friction, 0.0)
            }
            else {
                max(new_velocity.x - friction, 0.0)
            };
    }

    // Ensure that the velocity doesn't get too hight
    let max_vel = entity.physical_properties().max_velocity;
    new_velocity.x = clamp(new_velocity.x, -max_vel, max_vel);
    entity.set_velocity(new_velocity);

    let mut new_position = entity.position();
    // Calculate the new x position
    let move_x = entity.velocity().x * secs * PIXEL_SCALE;
    let collision_x = map_collision_x(entity, map, move_x);
    if abs(move_x) > abs(collision_x) {
        new_position.x += collision_x;
        new_velocity.x = 0.0;
    }
    else {
        new_position.x += move_x;
    }
    entity.set_position(new_position);

    // Calculate the new y position
    let move_y = new_velocity.y * secs * PIXEL_SCALE;
    let collision_y = map_collision_y(entity, map, move_y);
    if abs(move_y) > abs(collision_y) {
        new_position.y += collision_y;
        new_velocity.y = 0.0;
        entity.hit_y(true);
    }
    else {
        new_position.y += move_y;
        entity.hit_y(false);
    }
    entity.set_position(new_position);

    entity.set_velocity(new_velocity);
}

fn air_resistance<T: Entity>(entity: &T) -> Vec2<f32> {
    static AIR_DENSITY: f32 = 1.2; // (kg/m^3)
    if entity.velocity().length_sqr() > 0.1 {
        let properties = entity.physical_properties();
        // Calulate the force of the air resistance
        // (see: http://en.wikipedia.org/wiki/Drag_(physics))
        let force = 0.5 * AIR_DENSITY * entity.velocity().length_sqr() * properties.c_drag *
                properties.acting_area;
        // Return a vector of the acceleration due to air resistance
        entity.velocity().unit().mul(-force / properties.mass)
    }
    else {
        // Below the threshold velocity the air resistance is 0
        Vec2::<f32>::zero()
    }
}

/// Calculates the maximum distance the entity can travel in the x direction
fn map_collision_x<T: Entity>(entity: &T, map: &Map, max_dist: f32) -> f32 {
    if max_dist != 0.0 {
        let tile_size = map.tile_size() as f32;
        // Map bounds
        let map_top = 0;
        let map_bottom = (map.height() - 1) as int;
        let map_left = 0;
        let map_right = (map.width() - 1) as int;
        // Calculate range of x values to check
        let (start_x, end_x, dir_x) =
            if entity.velocity().x < 0.0 {
                ((entity.bounds().left() / tile_size).floor() as int, map_left, -1)
            }
            else {
                ((entity.bounds().right() / tile_size).ceil() as int, map_right, 1)
            };
        // Calculate the range of y values to check
        let start_y = max(map_top, (entity.bounds().top() / tile_size).floor() as int);
        let end_y = min(map_bottom, (entity.bounds().bottom() / tile_size).ceil() as int);
        // Check the tiles for collision
        let tile = scan_tiles_x(map, start_x, end_x, dir_x, start_y, end_y) as f32;
        // Calculate the new maximum distance
        if entity.velocity().x < 0.0 {
            (tile + 1.0) * tile_size - entity.bounds().left()
        }
        else {
            tile * tile_size - entity.bounds().right()
        }
    }
    else {
        // The entity will not move at all
        0.0
    }
}

/// Scan for solid tiles in the x direction
fn scan_tiles_x(map: &Map, start_x: int, end_x: int, dir_x: int, start_y: int, end_y: int) -> int {
    for x in range_step_inclusive(start_x, end_x, dir_x) {
        for y in range(start_y, end_y) {
            if map.tile_info_at(x as uint, y as uint).solid {
                return x;
            }
        }
    }
    end_x
}

/// Calculates the maximum distance the entity can travel in the x direction
fn map_collision_y<T: Entity>(entity: &T, map: &Map, max_dist: f32) -> f32 {
    if max_dist != 0.0 {
        let tile_size = map.tile_size() as f32;
        // Map bounds
        let map_top = 0;
        let map_bottom = (map.height() - 1) as int;
        let map_left = 0;
        let map_right = (map.width() - 1) as int;
        // Calculate range of y values to check
        let (start_y, end_y, dir_y) =
            if entity.velocity().y < 0.0 {
                ((entity.bounds().top() / tile_size).floor() as int, map_top, -1)
            }
            else {
                ((entity.bounds().bottom() / tile_size).ceil() as int, map_bottom, 1)
            };
        // Calculate the range of y values to check
        let start_x = max(map_left, (entity.bounds().left() / tile_size).floor() as int);
        let end_x = min(map_right, (entity.bounds().right() / tile_size).ceil() as int);
        // Check the tiles for collision
        let tile = scan_tiles_y(map, start_y, end_y, dir_y, start_x, end_x) as f32;
        // Calculate the new maximum distance
        if entity.velocity().y < 0.0 {
            (tile + 1.0) * tile_size - entity.bounds().top()
        }
        else {
            tile * tile_size - entity.bounds().bottom()
        }
    }
    else {
        // The entity will not move at all
        0.0
    }
}

/// Scan for solid tiles in the y direction
fn scan_tiles_y(map: &Map, start_y: int, end_y: int, dir_y: int, start_x: int, end_x: int) -> int {
    for y in range_step_inclusive(start_y, end_y, dir_y) {
        for x in range(start_x, end_x) {
            if map.tile_info_at(x as uint, y as uint).solid {
                return y;
            }
        }
    }
    end_y
}
<|endoftext|>


<commit_before>
extern crate time;
extern crate gl;
extern crate graphics;
extern crate piston;

use self::gl::types::GLint;
use self::graphics::{
    Context,
    RelativeTransform2d,
    View,
};
use self::piston::{
    AssetStore,
    GlData,
    event,
    Game,
    GameWindow,
    Gl,
};
use {
    Event,
    EventCenter,
    EventType,
    KeyType,
};

impl KeyType for piston::keyboard::Key {
    fn id(&self) -> uint {
        // add the last enum item in piston::mouse::Button
        self.code() as uint + piston::mouse::Button8 as uint + 1
    }
}

impl KeyType for piston::mouse::Button {
    fn id(&self) -> uint {
        *self as uint
    }
}

impl EventType for piston::event::Event {
    fn is_press_key(&self, key: &KeyType) -> bool {
        match *self {
            piston::event::KeyPressed(k) if k.id() == key.id() => {
                true
            },
            piston::event::MouseButtonPressed(k) if k.id() == key.id() => {
                true
            },
            _ => {
                false
            },
        }
    }
    fn is_release_key(&self, key: &KeyType) -> bool {
        match *self {
            piston::event::KeyReleased(k) if k.id() == key.id() => {
                true
            },
            piston::event::MouseButtonReleased(k) if k.id() == key.id() => {
                true
            },
            _ => {
                false
            }
        }
    }
}

/// ******************************
/// * MOST CODE COPY FROM PISTON *
/// ******************************
///
/// Implemented by game application which want to use rust-event.
pub trait EventGame {
    /// Perform tasks for loading before showing anything.
    fn load(&mut self, _asset_store: &mut AssetStore) {}

    /// Register event before game loop
    fn register_event(&mut self, _event_center: &mut EventCenter<Self>) {}

    /// Sets up viewport.
    ///
    /// A viewport is the region of the window where graphics is rendered.
    #[inline(always)]
    fn viewport<W: GameWindow>(&self, game_window: &W) {
        let (w, h) = game_window.get_size();
        gl::Viewport(0, 0, w as GLint, h as GLint);
    }

    /// Whether the window should be closed.
    ///
    /// When this is `true` the application shuts down.
    /// This can be overridden to emulate a user closing the window.
    /// One can also override this method to prevent window from closing.
    fn should_close<W: GameWindow>(&self, game_window: &W) -> bool {
        game_window.should_close()
    }

    /// Swaps the front buffer with the back buffer.
    ///
    /// When called, This shows the next frame.
    /// The graphics is rendered to the back buffer.
    /// The front buffer is displayed on the screen.
    fn swap_buffers<W: GameWindow>(&self, game_window: &W) {
        game_window.swap_buffers()
    }

    /// Handle current window's event with EventCenter.
    fn handle_events<W: GameWindow>(
        &mut self,
        game_window: &mut W,
        event_center: &mut EventCenter<Self>
    ) {
        loop {
            let event = game_window.poll_event();
            if event == event::NoEvent {
                break;
            }
            event_center.receive_event(&event);
        }
    }

    /// Update the physical state of the game.
    ///
    /// `dt` is the delta time from last update in seconds.
    fn update(&mut self, _dt: f64, _event_center: &mut EventCenter<Self>, _asset_store: &mut AssetStore) {}

    /// Render graphics.
    ///
    /// `context` is a Rust-Graphics context.
    /// `gl` is the Piston OpenGL back-end for Rust-Graphics.
    fn render(&self, _ext_dt: f64, _context: &Context, _gl: &mut Gl) {}

    /// Executes a game loop.
    fn run<W: GameWindow>(
        &mut self,
        game_window: &mut W,
        asset_store: &mut AssetStore
    ) {
        use graphics::{Clear, AddColor};

        let mut event_center = EventCenter::new();

        self.load(asset_store);
        self.register_event(&mut event_center);

        let mut gl_data = GlData::new();
        let context = Context::new();
        let bg = game_window.get_settings().background_color;
        let bg = context.rgba(bg[0], bg[1], bg[2], bg[3]);
        let updates_per_second: f64 = 120.0;
        let max_updates_per_frame: u32 = 240;
        let max_frames_per_second: f64 = 60.0;

        let billion: u64 = 1_000_000_000;
        let dt: f64 = 1.0 / updates_per_second;
        let update_time_in_ns: u64 = billion / updates_per_second as u64;

        let start = time::precise_time_ns();
        let min_ns_per_frame = (billion as f64 / max_frames_per_second) as u64;
        let mut next_render = start;
        let mut last_update = start;
        while !self.should_close(game_window) {
            let now = time::precise_time_ns();

            // Render.
            let (w, h) = game_window.get_size();
            if w != 0 && h != 0 {
                self.viewport(game_window);
                let mut gl = Gl::new(&mut gl_data, asset_store);
                bg.clear(&mut gl);
                // Extrapolate time forward to allow smooth motion.
                // 'now' is always bigger than 'last_update'.
                let ext_dt = (now - last_update) as f64 / billion as f64;
                self.render(
                    ext_dt,
                    &context
                    .trans(-1.0, 1.0)
                    .scale(2.0 / w as f64, -2.0 / h as f64)
                    .store_view(),
                    &mut gl
                );
                self.swap_buffers(game_window);
            }

            // Set moment for next rendering.
            next_render = now + min_ns_per_frame;
            // Perform updates by fixed time step until it catches up.
            for _ in range(0, max_updates_per_frame) {
                // Break when catching up to next frame.
                if next_render <= last_update { break; }

                // Handle user input.
                // This is handled every update to make it more responsive.
                self.handle_events(game_window, &mut event_center);

                // Update application state.
                event_center.update(self, dt);
                self.update(dt, &mut event_center, asset_store);
                last_update += update_time_in_ns;
            }
        }
    }
}

<commit_msg>Cleanup code<commit_after>
extern crate time;
extern crate gl;
extern crate graphics;
extern crate piston;

use self::gl::types::GLint;
use self::graphics::{
    Context,
    RelativeTransform2d,
    View,
};
use self::piston::{
    AssetStore,
    GlData,
    event,
    Game,
    GameWindow,
    Gl,
};
use {
    Event,
    EventCenter,
    EventType,
    KeyType,
};

impl KeyType for piston::keyboard::Key {
    fn id(&self) -> uint {
        // add the last enum item in piston::mouse::Button
        self.code() as uint + piston::mouse::Button8 as uint + 1
    }
}

impl KeyType for piston::mouse::Button {
    fn id(&self) -> uint {
        *self as uint
    }
}

impl EventType for piston::event::Event {
    fn is_press_key(&self, key: &KeyType) -> bool {
        match *self {
            piston::event::KeyPressed(k) if k.id() == key.id() => {
                true
            },
            piston::event::MouseButtonPressed(k) if k.id() == key.id() => {
                true
            },
            _ => {
                false
            },
        }
    }
    fn is_release_key(&self, key: &KeyType) -> bool {
        match *self {
            piston::event::KeyReleased(k) if k.id() == key.id() => {
                true
            },
            piston::event::MouseButtonReleased(k) if k.id() == key.id() => {
                true
            },
            _ => {
                false
            }
        }
    }
}

/// ******************************
/// * MOST CODE COPY FROM PISTON *
/// ******************************
///
/// Implemented by game application which want to use rust-event.
pub trait EventGame {
    /// Perform tasks for loading before showing anything.
    fn load(&mut self, _asset_store: &mut AssetStore) {}

    /// Register event before game loop
    fn register_event(&mut self, _event_center: &mut EventCenter<Self>) {}

    /// Sets up viewport.
    ///
    /// A viewport is the region of the window where graphics is rendered.
    #[inline(always)]
    fn viewport<W: GameWindow>(&self, game_window: &W) {
        let (w, h) = game_window.get_size();
        gl::Viewport(0, 0, w as GLint, h as GLint);
    }

    /// Whether the window should be closed.
    ///
    /// When this is `true` the application shuts down.
    /// This can be overridden to emulate a user closing the window.
    /// One can also override this method to prevent window from closing.
    fn should_close<W: GameWindow>(&self, game_window: &W) -> bool {
        game_window.should_close()
    }

    /// Swaps the front buffer with the back buffer.
    ///
    /// When called, This shows the next frame.
    /// The graphics is rendered to the back buffer.
    /// The front buffer is displayed on the screen.
    fn swap_buffers<W: GameWindow>(&self, game_window: &W) {
        game_window.swap_buffers()
    }

    /// Handle current window's event with EventCenter.
    fn handle_events<W: GameWindow>(
        &mut self,
        game_window: &mut W,
        event_center: &mut EventCenter<Self>
    ) {
        loop {
            let event = game_window.poll_event();
            match event {
                event::NoEvent => {
                    break;
                },
                _ => {
                    event_center.receive_event(&event);
                },
            }
        }
    }

    /// Update the physical state of the game.
    ///
    /// `dt` is the delta time from last update in seconds.
    fn update(&mut self, _dt: f64, _event_center: &mut EventCenter<Self>, _asset_store: &mut AssetStore) {}

    /// Render graphics.
    ///
    /// `context` is a Rust-Graphics context.
    /// `gl` is the Piston OpenGL back-end for Rust-Graphics.
    fn render(&self, _ext_dt: f64, _context: &Context, _gl: &mut Gl) {}

    /// Executes a game loop.
    fn run<W: GameWindow>(
        &mut self,
        game_window: &mut W,
        asset_store: &mut AssetStore
    ) {
        use graphics::{Clear, AddColor};

        let mut event_center = EventCenter::new();

        self.load(asset_store);
        self.register_event(&mut event_center);

        let mut gl_data = GlData::new();
        let context = Context::new();
        let bg = game_window.get_settings().background_color;
        let bg = context.rgba(bg[0], bg[1], bg[2], bg[3]);
        let updates_per_second: f64 = 120.0;
        let max_updates_per_frame: u32 = 240;
        let max_frames_per_second: f64 = 60.0;

        let billion: u64 = 1_000_000_000;
        let dt: f64 = 1.0 / updates_per_second;
        let update_time_in_ns: u64 = billion / updates_per_second as u64;

        let start = time::precise_time_ns();
        let min_ns_per_frame = (billion as f64 / max_frames_per_second) as u64;
        let mut next_render = start;
        let mut last_update = start;
        while !self.should_close(game_window) {
            let now = time::precise_time_ns();

            // Render.
            let (w, h) = game_window.get_size();
            if w != 0 && h != 0 {
                self.viewport(game_window);
                let mut gl = Gl::new(&mut gl_data, asset_store);
                bg.clear(&mut gl);
                // Extrapolate time forward to allow smooth motion.
                // 'now' is always bigger than 'last_update'.
                let ext_dt = (now - last_update) as f64 / billion as f64;
                self.render(
                    ext_dt,
                    &context
                    .trans(-1.0, 1.0)
                    .scale(2.0 / w as f64, -2.0 / h as f64)
                    .store_view(),
                    &mut gl
                );
                self.swap_buffers(game_window);
            }

            // Set moment for next rendering.
            next_render = now + min_ns_per_frame;
            // Perform updates by fixed time step until it catches up.
            for _ in range(0, max_updates_per_frame) {
                // Break when catching up to next frame.
                if next_render <= last_update { break; }

                // Handle user input.
                // This is handled every update to make it more responsive.
                self.handle_events(game_window, &mut event_center);

                // Update application state.
                event_center.update(self, dt);
                self.update(dt, &mut event_center, asset_store);
                last_update += update_time_in_ns;
            }
        }
    }
}

<|endoftext|>


<commit_before>use std::error::Error;
use std::fs::File;
use std::io::{Result, Write};
use std::path::Path;

use chunk::*;

pub trait SaveToFile {
    fn save_to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()>;
}

impl<I: Iterator<Item=Chunk>> SaveToFile for I {
    fn save_to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
        //TODO: add backup
        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {:?}: {}", path.as_ref(),
                            Error::description(&why)),
            Ok(file) => file,
        };
        for ch in self {
            match file.write_all(&ch.to_string().into_bytes()) {
                Err(why) => panic!("couldn't write to {:?}: {}", path.as_ref(),
                                Error::description(&why)),
                Ok(_) => (),
            }
        }

        Ok(())
    }
}
<commit_msg>Create backup file, if none<commit_after>use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::{Result, Write};
use std::path::Path;

use chunk::*;

pub trait SaveToFile {
    fn save_to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()>;
}

impl<I: Iterator<Item=Chunk>> SaveToFile for I {
    fn save_to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
        match create_backup(&path) {
            Err(why) => panic!("couldn't create backup for {:?}: {}", path.as_ref(),
                            Error::description(&why)),
            Ok(_) => (),
        }
        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {:?}: {}", path.as_ref(),
                            Error::description(&why)),
            Ok(file) => file,
        };
        for ch in self {
            match file.write_all(&ch.to_string().into_bytes()) {
                Err(why) => panic!("couldn't write to {:?}: {}", path.as_ref(),
                                Error::description(&why)),
                Ok(_) => (),
            }
        }

        Ok(())
    }
}

/// Create .bak file, only if one not present
pub fn create_backup<P: AsRef<Path>>(path: P) -> Result<bool> {
    match fs::metadata(&path) {
        Err(_) => return Ok(false),
        Ok(_) => (),
    }
    let new_path = path.as_ref().with_extension("bak");
    match fs::metadata(&new_path) {
        Err(_) => (),
        Ok(_) => return Ok(false),
    }
    fs::rename(path, new_path).map(|_| true)
}
<|endoftext|>


<commit_before>pub mod search;

use std::thread;
use std::cmp::min;
use std::collections::VecDeque;
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::*;

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

/// Implements `UciEngine` trait.
pub struct Engine {
    tt: Arc<TranspositionTable>,
    reply_queue: VecDeque<EngineReply>,
    position: Position,
    is_thinking: bool,
    is_pondering: bool,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // A channel for sending commands to the search thread.
    commands: Sender<search::Command>,

    // A channel for receiving reports from the search thread.
    reports: Receiver<search::Report>,

    // Search status info
    search_id: usize,
    thinking_since: SystemTime,
    silent_since: SystemTime,
    current_depth: u8,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    nps: u64, // nodes per second
    mangled_pv: bool, // `true` if the primary variation is imperfect
    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 {
            tt: tt,
            reply_queue: VecDeque::new(),
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            is_thinking: false,
            is_pondering: false,
            pondering_is_allowed: false,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            silent_since: SystemTime::now(),
            current_depth: 0,
            searched_nodes: 0,
            searched_time: 0,
            nps: 0,
            mangled_pv: false,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "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 "searchmoves" and "mate" parameters.

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

            // 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(depth.unwrap() as u8)
            } else {
                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 deepening search to 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();

            // TODO: send ponder move as well.
            let best_move = self.get_best_move();
            self.reply_queue.push_back(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> {
        // Check if we should stop thinking.
        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.current_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Check the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    search::Report::Progress { search_id, depth, searched_nodes }
                        if search_id == self.search_id => {
                        // Register search progress.
                        self.register_progress(depth, searched_nodes);
                        if self.silent_since.elapsed().unwrap().as_secs() > 20 {
                            if self.mangled_pv {
                                self.report_pv(depth - 1);
                            } else {
                                self.report_progress();
                            }
                        }
                    }
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        // Terminate the search (unless pondering of infinite).
                        if !self.is_pondering {
                            self.stop_when = match self.stop_when {
                                TimeManagement::Infinite => TimeManagement::Infinite,
                                _ => TimeManagement::MoveTime(0),
                            };
                        }
                    }
                    // We may still receive stale reports from already
                    // stopped searches.
                    _ => (),
                }
            }
        }

        self.reply_queue.pop_front()
    }
}

impl Engine {
    // A helper method. It updates the engine search status info and
    // makes sure that the PV is sent to the GUI for every newly
    // reached depth.
    fn register_progress(&mut self, depth: u8, searched_nodes: NodeCount) {
        let thinking_duration = self.thinking_since.elapsed().unwrap();
        self.searched_time = 1000 * thinking_duration.as_secs() +
                             (thinking_duration.subsec_nanos() / 1000000) as u64;
        self.searched_nodes = searched_nodes;
        self.nps = 1000 * (self.nps + self.searched_nodes) / (1000 + self.searched_time);
        if self.current_depth < depth {
            self.current_depth = depth;
            self.report_pv(depth - 1);
            if !self.mangled_pv {
                self.report_progress();
            }
        }
    }

    // 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, depth: u8) {
        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() < 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;
        }
        self.mangled_pv = bound != BOUND_EXACT || pv.len() < depth as usize;

        // 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 mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.reply_queue.push_back(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", 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!("{}", self.nps)),
            ("pv".to_string(), pv_string),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It reports the current depth, the node count,
    // and the nodes per second to the GUI.
    fn report_progress(&mut self) {
        self.reply_queue.push_back(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.current_depth)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", self.nps)),
        ]));
        self.silent_since = SystemTime::now();
    }

    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>beautification<commit_after>pub mod search;

use std::thread;
use std::cmp::min;
use std::collections::VecDeque;
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::*;

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

/// Implements `UciEngine` trait.
pub struct Engine {
    tt: Arc<TranspositionTable>,
    reply_queue: VecDeque<EngineReply>,
    position: Position,
    is_thinking: bool,
    is_pondering: bool,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // A channel for sending commands to the search thread.
    commands: Sender<search::Command>,

    // A channel for receiving reports from the search thread.
    reports: Receiver<search::Report>,

    // Search status info
    search_id: usize,
    thinking_since: SystemTime,
    silent_since: SystemTime,
    current_depth: u8,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    nps: u64, // nodes per second
    mangled_pv: bool, // `true` if the primary variation is imperfect
    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 tt1 = Arc::new(tt);
        let tt2 = tt1.clone();
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            tt: tt1,
            reply_queue: VecDeque::new(),
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            is_thinking: false,
            is_pondering: false,
            pondering_is_allowed: false,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            silent_since: SystemTime::now(),
            current_depth: 0,
            searched_nodes: 0,
            searched_time: 0,
            nps: 0,
            mangled_pv: false,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "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 "searchmoves" and "mate" parameters.

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

            // 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(depth.unwrap() as u8)
            } else {
                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 deepening search to 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();

            // TODO: send ponder move as well.
            let best_move = self.get_best_move();
            self.reply_queue.push_back(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> {
        // Check if we should stop thinking.
        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.current_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Check the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    search::Report::Progress { search_id, depth, searched_nodes }
                        if search_id == self.search_id => {
                        // Register search progress.
                        self.register_progress(depth, searched_nodes);
                        if self.silent_since.elapsed().unwrap().as_secs() > 20 {
                            if self.mangled_pv {
                                self.report_pv(depth - 1);
                            } else {
                                self.report_progress();
                            }
                        }
                    }
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        // Terminate the search (unless pondering of infinite).
                        if !self.is_pondering {
                            self.stop_when = match self.stop_when {
                                TimeManagement::Infinite => TimeManagement::Infinite,
                                _ => TimeManagement::MoveTime(0),
                            };
                        }
                    }
                    // We may still receive stale reports from already
                    // stopped searches.
                    _ => (),
                }
            }
        }

        self.reply_queue.pop_front()
    }
}

impl Engine {
    // A helper method. It updates the engine search status info and
    // makes sure that the PV is sent to the GUI for every newly
    // reached depth.
    fn register_progress(&mut self, depth: u8, searched_nodes: NodeCount) {
        let thinking_duration = self.thinking_since.elapsed().unwrap();
        self.searched_time = 1000 * thinking_duration.as_secs() +
                             (thinking_duration.subsec_nanos() / 1000000) as u64;
        self.searched_nodes = searched_nodes;
        self.nps = 1000 * (self.nps + self.searched_nodes) / (1000 + self.searched_time);
        if self.current_depth < depth {
            self.current_depth = depth;
            self.report_pv(depth - 1);
            if !self.mangled_pv {
                self.report_progress();
            }
        }
    }

    // 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, depth: u8) {
        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() < 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;
        }
        self.mangled_pv = bound != BOUND_EXACT || pv.len() < depth as usize;

        // 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 mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.reply_queue.push_back(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", 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!("{}", self.nps)),
            ("pv".to_string(), pv_string),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It reports the current depth, the node count,
    // and the nodes per second to the GUI.
    fn report_progress(&mut self) {
        self.reply_queue.push_back(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.current_depth)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", self.nps)),
        ]));
        self.silent_since = SystemTime::now();
    }

    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><commit_msg>Need to declare modules :-/<commit_after>pub mod events;
pub mod window;
<|endoftext|>


<commit_before>#[cfg(test)]
use ::card::*;

#[test]
fn create_hand() {
    let mut deck = ::deck::Deck::new();
    let mut cards: Vec<::card::Card> = vec![];
    for _i in 0..5 {
        cards.push(deck.draw().unwrap());
    }
    let hand = ::hand::Hand::new(cards);

    assert_eq!(hand.cards.len(), 5);
}

#[test]
fn has_flush() {
    let a = Card {
        value: Value::Three,
        suit: Suit::Hearts
    };
    let b = Card {
        value: Value::Six,
        suit: Suit::Hearts
    };
    let c = Card {
        value: Value::Ten,
        suit: Suit::Hearts
    };
    let d = Card {
        value: Value::Queen,
        suit: Suit::Hearts
    };
    let e = Card {
        value: Value::Two,
        suit: Suit::Hearts
    };
    let cards: Vec<Card> = vec![a, b, c, d, e];
    let hand: ::hand::Hand = ::hand::Hand::new(cards);

    assert_eq!(hand.has_flush(), true);
}
<commit_msg>Added test for not flush<commit_after>#[cfg(test)]
use ::card::*;

#[test]
fn create_hand() {
    let mut deck = ::deck::Deck::new();
    let mut cards: Vec<::card::Card> = vec![];
    for _i in 0..5 {
        cards.push(deck.draw().unwrap());
    }
    let hand = ::hand::Hand::new(cards);

    assert_eq!(hand.cards.len(), 5);
}

#[test]
fn has_flush() {
    let a = Card {
        value: Value::Three,
        suit: Suit::Hearts
    };
    let b = Card {
        value: Value::Six,
        suit: Suit::Hearts
    };
    let c = Card {
        value: Value::Ten,
        suit: Suit::Hearts
    };
    let d = Card {
        value: Value::Queen,
        suit: Suit::Hearts
    };
    let e = Card {
        value: Value::Two,
        suit: Suit::Hearts
    };
    let cards: Vec<Card> = vec![a, b, c, d, e];
    let hand: ::hand::Hand = ::hand::Hand::new(cards);

    assert_eq!(hand.has_flush(), true);
}

#[test]
fn no_flush() {
    let a = Card {
        value: Value::Three,
        suit: Suit::Hearts
    };
    let b = Card {
        value: Value::Six,
        suit: Suit::Spades
    };
    let c = Card {
        value: Value::Ten,
        suit: Suit::Hearts
    };
    let d = Card {
        value: Value::Queen,
        suit: Suit::Hearts
    };
    let e = Card {
        value: Value::Two,
        suit: Suit::Hearts
    };
    let cards: Vec<Card> = vec![a, b, c, d, e];
    let hand: ::hand::Hand = ::hand::Hand::new(cards);

    assert_eq!(hand.has_flush(), false);
}
<|endoftext|>


<commit_before>//! Implements <https://tools.ietf.org/html/rfc7232> Conditional Requests

use std;
use std::env;
use std::io;
use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};

use hyper::Uri;

use {Error, Result};

#[derive(Clone, Debug)]
pub enum HttpCache {
    Noop,
    FileBased(PathBuf),
}

use HttpCache::*;

impl HttpCache {
    pub fn noop() -> Self {
        HttpCache::Noop
    }

    pub fn in_home_dir() -> Self {
        let mut dir = env::home_dir().expect("Expected a home dir");
        dir.push(".hubcaps/cache");
        HttpCache::FileBased(dir)
    }

    #[doc(hidden)]
    pub fn cache_body_and_etag(&self, uri: &str, body: &[u8], etag: &[u8]) -> Result<()> {
        match self {
            Noop => Ok(()),
            FileBased(dir) => {
                let mut path = cache_path(dir, &uri, "json");
                if let Some(parent) = path.parent() {
                    fs::create_dir_all(parent)?;
                }
                fs::write(&path, body)?;
                path.set_extension("etag");
                fs::write(&path, etag)?;
                Ok(())
            },
        }
    }

    #[doc(hidden)]
    pub fn lookup_etag(&self, uri: &str) -> Result<String> {
        match self {
            Noop => HttpCache::no_read("No etag cached"),
            FileBased(dir) => read_to_string(cache_path(dir, uri, "etag")),
        }
    }

    #[doc(hidden)]
    pub fn lookup_body(&self, uri: &str) -> Result<String> {
        match self {
            Noop => HttpCache::no_read("No body cached"),
            FileBased(dir) => read_to_string(cache_path(dir, uri, "json")),
        }
    }

    fn no_read<E: Into<Box<std::error::Error + Send + Sync>>>(error: E) -> Result<String> {
        Err(Error::from(io::Error::new(io::ErrorKind::NotFound, error)))
    }
}

///       cache_path("https://api.github.com/users/dwijnand/repos", "json") ==>
/// ~/.hubcaps/cache/v1/https/api.github.com/users/dwijnand/repos.json
fn cache_path<S: AsRef<OsStr>>(dir: &Path, uri: &str, extension: S) -> PathBuf {
    let uri = uri.parse::<Uri>().expect("Expected a URI");
    let mut path = dir.to_path_buf();
    path.push("v1");
    path.push(uri.scheme_part().expect("Expected a URI scheme").as_ref()); // https
    path.push(uri.authority_part().expect("Expected a URI authority").as_ref()); // api.github.com
    path.push(
        Path::new(uri.path()) // /users/dwijnand/repos
            .strip_prefix("/")
            .expect("Expected URI path to start with /"),
    );
    path.set_extension(extension);
    path
}

fn read_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
    fs::read_to_string(path).map_err(Error::from)
}
<commit_msg>Silence home_dir warning<commit_after>//! Implements <https://tools.ietf.org/html/rfc7232> Conditional Requests

use std;
use std::env;
use std::io;
use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};

use hyper::Uri;

use {Error, Result};

#[derive(Clone, Debug)]
pub enum HttpCache {
    Noop,
    FileBased(PathBuf),
}

use HttpCache::*;

impl HttpCache {
    pub fn noop() -> Self {
        HttpCache::Noop
    }

    pub fn in_home_dir() -> Self {
        #[allow(deprecated)] // TODO: Switch to the dirs crate.
        let mut dir = env::home_dir().expect("Expected a home dir");
        dir.push(".hubcaps/cache");
        HttpCache::FileBased(dir)
    }

    #[doc(hidden)]
    pub fn cache_body_and_etag(&self, uri: &str, body: &[u8], etag: &[u8]) -> Result<()> {
        match self {
            Noop => Ok(()),
            FileBased(dir) => {
                let mut path = cache_path(dir, &uri, "json");
                if let Some(parent) = path.parent() {
                    fs::create_dir_all(parent)?;
                }
                fs::write(&path, body)?;
                path.set_extension("etag");
                fs::write(&path, etag)?;
                Ok(())
            },
        }
    }

    #[doc(hidden)]
    pub fn lookup_etag(&self, uri: &str) -> Result<String> {
        match self {
            Noop => HttpCache::no_read("No etag cached"),
            FileBased(dir) => read_to_string(cache_path(dir, uri, "etag")),
        }
    }

    #[doc(hidden)]
    pub fn lookup_body(&self, uri: &str) -> Result<String> {
        match self {
            Noop => HttpCache::no_read("No body cached"),
            FileBased(dir) => read_to_string(cache_path(dir, uri, "json")),
        }
    }

    fn no_read<E: Into<Box<std::error::Error + Send + Sync>>>(error: E) -> Result<String> {
        Err(Error::from(io::Error::new(io::ErrorKind::NotFound, error)))
    }
}

///       cache_path("https://api.github.com/users/dwijnand/repos", "json") ==>
/// ~/.hubcaps/cache/v1/https/api.github.com/users/dwijnand/repos.json
fn cache_path<S: AsRef<OsStr>>(dir: &Path, uri: &str, extension: S) -> PathBuf {
    let uri = uri.parse::<Uri>().expect("Expected a URI");
    let mut path = dir.to_path_buf();
    path.push("v1");
    path.push(uri.scheme_part().expect("Expected a URI scheme").as_ref()); // https
    path.push(uri.authority_part().expect("Expected a URI authority").as_ref()); // api.github.com
    path.push(
        Path::new(uri.path()) // /users/dwijnand/repos
            .strip_prefix("/")
            .expect("Expected URI path to start with /"),
    );
    path.set_extension(extension);
    path
}

fn read_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
    fs::read_to_string(path).map_err(Error::from)
}
<|endoftext|>


<commit_before>use dbus::arg::{Array};
use dbus::MessageItem;

use super::{DBusFactory, DBusObjPath};

pub fn setup(f: &mut DBusFactory) -> DBusObjPath{
    //let f = Factory::new_fn::<()>();

    // TODO
    // Not working, choking on "1 lifetime parameter" expected for arg to outarg
    // Expanded it myself, seems fine, will have to debug later. For now writing
    // out the boiler plate is not that bad.
    /*
    dbus_interface! {
        path: "/org/way_cooler/pixels";
        name: "org.way_cooler.pixels";

        fn Get() -> success: DBusResult<Array<u32, Vec<u32>>> {
            let result: Vec<MessageItem> = vec![MessageItem::UInt32(5u32)];
            Ok(MessageItem::Array(result, "(u)".into()))
        }
    }
*/

    f.object_path("/org/way_cooler/pixels", ()).introspectable().add(
        f.interface("org.way_cooler.pixels", ()).add_m(
            f.method("hello", (), |m| {
                // Also can just return a (&*result).into() (a slice) which IS faster...
                //let result = vec![5u8];
                let result: Vec<MessageItem> = vec![MessageItem::UInt32(5u32)];
                Ok(vec![m.msg.method_return().append((MessageItem::Array(result, "(u)".into())))])
            }).outarg::<Array<u32, Vec<u32>>, _>("success")
        )
    )
}
<commit_msg>Capitalized pixels, gotta keep up standards<commit_after>use dbus::arg::{Array};
use dbus::MessageItem;

use super::{DBusFactory, DBusObjPath};

pub fn setup(f: &mut DBusFactory) -> DBusObjPath{
    //let f = Factory::new_fn::<()>();

    // TODO
    // Not working, choking on "1 lifetime parameter" expected for arg to outarg
    // Expanded it myself, seems fine, will have to debug later. For now writing
    // out the boiler plate is not that bad.
    /*
    dbus_interface! {
        path: "/org/way_cooler/pixels";
        name: "org.way_cooler.pixels";

        fn Get() -> success: DBusResult<Array<u32, Vec<u32>>> {
            let result: Vec<MessageItem> = vec![MessageItem::UInt32(5u32)];
            Ok(MessageItem::Array(result, "(u)".into()))
        }
    }
*/

    f.object_path("/org/way_cooler/Pixels", ()).introspectable().add(
        f.interface("org.way_cooler.Pixels", ()).add_m(
            f.method("hello", (), |m| {
                // Also can just return a (&*result).into() (a slice) which IS faster...
                //let result = vec![5u8];
                let result: Vec<MessageItem> = vec![MessageItem::UInt32(5u32)];
                Ok(vec![m.msg.method_return().append((MessageItem::Array(result, "(u)".into())))])
            }).outarg::<Array<u32, Vec<u32>>, _>("success")
        )
    )
}
<|endoftext|>