<commit_before>#![crate_id = "rustic"]
#![feature(phase)]

#[phase(plugin, link)] extern crate log;

use std::io::fs;
use std::io::process::{Command,ExitSignal,ExitStatus};
use std::os;

use tmpdir::TmpDir;

mod child;
mod rand;
mod tmpdir;

fn is_crate(arg: &str) -> bool {
    Path::new(arg).exists()
}

fn main() {
    let args = os::args();
    let args = args.slice_from(1);

    // If `--run` is not in the arguments: pass all the arguments to `rustc`
    if args.iter().all(|arg| arg.as_slice() != "--run") {
        let mut cmd = Command::new("rustc");
        cmd.args(args);

        info!("cwd: . | cmd: `{}`", cmd);
        match cmd.spawn() {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(p) => match child::supplant(p) {
                Err(e) => fail!("`{}` failed: {}", cmd, e),
                Ok(exit) => if !exit.success() {
                    let exit_code = match exit {
                        ExitSignal(code) => code,
                        ExitStatus(code) => code,
                    };

                    os::set_exit_status(exit_code);
                    return;
                },
            },
        }
    }

    // Before `--run`: arguments for the compiler
    let mut splitted_args = args.split(|arg| arg.as_slice() == "--run");
    let rustc_args = splitted_args.next().unwrap();
    let executable_args = splitted_args.next().unwrap();

    // TODO `--crate-type=lib` should also be forbidden
    // XXX What if the crate has a `crate_type=*lib` attribute?
    if rustc_args.iter().any(|arg| arg.as_slice() == "--out-dir") {
        fail!("Can't use both `--out-dir` and `--run` flags at the same time");
    }

    // Separate the crate file from the other arguments
    let crate_arg = match args.iter().find(|arg| {
        is_crate(arg.as_slice())
    }) {
        Some(arg) => arg,
        None => fail!("Didn't find a crate file in the arguments passed"),
    };

    // Use full path for the crate file
    let current_dir = os::getcwd();
    let crate_path = {
        let path = Path::new(crate_arg.as_slice());

        if path.is_absolute() {
            path
        } else {
            current_dir.join(path)
        }
    };

    // Build the rustc command
    let mut cmd = Command::new("rustc");
    for arg in rustc_args.iter().filter(|&arg| arg != crate_arg) {
        cmd.arg(arg.as_slice());
    }
    cmd.arg(crate_path);

    // Create temporary directory
    let tmpdir = TmpDir::new();
    let tmpdir_path = tmpdir.path();
    let tmpdir_display = tmpdir_path.display();

    // Compile
    info!("cwd: {} | cmd: `{}`", tmpdir_display, cmd);
    match cmd.cwd(tmpdir_path).spawn() {
        Err(e) => fail!("`{}` failed: {}", cmd, e),
        Ok(p) => match child::supplant(p) {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(exit) => if !exit.success() {
                let exit_code = match exit {
                    ExitSignal(code) => code,
                    ExitStatus(code) => code,
                };

                os::set_exit_status(exit_code);
                return;
            },
        },
    }

    // Look for the produced binary
    let mut cmd = match fs::readdir(tmpdir_path) {
        Err(e) => fail!("`ls {}` failed: {}", tmpdir_display, e),
        Ok(paths) => match paths.as_slice().get(0) {
            Some(path) => Command::new(path),
            None => fail!("no binary found in {}", tmpdir_display),
        }
    };

    // Build the executable command
    for arg in executable_args.iter().filter(|&arg| arg != crate_arg) {
        cmd.arg(arg.as_slice());
    }

    // Execute
    info!("cwd: . | cmd: `{}`", cmd);
    match cmd.spawn() {
        Err(e) => fail!("`{}` failed: {}", cmd, e),
        Ok(p) => match child::supplant(p) {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(exit) => if !exit.success() {
                let exit_code = match exit {
                    ExitSignal(code) => code,
                    ExitStatus(code) => code,
                };

                os::set_exit_status(exit_code);
                return;
            },
        }
    }
}
<commit_msg>Replace `crate_id` with `crate_name`<commit_after>#![crate_name = "rustic"]
#![feature(phase)]

#[phase(plugin, link)] extern crate log;

use std::io::fs;
use std::io::process::{Command,ExitSignal,ExitStatus};
use std::os;

use tmpdir::TmpDir;

mod child;
mod rand;
mod tmpdir;

fn is_crate(arg: &str) -> bool {
    Path::new(arg).exists()
}

fn main() {
    let args = os::args();
    let args = args.slice_from(1);

    // If `--run` is not in the arguments: pass all the arguments to `rustc`
    if args.iter().all(|arg| arg.as_slice() != "--run") {
        let mut cmd = Command::new("rustc");
        cmd.args(args);

        info!("cwd: . | cmd: `{}`", cmd);
        match cmd.spawn() {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(p) => match child::supplant(p) {
                Err(e) => fail!("`{}` failed: {}", cmd, e),
                Ok(exit) => if !exit.success() {
                    let exit_code = match exit {
                        ExitSignal(code) => code,
                        ExitStatus(code) => code,
                    };

                    os::set_exit_status(exit_code);
                    return;
                },
            },
        }
    }

    // Before `--run`: arguments for the compiler
    let mut splitted_args = args.split(|arg| arg.as_slice() == "--run");
    let rustc_args = splitted_args.next().unwrap();
    let executable_args = splitted_args.next().unwrap();

    // TODO `--crate-type=lib` should also be forbidden
    // XXX What if the crate has a `crate_type=*lib` attribute?
    if rustc_args.iter().any(|arg| arg.as_slice() == "--out-dir") {
        fail!("Can't use both `--out-dir` and `--run` flags at the same time");
    }

    // Separate the crate file from the other arguments
    let crate_arg = match args.iter().find(|arg| {
        is_crate(arg.as_slice())
    }) {
        Some(arg) => arg,
        None => fail!("Didn't find a crate file in the arguments passed"),
    };

    // Use full path for the crate file
    let current_dir = os::getcwd();
    let crate_path = {
        let path = Path::new(crate_arg.as_slice());

        if path.is_absolute() {
            path
        } else {
            current_dir.join(path)
        }
    };

    // Build the rustc command
    let mut cmd = Command::new("rustc");
    for arg in rustc_args.iter().filter(|&arg| arg != crate_arg) {
        cmd.arg(arg.as_slice());
    }
    cmd.arg(crate_path);

    // Create temporary directory
    let tmpdir = TmpDir::new();
    let tmpdir_path = tmpdir.path();
    let tmpdir_display = tmpdir_path.display();

    // Compile
    info!("cwd: {} | cmd: `{}`", tmpdir_display, cmd);
    match cmd.cwd(tmpdir_path).spawn() {
        Err(e) => fail!("`{}` failed: {}", cmd, e),
        Ok(p) => match child::supplant(p) {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(exit) => if !exit.success() {
                let exit_code = match exit {
                    ExitSignal(code) => code,
                    ExitStatus(code) => code,
                };

                os::set_exit_status(exit_code);
                return;
            },
        },
    }

    // Look for the produced binary
    let mut cmd = match fs::readdir(tmpdir_path) {
        Err(e) => fail!("`ls {}` failed: {}", tmpdir_display, e),
        Ok(paths) => match paths.as_slice().get(0) {
            Some(path) => Command::new(path),
            None => fail!("no binary found in {}", tmpdir_display),
        }
    };

    // Build the executable command
    for arg in executable_args.iter().filter(|&arg| arg != crate_arg) {
        cmd.arg(arg.as_slice());
    }

    // Execute
    info!("cwd: . | cmd: `{}`", cmd);
    match cmd.spawn() {
        Err(e) => fail!("`{}` failed: {}", cmd, e),
        Ok(p) => match child::supplant(p) {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(exit) => if !exit.success() {
                let exit_code = match exit {
                    ExitSignal(code) => code,
                    ExitStatus(code) => code,
                };

                os::set_exit_status(exit_code);
                return;
            },
        }
    }
}
<|endoftext|>


<commit_before>/* Stones esoteric programming language
 * (c) 2016 Zack Hixon - see LICENSE.txt */

/** Hey there! This isn't even remotely started yet. I'm gonna need a WHOLE
 *  lot of stuff to make this even *resemble* an interpreter. I also don't
 *  have any real-world experience building interpreters and other stuff, so
 *  really don't expect anything proffessional level. Or good spelling. yeah. */

#[derive(Debug)]
struct Stone {
    x: i32,
    y: i32,
    id: i32,
    color: Color
}

impl Stone {
    fn new(x: i32, y: i32, id: i32, color: Color) -> Stone {
        Stone {
            x: x,
            y: y,
            id: id,
            color: color
        }
    }

    /* only check stones with 0 <= x <= 9 and 0 <= y <= 9 */
    fn empty() -> Stone {
        Stone {
            x: -1,
            y: -1,
            id: -1,
            color: Color::Red
        }
    }
}

#[derive(Debug)]
enum Color {
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Purple
}

fn main() {
    println!("test stone: (debug)");

    let mut test_stone = Stone {
        x: 0,
        y: 0,
        id: 1,
        color: Color::Yellow
    };

    println!("{:?}", test_stone);

    test_stone.x = 1;
    test_stone.y = 1;
    test_stone.id = 2;
    test_stone.color = Color::Blue;

    println!("{:?}", test_stone);

    let mut field: Vec<Vec<Stone>> =
        vec![vec![Stone::new(0, 0, 1, Color::Red),
                  Stone::new(1, 0, 2, Color::Blue),
                  Stone::new(2, 0, 3, Color::Green)],
             vec![Stone::empty(),
                  Stone::empty(),
                  Stone::empty()],
             vec![Stone::empty(),
                  Stone::empty(),
                  Stone::empty()]];

    println!("\nall stones:");
    for row in field {
        for stone in row {
            print!("{} {} {} {:?}\n", stone.x, stone.y, stone.id, stone.color);
        }
        println!("");
    }
}

<commit_msg>add note<commit_after>/* Stones esoteric programming language
 * (c) 2016 Zack Hixon - see LICENSE.txt */

/** NOTE: Hey there! This isn't even remotely started yet. I'm gonna need a
 * WHOLE lot of stuff to make this even *resemble* an interpreter. It also
 * isn't currently up-to-date with the "specification," so it features
 * misleading stuff like the Stone struct right below this, along with the
 * field vector.  Expect a lot of this stuff to change. */

#[derive(Debug)]
struct Stone {
    x: i32,
    y: i32,
    id: i32,
    color: Color
}

impl Stone {
    fn new(x: i32, y: i32, id: i32, color: Color) -> Stone {
        Stone {
            x: x,
            y: y,
            id: id,
            color: color
        }
    }

    /* only check stones with 0 <= x <= 9 and 0 <= y <= 9 */
    fn empty() -> Stone {
        Stone {
            x: -1,
            y: -1,
            id: -1,
            color: Color::Red
        }
    }
}

#[derive(Debug)]
enum Color {
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Purple
}

fn main() {
    println!("test stone: (debug)");

    let mut test_stone = Stone {
        x: 0,
        y: 0,
        id: 1,
        color: Color::Yellow
    };

    println!("{:?}", test_stone);

    test_stone.x = 1;
    test_stone.y = 1;
    test_stone.id = 2;
    test_stone.color = Color::Blue;

    println!("{:?}", test_stone);

    let mut field: Vec<Vec<Stone>> =
        vec![vec![Stone::new(0, 0, 1, Color::Red),
                  Stone::new(1, 0, 2, Color::Blue),
                  Stone::new(2, 0, 3, Color::Green)],
             vec![Stone::empty(),
                  Stone::empty(),
                  Stone::empty()],
             vec![Stone::empty(),
                  Stone::empty(),
                  Stone::empty()]];

    println!("\nall stones:");
    for row in field {
        for stone in row {
            print!("{} {} {} {:?}\n", stone.x, stone.y, stone.id, stone.color);
        }
        println!("");
    }
}

<|endoftext|>


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

/* TODO:
 *  * redirect root url
 *  * add watermark? url to repo?
 *  * update git repo?
 *  * serverside caching? redis?
 */

use std::collections::{HashMap, HashSet};
use std::env;
use std::io::{Write, Read};
use std::process::{Command, Stdio};

use tiny_http::{Header, Response};

extern crate crates_index;
extern crate tiny_http;

static INDEX_LOCAL_PATH: &'static str = "crates.io-index";

fn build_dot(crate_name: &str, dep_map: &HashMap<String, Vec<String>>) -> Vec<u8> {
    let mut crate_names = vec![crate_name];

    let mut dot = String::new();
    dot.push_str("digraph graphname {");
    dot.push_str("ratio=0.75;");

    dot.push_str(&format!("\"{}\" [root=true]", crate_name));

    // Which dependencies we've already seen
    let mut seen_set = HashSet::new();

    while let Some(crate_name) = crate_names.pop() {
        if seen_set.contains(crate_name as &str) {
            continue;
        }
        seen_set.insert(crate_name);
        for crate_dep in dep_map.get(crate_name).unwrap() {
            dot.push_str(&format!("\"{}\" -> \"{}\";", crate_name, crate_dep));
            if !seen_set.contains(crate_dep as &str) {
                crate_names.push(crate_dep);
            }
        }
    }
    dot.push_str("}");

    let child = Command::new("dot").arg("-Tpng").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn().unwrap();
    child.stdin.unwrap().write_all(dot.as_bytes()).unwrap();

    let mut ret = vec![];
    child.stdout.unwrap().read_to_end(&mut ret).unwrap();
    ret
}

fn main() {
    let index = crates_index::CratesIndex::new(INDEX_LOCAL_PATH.into());
    if !index.exists() {
        println!("Cloning crates.io-index");
        index.clone_index();
    }

    let dep_map = index.dependency_map();

    let port = match env::var("PORT") {
        Ok(p) => p.parse::<u16>().unwrap(),
        Err(..) => 8000,
    };

    let server = tiny_http::ServerBuilder::new().with_port(port).build().unwrap();

    println!("Server listening on port {}", port);
    for req in server.incoming_requests() {
        let response = {
            let crate_name = req.url().trim_left_matches("/");
            if dep_map.get(crate_name).is_some() {
                let data = build_dot(crate_name, &dep_map);
                let content_type_header = "Content-Type: image/png".parse::<Header>().unwrap();
                let cache_control_header = "cache-control: no-cache".parse::<Header>().unwrap();
                Response::from_data(data).with_header(content_type_header)
                                         .with_header(cache_control_header)
            } else {
                Response::from_string("could not find crate").with_status_code(400)
            }
        };
        req.respond(response);
    }
}
<commit_msg>Make root node grey<commit_after>// Copyright 2015 Corey Farwell
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/* TODO:
 *  * redirect root url
 *  * add watermark? url to repo?
 *  * update git repo?
 *  * serverside caching? redis?
 */

use std::collections::{HashMap, HashSet};
use std::env;
use std::io::{Write, Read};
use std::process::{Command, Stdio};

use tiny_http::{Header, Response};

extern crate crates_index;
extern crate tiny_http;

static INDEX_LOCAL_PATH: &'static str = "crates.io-index";

fn build_dot(crate_name: &str, dep_map: &HashMap<String, Vec<String>>) -> Vec<u8> {
    let mut crate_names = vec![crate_name];

    let mut dot = String::new();
    dot.push_str("digraph graphname {");
    dot.push_str("ratio=0.75;");

    dot.push_str(&format!("\"{}\" [root=true,style=filled,fillcolor=grey]", crate_name));

    // Which dependencies we've already seen
    let mut seen_set = HashSet::new();

    while let Some(crate_name) = crate_names.pop() {
        if seen_set.contains(crate_name as &str) {
            continue;
        }
        seen_set.insert(crate_name);
        for crate_dep in dep_map.get(crate_name).unwrap() {
            dot.push_str(&format!("\"{}\" -> \"{}\";", crate_name, crate_dep));
            if !seen_set.contains(crate_dep as &str) {
                crate_names.push(crate_dep);
            }
        }
    }
    dot.push_str("}");

    let child = Command::new("dot").arg("-Tpng").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn().unwrap();
    child.stdin.unwrap().write_all(dot.as_bytes()).unwrap();

    let mut ret = vec![];
    child.stdout.unwrap().read_to_end(&mut ret).unwrap();
    ret
}

fn main() {
    let index = crates_index::CratesIndex::new(INDEX_LOCAL_PATH.into());
    if !index.exists() {
        println!("Cloning crates.io-index");
        index.clone_index();
    }

    let dep_map = index.dependency_map();

    let port = match env::var("PORT") {
        Ok(p) => p.parse::<u16>().unwrap(),
        Err(..) => 8000,
    };

    let server = tiny_http::ServerBuilder::new().with_port(port).build().unwrap();

    println!("Server listening on port {}", port);
    for req in server.incoming_requests() {
        let response = {
            let crate_name = req.url().trim_left_matches("/");
            if dep_map.get(crate_name).is_some() {
                let data = build_dot(crate_name, &dep_map);
                let content_type_header = "Content-Type: image/png".parse::<Header>().unwrap();
                let cache_control_header = "cache-control: no-cache".parse::<Header>().unwrap();
                Response::from_data(data).with_header(content_type_header)
                                         .with_header(cache_control_header)
            } else {
                Response::from_string("could not find crate").with_status_code(400)
            }
        };
        req.respond(response);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate rand;

pub mod basetypes;
pub mod chess_move;
pub mod position;
pub mod search;
pub mod uci;
pub mod tt;
pub mod time_manager;

use std::process::exit;
use std::sync::Arc;
use std::time::SystemTime;
use std::collections::VecDeque;
use basetypes::NodeCount;
use tt::{Tt, BOUND_EXACT, BOUND_UPPER, BOUND_LOWER};
use position::Position;
use uci::{UciEngine, UciEngineFactory};
use search::{Variation, SearchStatus, SearchExecutor};
use time_manager::TimeManager;

/// The version of the program.
pub const VERSION: &'static str = "0.1";

// The name of the program.
const NAME: &'static str = "Alcibiades";

// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

// The starting position in Forsyth–Edwards notation (FEN).
const STARTING_POSITION: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(NodeCount), // Stop when the given number of nodes has been searched.
    Depth(u8), // Stop when the given search depth has been reached.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
pub struct Engine {
    tt: Arc<Tt>,
    position: Position,
    current_depth: u8,

    // `Engine` will hand over the real work to `SearchExecutor`.
    search: SearchExecutor,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine how many best lines to calculate and send to
    // the GUI (the first move in each best line is different). This
    // is the so called "MultiPV" mode.
    variation_count: usize,

    // 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,

    // `true` if the engine is thinking in pondering mode.
    is_pondering: bool,

    // Marks the last time when a message was sent by the engine to
    // the GUI, or if no message has been sent yet -- the time when
    // the search stared.
    silent_since: SystemTime,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<uci::EngineReply>,
}

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 = Tt::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);

        Engine {
            tt: tt.clone(),
            position: Position::from_fen(STARTING_POSITION).ok().unwrap(),
            current_depth: 0,
            search: SearchExecutor::new(tt),
            play_when: PlayWhen::Never,
            variation_count: 1,
            pondering_is_allowed: false,
            is_pondering: false,
            silent_since: SystemTime::now(),
            queue: VecDeque::new(),
        }
    }

    // A helper method. It it adds a progress report message to
    // `self.queue`.
    fn queue_progress_report(&mut self) {
        let &SearchStatus { depth, searched_nodes, nps, .. } = self.search.status();
        self.queue.push_back(uci::EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // (multi)PV to `self.queue`.
    fn queue_pv(&mut self) {
        let &SearchStatus { depth, ref variations, searched_nodes, duration_millis, nps, .. } =
            self.search.status();
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(uci::EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("multipv".to_string(), format!("{}", i + 1)),
            ("score".to_string(), format!("cp {}{}", value, bound_suffix)),
            ("time".to_string(), format!("{}", duration_millis)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), moves_string),
        ]));
        }
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // best move to `self.queue`.
    fn queue_best_move(&mut self) {
        let &SearchStatus { ref variations, .. } = self.search.status();
        self.queue.push_back(uci::EngineReply::BestMove {
            best_move: variations[0].moves.get(0).map_or("0000".to_string(), |m| m.notation()),
            ponder_move: variations[0].moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            "MultiPV" => {
                self.variation_count = match value.parse::<usize>().unwrap_or(0) {
                    0 => 1,
                    n if n > 500 => 500,
                    n => n,
                };
            }

            // 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) {
        self.tt.clear();
    }

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

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

        self.search.stop();
        self.tt.new_search();
        self.current_depth = 0;
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(depth.unwrap() as u8)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(&self.position,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.silent_since = SystemTime::now();
        self.search.start(&self.position, searchmoves, self.variation_count);
    }

    fn ponder_hit(&mut self) {
        if self.search.status().done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.search.stop();
        self.queue_best_move();
    }

    fn get_reply(&mut self) -> Option<uci::EngineReply> {
        if !self.search.status().done {
            // Update the search status.
            self.search.update_status();
            let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } = self.search
                                                                                .status();

            // Send the (multi)PV when changed.
            if depth > self.current_depth {
                self.current_depth = depth;
                self.queue_pv();
            }

            // Send periodic progress reports.
            if self.silent_since.elapsed().unwrap().as_secs() > 10 {
                self.queue_progress_report();
            }

            // Register the updated search status with the time manager.
            if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
                tm.update_status(self.search.status());
            }

            // Check if we must play now.
            if !self.is_pondering &&
               match self.play_when {
                PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                PlayWhen::MoveTime(t) => done || duration_millis >= t,
                PlayWhen::Nodes(n) => done || searched_nodes >= n,
                PlayWhen::Depth(d) => done || depth >= d,
                PlayWhen::Never => false,
            } {
                self.stop();
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.search.exit();
    }
}

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

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

    fn author(&self) -> String {
        AUTHOR.to_string()
    }

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

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

fn main() {
    if let Ok(mut uci_loop) = uci::Server::wait_for_hanshake(EngineFactory) {
        match uci_loop.serve() {
            Ok(_) => {
                exit(0);
            }
            Err(_) => {
                exit(1);
            }
        }
    }
    exit(2);
}
<commit_msg>empty line<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate rand;

pub mod basetypes;
pub mod chess_move;
pub mod position;
pub mod search;
pub mod uci;
pub mod tt;
pub mod time_manager;

use std::process::exit;
use std::sync::Arc;
use std::time::SystemTime;
use std::collections::VecDeque;
use basetypes::NodeCount;
use tt::{Tt, BOUND_EXACT, BOUND_UPPER, BOUND_LOWER};
use position::Position;
use uci::{UciEngine, UciEngineFactory};
use search::{Variation, SearchStatus, SearchExecutor};
use time_manager::TimeManager;

/// The version of the program.
pub const VERSION: &'static str = "0.1";

// The name of the program.
const NAME: &'static str = "Alcibiades";

// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

// The starting position in Forsyth–Edwards notation (FEN).
const STARTING_POSITION: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(NodeCount), // Stop when the given number of nodes has been searched.
    Depth(u8), // Stop when the given search depth has been reached.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
pub struct Engine {
    tt: Arc<Tt>,
    position: Position,
    current_depth: u8,

    // `Engine` will hand over the real work to `SearchExecutor`.
    search: SearchExecutor,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine how many best lines to calculate and send to
    // the GUI (the first move in each best line is different). This
    // is the so called "MultiPV" mode.
    variation_count: usize,

    // 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,

    // `true` if the engine is thinking in pondering mode.
    is_pondering: bool,

    // Marks the last time when a message was sent by the engine to
    // the GUI, or if no message has been sent yet -- the time when
    // the search stared.
    silent_since: SystemTime,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<uci::EngineReply>,
}

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 = Tt::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);

        Engine {
            tt: tt.clone(),
            position: Position::from_fen(STARTING_POSITION).ok().unwrap(),
            current_depth: 0,
            search: SearchExecutor::new(tt),
            play_when: PlayWhen::Never,
            variation_count: 1,
            pondering_is_allowed: false,
            is_pondering: false,
            silent_since: SystemTime::now(),
            queue: VecDeque::new(),
        }
    }

    // A helper method. It it adds a progress report message to
    // `self.queue`.
    fn queue_progress_report(&mut self) {
        let &SearchStatus { depth, searched_nodes, nps, .. } = self.search.status();
        self.queue.push_back(uci::EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // (multi)PV to `self.queue`.
    fn queue_pv(&mut self) {
        let &SearchStatus { depth, ref variations, searched_nodes, duration_millis, nps, .. } =
            self.search.status();
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(uci::EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("multipv".to_string(), format!("{}", i + 1)),
            ("score".to_string(), format!("cp {}{}", value, bound_suffix)),
            ("time".to_string(), format!("{}", duration_millis)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), moves_string),
        ]));
        }
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // best move to `self.queue`.
    fn queue_best_move(&mut self) {
        let &SearchStatus { ref variations, .. } = self.search.status();
        self.queue.push_back(uci::EngineReply::BestMove {
            best_move: variations[0].moves.get(0).map_or("0000".to_string(), |m| m.notation()),
            ponder_move: variations[0].moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            "MultiPV" => {
                self.variation_count = match value.parse::<usize>().unwrap_or(0) {
                    0 => 1,
                    n if n > 500 => 500,
                    n => n,
                };
            }

            // 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) {
        self.tt.clear();
    }

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

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

        self.search.stop();
        self.tt.new_search();
        self.current_depth = 0;
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(depth.unwrap() as u8)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(&self.position,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.silent_since = SystemTime::now();
        self.search.start(&self.position, searchmoves, self.variation_count);
    }

    fn ponder_hit(&mut self) {
        if self.search.status().done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.search.stop();
        self.queue_best_move();
    }

    fn get_reply(&mut self) -> Option<uci::EngineReply> {
        if !self.search.status().done {
            // Update the search status.
            self.search.update_status();
            let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } = self.search
                                                                                .status();
            // Send the (multi)PV when changed.
            if depth > self.current_depth {
                self.current_depth = depth;
                self.queue_pv();
            }

            // Send periodic progress reports.
            if self.silent_since.elapsed().unwrap().as_secs() > 10 {
                self.queue_progress_report();
            }

            // Register the updated search status with the time manager.
            if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
                tm.update_status(self.search.status());
            }

            // Check if we must play now.
            if !self.is_pondering &&
               match self.play_when {
                PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                PlayWhen::MoveTime(t) => done || duration_millis >= t,
                PlayWhen::Nodes(n) => done || searched_nodes >= n,
                PlayWhen::Depth(d) => done || depth >= d,
                PlayWhen::Never => false,
            } {
                self.stop();
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.search.exit();
    }
}

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

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

    fn author(&self) -> String {
        AUTHOR.to_string()
    }

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

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

fn main() {
    if let Ok(mut uci_loop) = uci::Server::wait_for_hanshake(EngineFactory) {
        match uci_loop.serve() {
            Ok(_) => {
                exit(0);
            }
            Err(_) => {
                exit(1);
            }
        }
    }
    exit(2);
}
<|endoftext|>


<commit_before>extern crate sdl2;
extern crate rand;

use std::thread;
use std::time::Duration;
use std::path::Path;
use std::io::BufReader;
use std::io::prelude::*;
use std::fs::File;
use std::env;

use sdl2::pixels::Color;
use sdl2::event::Event;
use sdl2::keyboard::{Keycode, Scancode};
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};
use sdl2::surface::Surface;

use rand::{thread_rng, Rng};

struct Font {
    texture: Texture,
    characters: [Character; 256],
}

#[derive(Copy, Clone, PartialEq)]
struct Character {
    x: u32,
    width: u32,
}

impl Font {
    fn load_bmp<P: AsRef<Path>>(renderer: &mut Renderer, path: P) -> Font {
        let mut characters = [Character { x: 0, width: 0 }; 256];
        let mut surface = Surface::load_bmp(path).unwrap();
        surface.with_lock(|pixels| {
            let mut count = 0;
            let mut last_x = 0;

            for (x, color) in pixels[3 as usize..(surface.width() * 3) as usize]
                .chunks(3)
                .enumerate() {
                if color == [255, 0, 255] {
                    characters[count].x = (last_x + 1) as u32;
                    characters[count].width = (x - last_x) as u32;
                    count += 1;
                    last_x = x + 1;
                }
            }
        });
        surface.set_color_key(true, Color::RGB(211, 203, 207)).unwrap();
        Font {
            texture: renderer.create_texture_from_surface(surface).unwrap(),
            characters: characters,
        }
    }

    fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, text: &str) {
        let mut position = x;
        for byte in text.bytes() {
            position += if byte == ' ' as u8 {
                2
            } else {
                let character = self.characters[byte as usize - '!' as usize];
                renderer.copy(&self.texture,
                              Some(Rect::new(character.x as i32, 0, character.width, 10)),
                              Some(Rect::new(position, y, character.width, 9)));
                character.width as i32
            }
        }
    }
}

#[derive(Copy, Clone, PartialEq)]
enum Direction {
    Up,
    Right,
    Down,
    Left,
}

impl Direction {
    fn opposite(self) -> Direction {
        match self {
            Direction::Up => Direction::Down,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
            Direction::Left => Direction::Right,
        }
    }
}

#[derive(Copy, Clone, PartialEq)]
enum Tile {
    Empty,
    Wall(u8),
    Food,
    SnakeVertical,
    SnakeHorizontal,
    SnakeTurn(Direction, bool),
    SnakeHead(Direction),
    SnakeTail(Direction),
}

struct Map {
    name: String,
    tiles: [[Tile; 23]; 32],
    snake_x: usize,
    snake_y: usize,
}

impl Map {
    fn new() -> Map {
        Map {
            name: "Default".to_string(),
            tiles: [[Tile::Empty; 23]; 32],
            snake_x: 5,
            snake_y: 5,
        }
    }

    fn load<P: AsRef<Path>>(path: P) -> Map {
        let f = BufReader::new(File::open(path).unwrap());
        let mut lines = f.lines();
        let name = lines.next().unwrap().unwrap();
        let mut tiles = [[Tile::Empty; 23]; 32];
        let mut snake_x = 0;
        let mut snake_y = 0;
        for (y, line) in lines.enumerate() {
            for (x, c) in line.unwrap().chars().enumerate() {
                 match c {
                    'X' => tiles[x][y] = Tile::Wall(0),
                    ' ' => tiles[x][y] = Tile::Empty,
                    '@' => { snake_x = x; snake_y = y; },
                    _ => unreachable!(),
                }
            }
        }
        for x in 0..32 {
            for y in 0..23 {
                match tiles[x][y] {
                    Tile::Wall(i) => {
                        let mut new_i = i;
                        if y == 0 || match tiles[x][y - 1] { Tile::Wall(_) => true, _ => false } {
                            new_i += 1;
                        }
                        if x == 32 - 1 || match tiles[x + 1][y] { Tile::Wall(_) => true, _ => false } {
                            new_i += 2;
                        }
                        if y == 23 - 1 || match tiles[x][y + 1] { Tile::Wall(_) => true, _ => false } {
                            new_i += 4;
                        }
                        if x == 0 || match tiles[x - 1][y] { Tile::Wall(_) => true, _ => false } {
                            new_i += 8;
                        }
                        tiles[x][y] = Tile::Wall(new_i);
                    }
                    _ => {}
                }
            }
        }
        Map {
            name: name,
            tiles: tiles,
            snake_x: snake_x,
            snake_y: snake_y,
        }
    }
}

struct SnakeEnd {
    x: i32,
    y: i32,
    direction: Direction,
}

impl SnakeEnd {
    fn update(&mut self) {
        match self.direction {
            Direction::Up => {
                if self.y == 0 {
                    self.y = 23 - 1;
                } else {
                    self.y -= 1;
                }
            }
            Direction::Right => {
                if self.x == 32 - 1 {
                    self.x = 0;
                } else {
                    self.x += 1;
                }
            }
            Direction::Down => {
                if self.y == 23 - 1 {
                    self.y = 0;
                } else {
                    self.y += 1;
                }
            }
            Direction::Left => {
                if self.x == 0 {
                    self.x = 32 - 1;
                } else {
                    self.x -= 1;
                }
            }
        }
    }
}

struct Snake {
    head: SnakeEnd,
    tail: SnakeEnd,
    grow: u8,
}

impl Snake {
    fn new(x: i32, y: i32, direction: Direction) -> Snake {
        Snake {
            head: SnakeEnd {
                x: x,
                y: y,
                direction: direction,
            },
            tail: SnakeEnd {
                x: x,
                y: y,
                direction: direction,
            },
            grow: 10,
        }
    }
}

fn update_tile(renderer: &mut Renderer, tiles: &Texture, map: &Map, x: i32, y: i32) {
    let target_rect = Rect::new(x * 10, 10 + y * 10, 10, 10);
    match map.tiles[x as usize][y as usize] {
        Tile::Empty => {
            renderer.set_draw_color(Color::RGB(255, 255, 255));
            renderer.fill_rect(target_rect).unwrap();
        }
        tile => {
            renderer.copy(&tiles,
                          Some(Rect::new(match tile {
                                             Tile::Wall(i) => 150 + 10 * i as i32,
                                             Tile::Food => 140,
                                             Tile::SnakeVertical => 120,
                                             Tile::SnakeHorizontal => 130,
                                             Tile::SnakeTail(Direction::Up) => 60,
                                             Tile::SnakeTail(Direction::Right) => 70,
                                             Tile::SnakeTail(Direction::Down) => 40,
                                             Tile::SnakeTail(Direction::Left) => 50,
                                             Tile::SnakeHead(Direction::Up) => 0,
                                             Tile::SnakeHead(Direction::Right) => 10,
                                             Tile::SnakeHead(Direction::Down) => 20,
                                             Tile::SnakeHead(Direction::Left) => 30,
                                             Tile::SnakeTurn(Direction::Up, false) |
                                             Tile::SnakeTurn(Direction::Left, true) => 110,
                                             Tile::SnakeTurn(Direction::Right, false) |
                                             Tile::SnakeTurn(Direction::Up, true) => 80,
                                             Tile::SnakeTurn(Direction::Down, false) |
                                             Tile::SnakeTurn(Direction::Right, true) => 90,
                                             Tile::SnakeTurn(Direction::Left, false) |
                                             Tile::SnakeTurn(Direction::Down, true) => 100,
                                             _ => unreachable!(),
                                         },
                                         0,
                                         10,
                                         10)),
                          Some(target_rect));
        }
    }
}

fn place_food(map: &mut Map, renderer: &mut Renderer, tiles: &Texture) {
    let mut rng = thread_rng();
    loop {
        let (x, y) = (rng.gen_range(0, 32), rng.gen_range(0, 23));
        if map.tiles[x as usize][y as usize] != Tile::Empty {
            continue;
        }
        map.tiles[x as usize][y as usize] = Tile::Food;
        update_tile(renderer, &tiles, &map, x, y);
        break;
    }
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Natrix", 320, 240)
        .position_centered()
        .build()
        .unwrap();

    let mut renderer = window.renderer().present_vsync().build().unwrap();

    let tiles = renderer.create_texture_from_surface(Surface::load_bmp("data/images/tiles.bmp").unwrap())
        .unwrap();

    let font = Font::load_bmp(&mut renderer, "data/images/NeoSans.bmp");

    let mut event_pump = sdl_context.event_pump().unwrap();

    let mut map = if let Some(path) = env::args().nth(1) {
        Map::load(path)
    } else {
        Map::new()
    };

    let mut snake = Snake::new(map.snake_x as i32, map.snake_y as i32, Direction::Right);

    let mut score = 0;

    renderer.set_draw_color(Color::RGB(255, 255, 255));
    renderer.clear();

    renderer.set_draw_color(Color::RGB(0, 0, 0));
    renderer.fill_rect(Rect::new(0, 0, 320, 10)).unwrap();
    font.draw(&mut renderer, 1, 0, &format!("Score: {}", score));
    font.draw(&mut renderer, 100, 0, &map.name);

    for x in 0..32 {
        for y in 0..23 {
            update_tile(&mut renderer, &tiles, &map, x, y);
        }
    }

    place_food(&mut map, &mut renderer, &tiles);

    'running: loop {
        let mut next_direction = snake.head.direction;

        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } |
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => break 'running,
                Event::KeyDown { scancode: Some(scancode), .. } => {
                    next_direction = match scancode {
                        Scancode::W => Direction::Up,
                        Scancode::D => Direction::Right,
                        Scancode::S => Direction::Down,
                        Scancode::A => Direction::Left,
                        _ => next_direction,
                    }
                }
                _ => {}
            }
        }

        if snake.grow > 0 {
            snake.grow -= 1;
        } else {
            map.tiles[snake.tail.x as usize][snake.tail.y as usize] = Tile::Empty;

            update_tile(&mut renderer, &tiles, &map, snake.tail.x, snake.tail.y);

            snake.tail.update();

            match map.tiles[snake.tail.x as usize][snake.tail.y as usize] {
                Tile::SnakeTurn(direction, _) => snake.tail.direction = direction,
                Tile::SnakeVertical | Tile::SnakeHorizontal => {}
                _ => unreachable!(),
            };

            map.tiles[snake.tail.x as usize][snake.tail.y as usize] = Tile::SnakeTail(snake.tail
                .direction);

            update_tile(&mut renderer, &tiles, &map, snake.tail.x, snake.tail.y);
        }

        if snake.head.direction != next_direction &&
           next_direction.opposite() != snake.head.direction {
            map.tiles[snake.head.x as usize][snake.head.y as usize] =
                Tile::SnakeTurn(next_direction,
                                match (snake.head.direction, next_direction) {
                                    (Direction::Right, Direction::Down) |
                                    (Direction::Down, Direction::Left) |
                                    (Direction::Left, Direction::Up) |
                                    (Direction::Up, Direction::Right) => true,
                                    _ => false,
                                });
            snake.head.direction = next_direction;
        } else {
            map.tiles[snake.head.x as usize][snake.head.y as usize] = match snake.head.direction {
                Direction::Up | Direction::Down => Tile::SnakeVertical,
                Direction::Right | Direction::Left => Tile::SnakeHorizontal,
            };
        }

        update_tile(&mut renderer, &tiles, &map, snake.head.x, snake.head.y);

        snake.head.update();

        match map.tiles[snake.head.x as usize][snake.head.y as usize] {
            Tile::Food => {
                place_food(&mut map, &mut renderer, &tiles);
                snake.grow += 5;
                score += 1;
                font.draw(&mut renderer, 1, 0, &format!("Score: {}", score));
            }
            Tile::Wall(_) |
            Tile::SnakeVertical |
            Tile::SnakeHorizontal |
            Tile::SnakeTurn(_, _) |
            Tile::SnakeTail(_) => panic!("You die!"),
            _ => {}
        }

        map.tiles[snake.head.x as usize][snake.head.y as usize] = Tile::SnakeHead(snake.head.direction);

        update_tile(&mut renderer, &tiles, &map, snake.head.x, snake.head.y);

        renderer.present();

        thread::sleep(Duration::from_millis(100));
    }
}
<commit_msg>Allow restart after game over<commit_after>extern crate sdl2;
extern crate rand;

use std::thread;
use std::time::Duration;
use std::path::Path;
use std::io::BufReader;
use std::io::prelude::*;
use std::fs::File;
use std::env;

use sdl2::pixels::Color;
use sdl2::event::Event;
use sdl2::keyboard::{Keycode, Scancode};
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};
use sdl2::surface::Surface;

use rand::{thread_rng, Rng};

struct Font {
    texture: Texture,
    characters: [Character; 256],
}

#[derive(Copy, Clone, PartialEq)]
struct Character {
    x: u32,
    width: u32,
}

impl Font {
    fn load_bmp<P: AsRef<Path>>(renderer: &mut Renderer, path: P) -> Font {
        let mut characters = [Character { x: 0, width: 0 }; 256];
        let mut surface = Surface::load_bmp(path).unwrap();
        surface.with_lock(|pixels| {
            let mut count = 0;
            let mut last_x = 0;

            for (x, color) in pixels[3 as usize..(surface.width() * 3) as usize]
                .chunks(3)
                .enumerate() {
                if color == [255, 0, 255] {
                    characters[count].x = (last_x + 1) as u32;
                    characters[count].width = (x - last_x) as u32;
                    count += 1;
                    last_x = x + 1;
                }
            }
        });
        surface.set_color_key(true, Color::RGB(211, 203, 207)).unwrap();
        Font {
            texture: renderer.create_texture_from_surface(surface).unwrap(),
            characters: characters,
        }
    }

    fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, text: &str) {
        let mut position = x;
        for byte in text.bytes() {
            position += if byte == ' ' as u8 {
                2
            } else {
                let character = self.characters[byte as usize - '!' as usize];
                renderer.copy(&self.texture,
                              Some(Rect::new(character.x as i32, 0, character.width, 10)),
                              Some(Rect::new(position, y, character.width, 9)));
                character.width as i32
            }
        }
    }
}

#[derive(Copy, Clone, PartialEq)]
enum Direction {
    Up,
    Right,
    Down,
    Left,
}

impl Direction {
    fn opposite(self) -> Direction {
        match self {
            Direction::Up => Direction::Down,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
            Direction::Left => Direction::Right,
        }
    }
}

#[derive(Copy, Clone, PartialEq)]
enum Tile {
    Empty,
    Wall(u8),
    Food,
    SnakeVertical,
    SnakeHorizontal,
    SnakeTurn(Direction, bool),
    SnakeHead(Direction),
    SnakeTail(Direction),
}

#[derive(Clone)]
struct Map {
    name: String,
    tiles: [[Tile; 23]; 32],
    snake_x: usize,
    snake_y: usize,
}

impl Map {
    fn new() -> Map {
        Map {
            name: "Default".to_string(),
            tiles: [[Tile::Empty; 23]; 32],
            snake_x: 5,
            snake_y: 5,
        }
    }

    fn load<P: AsRef<Path>>(path: P) -> Map {
        let f = BufReader::new(File::open(path).unwrap());
        let mut lines = f.lines();
        let name = lines.next().unwrap().unwrap();
        let mut tiles = [[Tile::Empty; 23]; 32];
        let mut snake_x = 0;
        let mut snake_y = 0;
        for (y, line) in lines.enumerate() {
            for (x, c) in line.unwrap().chars().enumerate() {
                 match c {
                    'X' => tiles[x][y] = Tile::Wall(0),
                    ' ' => tiles[x][y] = Tile::Empty,
                    '@' => { snake_x = x; snake_y = y; },
                    _ => unreachable!(),
                }
            }
        }
        for x in 0..32 {
            for y in 0..23 {
                match tiles[x][y] {
                    Tile::Wall(i) => {
                        let mut new_i = i;
                        if y == 0 || match tiles[x][y - 1] { Tile::Wall(_) => true, _ => false } {
                            new_i += 1;
                        }
                        if x == 32 - 1 || match tiles[x + 1][y] { Tile::Wall(_) => true, _ => false } {
                            new_i += 2;
                        }
                        if y == 23 - 1 || match tiles[x][y + 1] { Tile::Wall(_) => true, _ => false } {
                            new_i += 4;
                        }
                        if x == 0 || match tiles[x - 1][y] { Tile::Wall(_) => true, _ => false } {
                            new_i += 8;
                        }
                        tiles[x][y] = Tile::Wall(new_i);
                    }
                    _ => {}
                }
            }
        }
        Map {
            name: name,
            tiles: tiles,
            snake_x: snake_x,
            snake_y: snake_y,
        }
    }
}

struct SnakeEnd {
    x: i32,
    y: i32,
    direction: Direction,
}

impl SnakeEnd {
    fn update(&mut self) {
        match self.direction {
            Direction::Up => {
                if self.y == 0 {
                    self.y = 23 - 1;
                } else {
                    self.y -= 1;
                }
            }
            Direction::Right => {
                if self.x == 32 - 1 {
                    self.x = 0;
                } else {
                    self.x += 1;
                }
            }
            Direction::Down => {
                if self.y == 23 - 1 {
                    self.y = 0;
                } else {
                    self.y += 1;
                }
            }
            Direction::Left => {
                if self.x == 0 {
                    self.x = 32 - 1;
                } else {
                    self.x -= 1;
                }
            }
        }
    }
}

struct Snake {
    head: SnakeEnd,
    tail: SnakeEnd,
    grow: u8,
}

impl Snake {
    fn new(x: i32, y: i32, direction: Direction) -> Snake {
        Snake {
            head: SnakeEnd {
                x: x,
                y: y,
                direction: direction,
            },
            tail: SnakeEnd {
                x: x,
                y: y,
                direction: direction,
            },
            grow: 10,
        }
    }
}

fn update_tile(renderer: &mut Renderer, tiles: &Texture, map: &Map, x: i32, y: i32) {
    let target_rect = Rect::new(x * 10, 10 + y * 10, 10, 10);
    match map.tiles[x as usize][y as usize] {
        Tile::Empty => {
            renderer.set_draw_color(Color::RGB(255, 255, 255));
            renderer.fill_rect(target_rect).unwrap();
        }
        tile => {
            renderer.copy(&tiles,
                          Some(Rect::new(match tile {
                                             Tile::Wall(i) => 150 + 10 * i as i32,
                                             Tile::Food => 140,
                                             Tile::SnakeVertical => 120,
                                             Tile::SnakeHorizontal => 130,
                                             Tile::SnakeTail(Direction::Up) => 60,
                                             Tile::SnakeTail(Direction::Right) => 70,
                                             Tile::SnakeTail(Direction::Down) => 40,
                                             Tile::SnakeTail(Direction::Left) => 50,
                                             Tile::SnakeHead(Direction::Up) => 0,
                                             Tile::SnakeHead(Direction::Right) => 10,
                                             Tile::SnakeHead(Direction::Down) => 20,
                                             Tile::SnakeHead(Direction::Left) => 30,
                                             Tile::SnakeTurn(Direction::Up, false) |
                                             Tile::SnakeTurn(Direction::Left, true) => 110,
                                             Tile::SnakeTurn(Direction::Right, false) |
                                             Tile::SnakeTurn(Direction::Up, true) => 80,
                                             Tile::SnakeTurn(Direction::Down, false) |
                                             Tile::SnakeTurn(Direction::Right, true) => 90,
                                             Tile::SnakeTurn(Direction::Left, false) |
                                             Tile::SnakeTurn(Direction::Down, true) => 100,
                                             _ => unreachable!(),
                                         },
                                         0,
                                         10,
                                         10)),
                          Some(target_rect));
        }
    }
}

fn place_food(map: &mut Map, renderer: &mut Renderer, tiles: &Texture) {
    let mut rng = thread_rng();
    loop {
        let (x, y) = (rng.gen_range(0, 32), rng.gen_range(0, 23));
        if map.tiles[x as usize][y as usize] != Tile::Empty {
            continue;
        }
        map.tiles[x as usize][y as usize] = Tile::Food;
        update_tile(renderer, &tiles, &map, x, y);
        break;
    }
}

struct Game {
    snake: Snake,
    score: u32,
    snake_alive: bool,
    snake_show: bool,
    map: Map,
}

impl Game {
    fn new(renderer: &mut Renderer, font: &Font, tiles: &Texture, map: &Map) -> Game {
        let mut game = Game {
            snake: Snake::new(map.snake_x as i32, map.snake_y as i32, Direction::Right),
            snake_alive: true,
            snake_show: true,
            score: 0,
            map: map.clone(),
        };

        renderer.set_draw_color(Color::RGB(255, 255, 255));
        renderer.clear();

        renderer.set_draw_color(Color::RGB(0, 0, 0));
        renderer.fill_rect(Rect::new(0, 0, 320, 10)).unwrap();
        font.draw(renderer, 1, 0, &format!("Score: {}", game.score));
        font.draw(renderer, 100, 0, &game.map.name);

        for x in 0..32 {
            for y in 0..23 {
                update_tile(renderer, &tiles, &game.map, x, y);
            }
        }

        place_food(&mut game.map, renderer, &tiles);

        game
    }
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Natrix", 320, 240)
        .position_centered()
        .build()
        .unwrap();

    let mut renderer = window.renderer().present_vsync().build().unwrap();

    let tiles = renderer.create_texture_from_surface(Surface::load_bmp("data/images/tiles.bmp").unwrap())
        .unwrap();

    let font = Font::load_bmp(&mut renderer, "data/images/NeoSans.bmp");

    let mut event_pump = sdl_context.event_pump().unwrap();

    let map = if let Some(path) = env::args().nth(1) {
        Map::load(path)
    } else {
        Map::new()
    };

    let mut game = Game::new(&mut renderer, &font, &tiles, &map);

    'running: loop {
        let mut next_direction = game.snake.head.direction;

        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } |
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => break 'running,
                Event::KeyDown { scancode: Some(scancode), .. } => {
                    if game.snake_alive {
                        next_direction = match scancode {
                            Scancode::W => Direction::Up,
                            Scancode::D => Direction::Right,
                            Scancode::S => Direction::Down,
                            Scancode::A => Direction::Left,
                            _ => next_direction,
                        }
                    } else {
                        if scancode == Scancode::R {
                            game = Game::new(&mut renderer, &font, &tiles, &map);
                            continue 'running;
                        }
                    }
                }
                _ => {}
            }
        }

        if game.snake_alive {
            if game.snake.grow > 0 {
                game.snake.grow -= 1;
            } else {
                game.map.tiles[game.snake.tail.x as usize][game.snake.tail.y as usize] = Tile::Empty;

                update_tile(&mut renderer, &tiles, &game.map, game.snake.tail.x, game.snake.tail.y);

                game.snake.tail.update();

                match game.map.tiles[game.snake.tail.x as usize][game.snake.tail.y as usize] {
                    Tile::SnakeTurn(direction, _) => game.snake.tail.direction = direction,
                    Tile::SnakeVertical | Tile::SnakeHorizontal => {}
                    _ => unreachable!(),
                };

                game.map.tiles[game.snake.tail.x as usize][game.snake.tail.y as usize] = Tile::SnakeTail(game.snake.tail
                    .direction);

                update_tile(&mut renderer, &tiles, &game.map, game.snake.tail.x, game.snake.tail.y);
            }

            if game.snake.head.direction != next_direction &&
               next_direction.opposite() != game.snake.head.direction {
                game.map.tiles[game.snake.head.x as usize][game.snake.head.y as usize] =
                    Tile::SnakeTurn(next_direction,
                                    match (game.snake.head.direction, next_direction) {
                                        (Direction::Right, Direction::Down) |
                                        (Direction::Down, Direction::Left) |
                                        (Direction::Left, Direction::Up) |
                                        (Direction::Up, Direction::Right) => true,
                                        _ => false,
                                    });
                game.snake.head.direction = next_direction;
            } else {
                game.map.tiles[game.snake.head.x as usize][game.snake.head.y as usize] = match game.snake.head.direction {
                    Direction::Up | Direction::Down => Tile::SnakeVertical,
                    Direction::Right | Direction::Left => Tile::SnakeHorizontal,
                };
            }

            update_tile(&mut renderer, &tiles, &game.map, game.snake.head.x, game.snake.head.y);

            game.snake.head.update();

            match game.map.tiles[game.snake.head.x as usize][game.snake.head.y as usize] {
                Tile::Food => {
                    place_food(&mut game.map, &mut renderer, &tiles);
                    game.snake.grow += 5;
                    game.score += 1;
                    font.draw(&mut renderer, 1, 0, &format!("Score: {}", game.score));
                }
                Tile::Wall(_) |
                Tile::SnakeVertical |
                Tile::SnakeHorizontal |
                Tile::SnakeTurn(_, _) |
                Tile::SnakeTail(_) => {
                    game.snake_alive = false;
                }
                _ => {}
            }

            if game.snake_alive {
                game.map.tiles[game.snake.head.x as usize][game.snake.head.y as usize] = Tile::SnakeHead(game.snake.head.direction);
                update_tile(&mut renderer, &tiles, &game.map, game.snake.head.x, game.snake.head.y);
            }
        } else {
            for x in 0..32 {
                for y in 0..23 {
                    if !game.snake_show && match game.map.tiles[x as usize][y as usize] {
                        Tile::SnakeVertical | Tile::SnakeHorizontal | Tile::SnakeTurn(_, _) | Tile::SnakeTail(_) => true,
                        _ => false,
                    } {
                        let target_rect = Rect::new(x * 10, 10 + y * 10, 10, 10);
                        renderer.set_draw_color(Color::RGB(255, 255, 255));
                        renderer.fill_rect(target_rect).unwrap();
                    } else {
                        update_tile(&mut renderer, &tiles, &game.map, x, y);
                    }
                }
            }
            if game.snake_show {
                font.draw(&mut renderer, 200, 0, "Press R to restart");
            } else {
                renderer.set_draw_color(Color::RGB(0, 0, 0));
                renderer.fill_rect(Rect::new(200, 0, 120, 10)).unwrap();
            }
            game.snake_show = !game.snake_show;
        }

        renderer.present();

        thread::sleep(Duration::from_millis(100));
    }
}
<|endoftext|>


<commit_before>#![crate_name = "rboy"]

extern crate clap;
extern crate cpal;
extern crate glium;
extern crate rboy;

use glium::DisplayBuild;
use rboy::device::Device;
use std::sync::mpsc::{self, Receiver, SyncSender, TryRecvError, TrySendError};
use std::thread;
use std::error::Error;

const EXITCODE_SUCCESS : i32 = 0;
const EXITCODE_CPULOADFAILS : i32 = 2;

#[derive(Default)]
struct RenderOptions {
    pub linear_interpolation: bool,
}

enum GBEvent {
    KeyUp(rboy::KeypadKey),
    KeyDown(rboy::KeypadKey),
    SpeedUp,
    SpeedDown,
}

fn main() {
    let exit_status = real_main();
    if exit_status != EXITCODE_SUCCESS {
        std::process::exit(exit_status);
    }
}

fn real_main() -> i32 {
    let matches = clap::App::new("rboy")
        .version("0.1")
        .author("Mathijs van de Nes")
        .about("A Gameboy Colour emulator written in Rust")
        .arg(clap::Arg::with_name("filename")
             .help("Sets the ROM file to load")
             .required(true))
        .arg(clap::Arg::with_name("serial")
             .help("Prints the data from the serial port to stdout")
             .short("s")
             .long("serial"))
        .arg(clap::Arg::with_name("classic")
             .help("Forces the emulator to run in classic Gameboy mode")
             .short("c")
             .long("classic"))
        .arg(clap::Arg::with_name("scale")
             .help("Sets the scale of the interface. Default: 2")
             .short("x")
             .long("scale")
             .validator(|s|
                 match s.parse::<u32>() {
                     Err(e) => Err(format!("Could not parse scale: {}", e.description())),
                     Ok(s) if s < 1 => Err("Scale must be at least 1".to_owned()),
                     Ok(s) if s > 8 => Err("Scale may be at most 8".to_owned()),
                     Ok(..) => Ok(()),
                 })
             .takes_value(true))
        .arg(clap::Arg::with_name("audio")
             .help("Enables audio")
             .short("a")
             .long("audio"))
        .get_matches();

    let opt_serial = matches.is_present("serial");
    let opt_classic = matches.is_present("classic");
    let opt_audio = matches.is_present("audio");
    let filename = matches.value_of("filename").unwrap();
    let scale = matches.value_of("scale").unwrap_or("2").parse::<u32>().unwrap();

    let cpu = construct_cpu(filename, opt_classic, opt_serial);
    if cpu.is_none() { return EXITCODE_CPULOADFAILS; }
    let mut cpu = cpu.unwrap();
    if opt_audio {
        let player = CpalPlayer::get();
        match player {
            Some(v) => cpu.enable_audio(Box::new(v) as Box<rboy::AudioPlayer>),
            None => { warn("Could not open audio device"); return EXITCODE_CPULOADFAILS; },
        }
    }
    let romname = cpu.romname();

    let (sender1, receiver1) = mpsc::channel();
    let (sender2, receiver2) = mpsc::sync_channel(1);

    let display = glium::glutin::WindowBuilder::new()
        .with_dimensions(rboy::SCREEN_W as u32 * scale, rboy::SCREEN_H as u32 * scale)
        .with_title("RBoy - ".to_owned() + &romname)
        .build_glium()
        .unwrap();

    let mut texture = glium::texture::texture2d::Texture2d::empty_with_format(
            &display,
            glium::texture::UncompressedFloatFormat::U8U8U8,
            glium::texture::MipmapsOption::NoMipmap,
            rboy::SCREEN_W as u32,
            rboy::SCREEN_H as u32)
        .unwrap();

    let mut renderoptions = <RenderOptions as Default>::default();

    let cputhread = thread::spawn(move|| run_cpu(cpu, sender2, receiver1));

    'main : loop {
        for ev in display.poll_events() {
            use glium::glutin::Event;
            use glium::glutin::ElementState::{Pressed, Released};
            use glium::glutin::VirtualKeyCode;

            match ev {
                Event::Closed
                    => break 'main,
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::Escape))
                    => break 'main,
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::Key1))
                    => display.get_window().unwrap().set_inner_size(rboy::SCREEN_W as u32, rboy::SCREEN_H as u32),
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::R))
                    => display.get_window().unwrap().set_inner_size(rboy::SCREEN_W as u32 * scale, rboy::SCREEN_H as u32 * scale),
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::LShift))
                    => { let _ = sender1.send(GBEvent::SpeedUp); },
                Event::KeyboardInput(Released, _, Some(VirtualKeyCode::LShift))
                    => { let _ = sender1.send(GBEvent::SpeedDown); },
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::T))
                    => { renderoptions.linear_interpolation = !renderoptions.linear_interpolation; }
                Event::KeyboardInput(Pressed, _, Some(glutinkey)) => {
                    if let Some(key) = glutin_to_keypad(glutinkey) {
                        let _ = sender1.send(GBEvent::KeyDown(key));
                    }
                },
                Event::KeyboardInput(Released, _, Some(glutinkey)) => {
                    if let Some(key) = glutin_to_keypad(glutinkey) {
                        let _ = sender1.send(GBEvent::KeyUp(key));
                    }
                },
                _ => (),
            }
        }

        match receiver2.try_recv() {
            Ok(data) => recalculate_screen(&display, &mut texture, &*data, &renderoptions),
            Err(TryRecvError::Empty) => (),
            Err(TryRecvError::Disconnected) => break 'main,
        }
    }

    drop(sender1);
    let _ = cputhread.join();

    EXITCODE_SUCCESS
}

fn glutin_to_keypad(key: glium::glutin::VirtualKeyCode) -> Option<rboy::KeypadKey> {
    use glium::glutin::VirtualKeyCode;
    match key {
        VirtualKeyCode::Z => Some(rboy::KeypadKey::A),
        VirtualKeyCode::X => Some(rboy::KeypadKey::B),
        VirtualKeyCode::Up => Some(rboy::KeypadKey::Up),
        VirtualKeyCode::Down => Some(rboy::KeypadKey::Down),
        VirtualKeyCode::Left => Some(rboy::KeypadKey::Left),
        VirtualKeyCode::Right => Some(rboy::KeypadKey::Right),
        VirtualKeyCode::Space => Some(rboy::KeypadKey::Select),
        VirtualKeyCode::Return => Some(rboy::KeypadKey::Start),
        _ => None,
    }
}

fn recalculate_screen(display: &glium::backend::glutin_backend::GlutinFacade,
                      texture: &mut glium::texture::texture2d::Texture2d,
                      datavec: &[u8],
                      renderoptions: &RenderOptions)
{
    use glium::Surface;

    let interpolation_type = if renderoptions.linear_interpolation {
        glium::uniforms::MagnifySamplerFilter::Linear
    }
    else {
        glium::uniforms::MagnifySamplerFilter::Nearest
    };

    let rawimage2d = glium::texture::RawImage2d {
        data: std::borrow::Cow::Borrowed(datavec),
        width: rboy::SCREEN_W as u32,
        height: rboy::SCREEN_H as u32,
        format: glium::texture::ClientFormat::U8U8U8,
    };
    texture.write(
        glium::Rect {
            left: 0,
            bottom: 0,
            width: rboy::SCREEN_W as u32,
            height: rboy::SCREEN_H as u32
        },
        rawimage2d);

    // We use a custom BlitTarget to transform OpenGL coordinates to row-column coordinates
    let target = display.draw();
    let (target_w, target_h) = target.get_dimensions();
    texture.as_surface().blit_whole_color_to(
        &target,
        &glium::BlitTarget {
            left: 0,
            bottom: target_h,
            width: target_w as i32,
            height: -(target_h as i32)
        },
        interpolation_type);
    target.finish().unwrap();
}

fn warn(message: &'static str) {
    use std::io::Write;
    let _ = write!(&mut std::io::stderr(), "{}\n", message);
}

fn construct_cpu(filename: &str, classic_mode: bool, output_serial: bool) -> Option<Device> {
    let opt_c = match classic_mode {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); return None; },
    };
    c.set_stdout(output_serial);
    Some(c)
}

fn run_cpu(mut cpu: Device, sender: SyncSender<Vec<u8>>, receiver: Receiver<GBEvent>) {
    let periodic = timer_periodic(16);
    let mut limit_speed = true;

    let waitticks = (4194304f64 / 1000.0 * 16.0).round() as u32;
    let mut ticks = 0;

    'outer: loop {
        while ticks < waitticks {
            ticks += cpu.do_cycle();
            if cpu.check_and_reset_gpu_updated() {
                let data = cpu.get_gpu_data().to_vec();
                if let Err(TrySendError::Disconnected(..)) = sender.try_send(data) {
                    break 'outer;
                }
            }
        }

        ticks -= waitticks;

        'recv: loop {
            match receiver.try_recv() {
                Ok(event) => {
                    match event {
                        GBEvent::KeyUp(key) => cpu.keyup(key),
                        GBEvent::KeyDown(key) => cpu.keydown(key),
                        GBEvent::SpeedUp => limit_speed = false,
                        GBEvent::SpeedDown => { limit_speed = true; cpu.sync_audio(); }
                    }
                },
                Err(TryRecvError::Empty) => break 'recv,
                Err(TryRecvError::Disconnected) => break 'outer,
            }
        }

        if limit_speed { let _ = periodic.recv(); }
    }
}

fn timer_periodic(ms: u64) -> Receiver<()> {
    let (tx, rx) = std::sync::mpsc::sync_channel(1);
    std::thread::spawn(move || {
        loop {
            std::thread::sleep(std::time::Duration::from_millis(ms));
            if tx.send(()).is_err() {
                break;
            }
        }
    });
    rx
}

struct CpalPlayer {
    voice: cpal::Voice,
}

impl CpalPlayer {
    fn get() -> Option<CpalPlayer> {
        if cpal::get_endpoints_list().count() == 0 { return None; }

        let endpoint = match cpal::get_default_endpoint() {
            Some(e) => e,
            None => return None,
        };

        let format = match endpoint.get_supported_formats_list().ok().and_then(|mut v| v.next()) {
            Some(f) => f,
            None => return None,
        };

        cpal::Voice::new(&endpoint, &format).ok().map(|v| CpalPlayer { voice: v })
    }
}

impl rboy::AudioPlayer for CpalPlayer {
    fn play(&mut self, buf_left: &[f32], buf_right: &[f32]) {
        debug_assert!(buf_left.len() == buf_right.len());

        let left_idx = self.voice.format().channels.iter().position(|c| *c == cpal::ChannelPosition::FrontLeft);
        let right_idx = self.voice.format().channels.iter().position(|c| *c == cpal::ChannelPosition::FrontRight);

        let channel_count = self.voice.format().channels.len();

        let count = buf_left.len();
        let mut done = 0;
        let mut lastdone = count;

        while lastdone != done && done < count {
            lastdone = done;
            let buf_left_next = &buf_left[done..];
            let buf_right_next = &buf_right[done..];
            match self.voice.append_data(count - done) {
                cpal::UnknownTypeBuffer::U16(mut buffer) => {
                    for (i, sample) in buffer.chunks_mut(channel_count).enumerate() {
                        if let Some(idx) = left_idx {
                            sample[idx] = (buf_left_next[i] * (std::i16::MAX as f32) + (std::i16::MAX as f32)) as u16;
                        }
                        if let Some(idx) = right_idx {
                            sample[idx] = (buf_right_next[i] * (std::i16::MAX as f32) + (std::i16::MAX as f32)) as u16;
                        }
                        done += 1;
                    }
                }
                cpal::UnknownTypeBuffer::I16(mut buffer) => {
                    for (i, sample) in buffer.chunks_mut(channel_count).enumerate() {
                        if let Some(idx) = left_idx {
                            sample[idx] = (buf_left_next[i] * std::i16::MAX as f32) as i16;
                        }
                        if let Some(idx) = right_idx {
                            sample[idx] = (buf_right_next[i] * std::i16::MAX as f32) as i16;
                        }
                        done += 1;
                    }
                }
                cpal::UnknownTypeBuffer::F32(mut buffer) => {
                    for (i, sample) in buffer.chunks_mut(channel_count).enumerate() {
                        if let Some(idx) = left_idx {
                            sample[idx] = buf_left_next[i];
                        }
                        if let Some(idx) = right_idx {
                            sample[idx] = buf_right_next[i];
                        }
                        done += 1;
                    }
                }
            }
        }
        self.voice.play();
    }

    fn samples_rate(&self) -> u32 {
        self.voice.format().samples_rate.0
    }

    fn underflowed(&self) -> bool {
        self.voice.underflowed()
    }
}
<commit_msg>Choose more appropriate audio format if available<commit_after>#![crate_name = "rboy"]

extern crate clap;
extern crate cpal;
extern crate glium;
extern crate rboy;

use glium::DisplayBuild;
use rboy::device::Device;
use std::sync::mpsc::{self, Receiver, SyncSender, TryRecvError, TrySendError};
use std::thread;
use std::error::Error;

const EXITCODE_SUCCESS : i32 = 0;
const EXITCODE_CPULOADFAILS : i32 = 2;

#[derive(Default)]
struct RenderOptions {
    pub linear_interpolation: bool,
}

enum GBEvent {
    KeyUp(rboy::KeypadKey),
    KeyDown(rboy::KeypadKey),
    SpeedUp,
    SpeedDown,
}

fn main() {
    let exit_status = real_main();
    if exit_status != EXITCODE_SUCCESS {
        std::process::exit(exit_status);
    }
}

fn real_main() -> i32 {
    let matches = clap::App::new("rboy")
        .version("0.1")
        .author("Mathijs van de Nes")
        .about("A Gameboy Colour emulator written in Rust")
        .arg(clap::Arg::with_name("filename")
             .help("Sets the ROM file to load")
             .required(true))
        .arg(clap::Arg::with_name("serial")
             .help("Prints the data from the serial port to stdout")
             .short("s")
             .long("serial"))
        .arg(clap::Arg::with_name("classic")
             .help("Forces the emulator to run in classic Gameboy mode")
             .short("c")
             .long("classic"))
        .arg(clap::Arg::with_name("scale")
             .help("Sets the scale of the interface. Default: 2")
             .short("x")
             .long("scale")
             .validator(|s|
                 match s.parse::<u32>() {
                     Err(e) => Err(format!("Could not parse scale: {}", e.description())),
                     Ok(s) if s < 1 => Err("Scale must be at least 1".to_owned()),
                     Ok(s) if s > 8 => Err("Scale may be at most 8".to_owned()),
                     Ok(..) => Ok(()),
                 })
             .takes_value(true))
        .arg(clap::Arg::with_name("audio")
             .help("Enables audio")
             .short("a")
             .long("audio"))
        .get_matches();

    let opt_serial = matches.is_present("serial");
    let opt_classic = matches.is_present("classic");
    let opt_audio = matches.is_present("audio");
    let filename = matches.value_of("filename").unwrap();
    let scale = matches.value_of("scale").unwrap_or("2").parse::<u32>().unwrap();

    let cpu = construct_cpu(filename, opt_classic, opt_serial);
    if cpu.is_none() { return EXITCODE_CPULOADFAILS; }
    let mut cpu = cpu.unwrap();
    if opt_audio {
        let player = CpalPlayer::get();
        match player {
            Some(v) => cpu.enable_audio(Box::new(v) as Box<rboy::AudioPlayer>),
            None => { warn("Could not open audio device"); return EXITCODE_CPULOADFAILS; },
        }
    }
    let romname = cpu.romname();

    let (sender1, receiver1) = mpsc::channel();
    let (sender2, receiver2) = mpsc::sync_channel(1);

    let display = glium::glutin::WindowBuilder::new()
        .with_dimensions(rboy::SCREEN_W as u32 * scale, rboy::SCREEN_H as u32 * scale)
        .with_title("RBoy - ".to_owned() + &romname)
        .build_glium()
        .unwrap();

    let mut texture = glium::texture::texture2d::Texture2d::empty_with_format(
            &display,
            glium::texture::UncompressedFloatFormat::U8U8U8,
            glium::texture::MipmapsOption::NoMipmap,
            rboy::SCREEN_W as u32,
            rboy::SCREEN_H as u32)
        .unwrap();

    let mut renderoptions = <RenderOptions as Default>::default();

    let cputhread = thread::spawn(move|| run_cpu(cpu, sender2, receiver1));

    'main : loop {
        for ev in display.poll_events() {
            use glium::glutin::Event;
            use glium::glutin::ElementState::{Pressed, Released};
            use glium::glutin::VirtualKeyCode;

            match ev {
                Event::Closed
                    => break 'main,
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::Escape))
                    => break 'main,
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::Key1))
                    => display.get_window().unwrap().set_inner_size(rboy::SCREEN_W as u32, rboy::SCREEN_H as u32),
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::R))
                    => display.get_window().unwrap().set_inner_size(rboy::SCREEN_W as u32 * scale, rboy::SCREEN_H as u32 * scale),
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::LShift))
                    => { let _ = sender1.send(GBEvent::SpeedUp); },
                Event::KeyboardInput(Released, _, Some(VirtualKeyCode::LShift))
                    => { let _ = sender1.send(GBEvent::SpeedDown); },
                Event::KeyboardInput(Pressed, _, Some(VirtualKeyCode::T))
                    => { renderoptions.linear_interpolation = !renderoptions.linear_interpolation; }
                Event::KeyboardInput(Pressed, _, Some(glutinkey)) => {
                    if let Some(key) = glutin_to_keypad(glutinkey) {
                        let _ = sender1.send(GBEvent::KeyDown(key));
                    }
                },
                Event::KeyboardInput(Released, _, Some(glutinkey)) => {
                    if let Some(key) = glutin_to_keypad(glutinkey) {
                        let _ = sender1.send(GBEvent::KeyUp(key));
                    }
                },
                _ => (),
            }
        }

        match receiver2.try_recv() {
            Ok(data) => recalculate_screen(&display, &mut texture, &*data, &renderoptions),
            Err(TryRecvError::Empty) => (),
            Err(TryRecvError::Disconnected) => break 'main,
        }
    }

    drop(sender1);
    let _ = cputhread.join();

    EXITCODE_SUCCESS
}

fn glutin_to_keypad(key: glium::glutin::VirtualKeyCode) -> Option<rboy::KeypadKey> {
    use glium::glutin::VirtualKeyCode;
    match key {
        VirtualKeyCode::Z => Some(rboy::KeypadKey::A),
        VirtualKeyCode::X => Some(rboy::KeypadKey::B),
        VirtualKeyCode::Up => Some(rboy::KeypadKey::Up),
        VirtualKeyCode::Down => Some(rboy::KeypadKey::Down),
        VirtualKeyCode::Left => Some(rboy::KeypadKey::Left),
        VirtualKeyCode::Right => Some(rboy::KeypadKey::Right),
        VirtualKeyCode::Space => Some(rboy::KeypadKey::Select),
        VirtualKeyCode::Return => Some(rboy::KeypadKey::Start),
        _ => None,
    }
}

fn recalculate_screen(display: &glium::backend::glutin_backend::GlutinFacade,
                      texture: &mut glium::texture::texture2d::Texture2d,
                      datavec: &[u8],
                      renderoptions: &RenderOptions)
{
    use glium::Surface;

    let interpolation_type = if renderoptions.linear_interpolation {
        glium::uniforms::MagnifySamplerFilter::Linear
    }
    else {
        glium::uniforms::MagnifySamplerFilter::Nearest
    };

    let rawimage2d = glium::texture::RawImage2d {
        data: std::borrow::Cow::Borrowed(datavec),
        width: rboy::SCREEN_W as u32,
        height: rboy::SCREEN_H as u32,
        format: glium::texture::ClientFormat::U8U8U8,
    };
    texture.write(
        glium::Rect {
            left: 0,
            bottom: 0,
            width: rboy::SCREEN_W as u32,
            height: rboy::SCREEN_H as u32
        },
        rawimage2d);

    // We use a custom BlitTarget to transform OpenGL coordinates to row-column coordinates
    let target = display.draw();
    let (target_w, target_h) = target.get_dimensions();
    texture.as_surface().blit_whole_color_to(
        &target,
        &glium::BlitTarget {
            left: 0,
            bottom: target_h,
            width: target_w as i32,
            height: -(target_h as i32)
        },
        interpolation_type);
    target.finish().unwrap();
}

fn warn(message: &'static str) {
    use std::io::Write;
    let _ = write!(&mut std::io::stderr(), "{}\n", message);
}

fn construct_cpu(filename: &str, classic_mode: bool, output_serial: bool) -> Option<Device> {
    let opt_c = match classic_mode {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); return None; },
    };
    c.set_stdout(output_serial);
    Some(c)
}

fn run_cpu(mut cpu: Device, sender: SyncSender<Vec<u8>>, receiver: Receiver<GBEvent>) {
    let periodic = timer_periodic(16);
    let mut limit_speed = true;

    let waitticks = (4194304f64 / 1000.0 * 16.0).round() as u32;
    let mut ticks = 0;

    'outer: loop {
        while ticks < waitticks {
            ticks += cpu.do_cycle();
            if cpu.check_and_reset_gpu_updated() {
                let data = cpu.get_gpu_data().to_vec();
                if let Err(TrySendError::Disconnected(..)) = sender.try_send(data) {
                    break 'outer;
                }
            }
        }

        ticks -= waitticks;

        'recv: loop {
            match receiver.try_recv() {
                Ok(event) => {
                    match event {
                        GBEvent::KeyUp(key) => cpu.keyup(key),
                        GBEvent::KeyDown(key) => cpu.keydown(key),
                        GBEvent::SpeedUp => limit_speed = false,
                        GBEvent::SpeedDown => { limit_speed = true; cpu.sync_audio(); }
                    }
                },
                Err(TryRecvError::Empty) => break 'recv,
                Err(TryRecvError::Disconnected) => break 'outer,
            }
        }

        if limit_speed { let _ = periodic.recv(); }
    }
}

fn timer_periodic(ms: u64) -> Receiver<()> {
    let (tx, rx) = std::sync::mpsc::sync_channel(1);
    std::thread::spawn(move || {
        loop {
            std::thread::sleep(std::time::Duration::from_millis(ms));
            if tx.send(()).is_err() {
                break;
            }
        }
    });
    rx
}

struct CpalPlayer {
    voice: cpal::Voice,
}

impl CpalPlayer {
    fn get() -> Option<CpalPlayer> {
        if cpal::get_endpoints_list().count() == 0 { return None; }

        let endpoint = match cpal::get_default_endpoint() {
            Some(e) => e,
            None => return None,
        };

        let mut bestf = None;
        for f in endpoint.get_supported_formats_list().unwrap() {
            if bestf.is_none() {
                bestf = Some(f);
            }
            else if f.channels.len() == 2 && f.samples_rate.0 >= 192000 && f.data_type == cpal::SampleFormat::F32 {
                bestf = Some(f.clone());
                break;
            }
        }

        let format = match bestf {
            Some(f) => f,
            None => return None,
        };

        cpal::Voice::new(&endpoint, &format).ok().map(|v| CpalPlayer { voice: v })
    }
}

impl rboy::AudioPlayer for CpalPlayer {
    fn play(&mut self, buf_left: &[f32], buf_right: &[f32]) {
        debug_assert!(buf_left.len() == buf_right.len());

        let left_idx = self.voice.format().channels.iter().position(|c| *c == cpal::ChannelPosition::FrontLeft);
        let right_idx = self.voice.format().channels.iter().position(|c| *c == cpal::ChannelPosition::FrontRight);

        let channel_count = self.voice.format().channels.len();

        let count = buf_left.len();
        let mut done = 0;
        let mut lastdone = count;

        while lastdone != done && done < count {
            lastdone = done;
            let buf_left_next = &buf_left[done..];
            let buf_right_next = &buf_right[done..];
            match self.voice.append_data(count - done) {
                cpal::UnknownTypeBuffer::U16(mut buffer) => {
                    for (i, sample) in buffer.chunks_mut(channel_count).enumerate() {
                        if sample.len() < channel_count {
                            break;
                        }
                        if let Some(idx) = left_idx {
                            sample[idx] = (buf_left_next[i] * (std::i16::MAX as f32) + (std::i16::MAX as f32)) as u16;
                        }
                        if let Some(idx) = right_idx {
                            sample[idx] = (buf_right_next[i] * (std::i16::MAX as f32) + (std::i16::MAX as f32)) as u16;
                        }
                        done += 1;
                    }
                }
                cpal::UnknownTypeBuffer::I16(mut buffer) => {
                    for (i, sample) in buffer.chunks_mut(channel_count).enumerate() {
                        if sample.len() < channel_count {
                            break;
                        }
                        if let Some(idx) = left_idx {
                            sample[idx] = (buf_left_next[i] * std::i16::MAX as f32) as i16;
                        }
                        if let Some(idx) = right_idx {
                            sample[idx] = (buf_right_next[i] * std::i16::MAX as f32) as i16;
                        }
                        done += 1;
                    }
                }
                cpal::UnknownTypeBuffer::F32(mut buffer) => {
                    for (i, sample) in buffer.chunks_mut(channel_count).enumerate() {
                        if sample.len() < channel_count {
                            break;
                        }
                        if let Some(idx) = left_idx {
                            sample[idx] = buf_left_next[i];
                        }
                        if let Some(idx) = right_idx {
                            sample[idx] = buf_right_next[i];
                        }
                        done += 1;
                    }
                }
            }
        }
        self.voice.play();
    }

    fn samples_rate(&self) -> u32 {
        self.voice.format().samples_rate.0
    }

    fn underflowed(&self) -> bool {
        self.voice.underflowed()
    }
}
<|endoftext|>