<commit_before>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError, LuaTable, PushGuard};
use hlua::any::AnyLuaValue;

use rustc_serialize::json::Json;

use std::thread;
use std::fs::{File};
use std::path::Path;
use std::io::Write;
use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;
use std::borrow::Borrow;

use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

#[macro_use]
mod funcs;
#[cfg(test)]
mod tests;

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Option<Sender<LuaMessage>>> = Mutex::new(None);

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Represents an identifier for dealing with nested tables.
///
/// To access foo.bar.baz, use vec!["foo", "bar", "baz"].
///
/// To access foo[2], use vec!["foo", 2].
pub type LuaIdent = Vec<String>;

/// Messages sent to the lua thread
#[derive(Debug)]
pub enum LuaQuery {
    /// Pings the lua thread
    Ping,
    /// Halt the lua thread
    Terminate,
    // Restart the lua thread
    Restart,

    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecFile(String),

    /// Get a variable, expecting an AnyLuaValue
    GetValue(LuaIdent),
    /// Invoke a function found at the position,
    /// with the specified arguments.
    Invoke(LuaIdent, Vec<AnyLuaValue>),
    /// Set a value
    SetValue(LuaIdent, Json),
    /// Create a new table
    NewTable(LuaIdent),
}

/// Messages received from lua thread
pub enum LuaResponse {
    /// If the identifier had length 0
    InvalidName,
    /// Lua variable obtained
    Variable(Option<AnyLuaValue>),
    /// Lua error
    Error(hlua::LuaError),
    /// A function is returned
    Function(hlua::functions_read::LuaFunction<String>),
    /// Pong response from lua ping
    Pong,
}

/// Struct sent to the lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaQuery { }
unsafe impl Sync for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaResponse { }
unsafe impl Send for LuaMessage { }
unsafe impl Sync for LuaMessage { }

impl Debug for LuaResponse {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            LuaResponse::InvalidName =>
                write!(f, "LuaResponse::InvalidName"),
            LuaResponse::Variable(ref var) =>
                write!(f, "LuaResponse::Variable({:?})", var),
            LuaResponse::Error(ref err) =>
                write!(f, "LuaResponse::Error({:?})", err),
            LuaResponse::Function(_) =>
                write!(f, "LuaResponse::Function"),
            LuaResponse::Pong =>
                write!(f, "LuaResponse::Pong")
        }
    }
}

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

/// Whether the lua thread is currently available
pub fn thread_running() -> bool {
    *RUNNING.read().unwrap()
}

/// Errors which may arise from attempting
/// to sending a message to the lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The thread crashed, was shut down, or rebooted.
    ThreadClosed,
    /// The thread has not been initialized yet (maybe not used)
    ThreadUninitialized,
    /// The sender had an issue, most likey because the thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

/// Attemps to send a LuaQuery to the lua thread.
pub fn try_send(query: LuaQuery) -> Result<Receiver<LuaResponse>,LuaSendError> {
    if !thread_running() {
        Err(LuaSendError::ThreadClosed)
    }
    else if let Some(sender) = SENDER.lock().unwrap().clone() {
        let (tx, rx) = channel();
        let message = LuaMessage { reply: tx, query: query };
        match sender.send(message) {
            Ok(_) => Ok(rx),
            Err(e) => Err(LuaSendError::Sender(e.0.query))
        }
    }
    else {
        Err(LuaSendError::ThreadUninitialized)
    }
}

/// Initialize the lua thread
pub fn init() {
    trace!("Initializing...");
    let (query_tx, query_rx) = channel::<LuaMessage>();
    {
        let mut sender = SENDER.lock().unwrap();
        *sender = Some(query_tx);
    }

    thread::spawn(move || {
        thread_init(query_rx);
    });
    trace!("Created thread. Init finished.");
}

fn thread_init(receiver: Receiver<LuaMessage>) {
    trace!("thread: initializing.");
    let mut lua = Lua::new();
    //unsafe {
    //    hlua_ffi::lua_atpanic(&mut lua.as_mut_lua().0, thread_on_panic);
    //}
    debug!("thread: Loading Lua libraries...");
    lua.openlibs();
    trace!("thread: Loading way-cooler lua extensions...");
    // We should have some good file handling, read files from /usr by default,
    // but for now we're reading directly from the source.
    lua.execute_from_reader::<(), File>(
        File::open("lib/lua/init.lua").unwrap()
    ).unwrap();
    trace!("thread: loading way-cooler libraries...");
    funcs::register_libraries(&mut lua);
    // Only ready after loading libs
    *RUNNING.write().unwrap() = true;
    debug!("thread: entering main loop...");
    thread_main_loop(receiver, &mut lua);
}

fn thread_main_loop(receiver: Receiver<LuaMessage>, lua: &mut Lua) {
    loop {
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");
                *RUNNING.write().unwrap() = false;

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                thread_handle_message(message, lua);
            }
        }
    }
}

fn thread_handle_message(request: LuaMessage, lua: &mut Lua) {
    match request.query {
        LuaQuery::Terminate => {
            trace!("thread: Received terminate signal");
            *RUNNING.write().unwrap() = false;

            info!("thread: Lua thread terminating!");
            thread_send(request.reply, LuaResponse::Pong);
            return;
        },

        LuaQuery::Restart => {
            trace!("thread: Received restart signal!");
            error!("thread: Lua thread restart not supported!");

            *RUNNING.write().unwrap() = false;
            thread_send(request.reply, LuaResponse::Pong);

            panic!("Lua thread: Restart not supported!");
        },

        LuaQuery::Execute(code) => {
            trace!("thread: Received request to execute code");
            trace!("thread: Executing {}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("thread: Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("thread: Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        },

        LuaQuery::ExecFile(name) => {
            info!("thread: Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("thread: Error executing {}!", name);

                    thread_send(request.reply, LuaResponse::Error(err));
                }
                else {
                    trace!("thread: Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());

                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        },

        LuaQuery::GetValue(varname) => {
            trace!("thread: Received request to get variable {:?}", varname);

            if varname.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
                return;
            }
            // Table[0] String had to be cloned, it'd be nice if Rust let us
            // borrow out parts of memory
            match lua.get::<AnyLuaValue, _>(varname[0].clone()) {
                Some(table) => {
                    let full_table = walk_table(table, &varname[1..]);
                    thread_send(request.reply,
                                LuaResponse::Variable(full_table));
                },
                None => thread_send(request.reply, LuaResponse::Variable(None))
            }
        },

        LuaQuery::SetValue(name, val) => {
            trace!("thread: SetValue: Setting {:?} to {:?}", name, val);
            /*
            let maybe_table: Option<LuaTable<_>> = lua.get::<_, _>(name[0]);

            let table: LuaTable<_>;

            if name.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
            }
            else if name.len() == 1 {
                let lua_val =
                table.set(name[0], val);
            }
            else {
                match maybe_table {
                    Some(table) => {

                    }
                }
            }*/
            unimplemented!();
        },

        LuaQuery::NewTable(name_list) => {
            panic!("thread: unimplemented LuaQuery::NewTable!");
        },

        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        },
        _ => {
            panic!("Unimplemented send type for lua thread!");
        }
    }
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {}, // Those are boring
                _ => {
                    warn!("thread: Someone dropped an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}

fn walk_table(table: AnyLuaValue, names: &[String]) -> Option<AnyLuaValue> {
    if let Some(name) = names.first() {
        if let AnyLuaValue::LuaArray(arr) = table {
            for (key, val) in arr {
                if let AnyLuaValue::LuaString(key_str) = key {
                    if *key_str == *name {
                        return walk_table(val, &names[1..]);
                    }
                }
            }
            return None;
        }
        else {
            return None;
        }
    }
    else {
        return Some(table); // ???
    }
}

/*
fn set_value<'l>(lua: &'l mut Lua, mut table: PushGuard<LuaTable<Lua>>,
             names: &[String], val: AnyLuaValue) {
    // Should not be reached!
    if names.len() == 0 {
        return;
    }
    // The last name is the name of the value
    else if names.len() == 1 {
        table.set(names[0].clone(), val);
    }
    else {
        let maybe_table = table.get::<_, _>(names[0]);
        match maybe_table {
            Some(new_table) => {
                set_value(&mut lua, new_table, &names[1..], val);
            }
            None => { return; }
        }
    }
}*/

/// Converts a Json map into an AnyLuaValue
pub fn json_to_lua(json: Json) -> AnyLuaValue {
    match json {
        Json::String(val)  => AnyLuaValue::LuaString(val),
        Json::Boolean(val) => AnyLuaValue::LuaBoolean(val),
        Json::F64(val)     => AnyLuaValue::LuaNumber(val),
        Json::I64(val)     => AnyLuaValue::LuaNumber((val as i32) as f64),
        Json::U64(val)     => AnyLuaValue::LuaNumber((val as u32) as f64),
        Json::Null         => AnyLuaValue::LuaNil,
        Json::Array(vals)  => {
            let mut lua_arr = Vec::with_capacity(vals.len());
            for (ix, val) in vals.into_iter().enumerate() {
                lua_arr.push((AnyLuaValue::LuaNumber(ix as f64 + 1.0),
                              json_to_lua(val)));
            }
            AnyLuaValue::LuaArray(lua_arr)
        },
        Json::Object(vals) => {
            let mut lua_table = Vec::with_capacity(vals.len());
            for (key, val) in vals.into_iter() {
                lua_table.push((AnyLuaValue::LuaString(key),
                                json_to_lua(val)));
            }
            AnyLuaValue::LuaArray(lua_table)
        }
    }
}

<commit_msg>Add lua_to_json conversion<commit_after>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError, LuaTable, PushGuard};
use hlua::any::AnyLuaValue;

use rustc_serialize::json::Json;

use std::collections::BTreeMap;

use std::thread;
use std::fs::{File};
use std::path::Path;
use std::io::Write;

use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;
use std::borrow::Borrow;

use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

#[macro_use]
mod funcs;
#[cfg(test)]
mod tests;

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Option<Sender<LuaMessage>>> = Mutex::new(None);

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Represents an identifier for dealing with nested tables.
///
/// To access foo.bar.baz, use vec!["foo", "bar", "baz"].
///
/// To access foo[2], use vec!["foo", 2].
pub type LuaIdent = Vec<String>;

/// Messages sent to the lua thread
#[derive(Debug)]
pub enum LuaQuery {
    /// Pings the lua thread
    Ping,
    /// Halt the lua thread
    Terminate,
    // Restart the lua thread
    Restart,

    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecFile(String),

    /// Get a variable, expecting an AnyLuaValue
    GetValue(LuaIdent),
    /// Invoke a function found at the position,
    /// with the specified arguments.
    Invoke(LuaIdent, Vec<AnyLuaValue>),
    /// Set a value
    SetValue(LuaIdent, Json),
    /// Create a new table
    NewTable(LuaIdent),
}

/// Messages received from lua thread
pub enum LuaResponse {
    /// If the identifier had length 0
    InvalidName,
    /// Lua variable obtained
    Variable(Option<AnyLuaValue>),
    /// Lua error
    Error(hlua::LuaError),
    /// A function is returned
    Function(hlua::functions_read::LuaFunction<String>),
    /// Pong response from lua ping
    Pong,
}

/// Struct sent to the lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaQuery { }
unsafe impl Sync for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaResponse { }
unsafe impl Send for LuaMessage { }
unsafe impl Sync for LuaMessage { }

impl Debug for LuaResponse {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            LuaResponse::InvalidName =>
                write!(f, "LuaReponse::InvalidName"),
            LuaResponse::Variable(ref var) =>
                write!(f, "LuaResponse::Variable({:?})", var),
            LuaResponse::Error(ref err) =>
                write!(f, "LuaResponse::Error({:?})", err),
            LuaResponse::Function(_) =>
                write!(f, "LuaResponse::Function"),
            LuaResponse::Pong =>
                write!(f, "LuaResponse::Pong")
        }
    }
}

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

/// Whether the lua thread is currently available
pub fn thread_running() -> bool {
    *RUNNING.read().unwrap()
}

/// Errors which may arise from attempting
/// to sending a message to the lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The thread crashed, was shut down, or rebooted.
    ThreadClosed,
    /// The thread has not been initialized yet (maybe not used)
    ThreadUninitialized,
    /// The sender had an issue, most likey because the thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

/// Attemps to send a LuaQuery to the lua thread.
pub fn try_send(query: LuaQuery) -> Result<Receiver<LuaResponse>,LuaSendError> {
    if !thread_running() {
        Err(LuaSendError::ThreadClosed)
    }
    else if let Some(sender) = SENDER.lock().unwrap().clone() {
        let (tx, rx) = channel();
        let message = LuaMessage { reply: tx, query: query };
        match sender.send(message) {
            Ok(_) => Ok(rx),
            Err(e) => Err(LuaSendError::Sender(e.0.query))
        }
    }
    else {
        Err(LuaSendError::ThreadUninitialized)
    }
}

/// Initialize the lua thread
pub fn init() {
    trace!("Initializing...");
    let (query_tx, query_rx) = channel::<LuaMessage>();
    {
        let mut sender = SENDER.lock().unwrap();
        *sender = Some(query_tx);
    }

    thread::spawn(move || {
        thread_init(query_rx);
    });
    trace!("Created thread. Init finished.");
}

fn thread_init(receiver: Receiver<LuaMessage>) {
    trace!("thread: initializing.");
    let mut lua = Lua::new();
    //unsafe {
    //    hlua_ffi::lua_atpanic(&mut lua.as_mut_lua().0, thread_on_panic);
    //}
    debug!("thread: Loading Lua libraries...");
    lua.openlibs();
    trace!("thread: Loading way-cooler lua extensions...");
    // We should have some good file handling, read files from /usr by default,
    // but for now we're reading directly from the source.
    lua.execute_from_reader::<(), File>(
        File::open("lib/lua/init.lua").unwrap()
    ).unwrap();
    trace!("thread: loading way-cooler libraries...");
    funcs::register_libraries(&mut lua);
    // Only ready after loading libs
    *RUNNING.write().unwrap() = true;
    debug!("thread: entering main loop...");
    thread_main_loop(receiver, &mut lua);
}

fn thread_main_loop(receiver: Receiver<LuaMessage>, lua: &mut Lua) {
    loop {
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");
                *RUNNING.write().unwrap() = false;

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                thread_handle_message(message, lua);
            }
        }
    }
}

fn thread_handle_message(request: LuaMessage, lua: &mut Lua) {
    match request.query {
        LuaQuery::Terminate => {
            trace!("thread: Received terminate signal");
            *RUNNING.write().unwrap() = false;

            info!("thread: Lua thread terminating!");
            thread_send(request.reply, LuaResponse::Pong);
            return;
        },

        LuaQuery::Restart => {
            trace!("thread: Received restart signal!");
            error!("thread: Lua thread restart not supported!");

            *RUNNING.write().unwrap() = false;
            thread_send(request.reply, LuaResponse::Pong);

            panic!("Lua thread: Restart not supported!");
        },

        LuaQuery::Execute(code) => {
            trace!("thread: Received request to execute code");
            trace!("thread: Executing {}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("thread: Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("thread: Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        },

        LuaQuery::ExecFile(name) => {
            info!("thread: Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("thread: Error executing {}!", name);

                    thread_send(request.reply, LuaResponse::Error(err));
                }
                else {
                    trace!("thread: Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());

                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        },

        LuaQuery::GetValue(varname) => {
            trace!("thread: Received request to get variable {:?}", varname);

            if varname.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
                return;
            }
            // Table[0] String had to be cloned, it'd be nice if Rust let us
            // borrow out parts of memory
            match lua.get::<AnyLuaValue, _>(varname[0].clone()) {
                Some(table) => {
                    let full_table = walk_table(table, &varname[1..]);
                    thread_send(request.reply,
                                LuaResponse::Variable(full_table));
                },
                None => thread_send(request.reply, LuaResponse::Variable(None))
            }
        },

        LuaQuery::SetValue(name, val) => {
            trace!("thread: SetValue: Setting {:?} to {:?}", name, val);
            /*
            let maybe_table: Option<LuaTable<_>> = lua.get::<_, _>(name[0]);

            let table: LuaTable<_>;

            if name.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
            }
            else if name.len() == 1 {
                let lua_val =
                table.set(name[0], val);
            }
            else {
                match maybe_table {
                    Some(table) => {

                    }
                }
            }*/
            unimplemented!();
        },

        LuaQuery::NewTable(name_list) => {
            panic!("thread: unimplemented LuaQuery::NewTable!");
        },

        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        },
        _ => {
            panic!("Unimplemented send type for lua thread!");
        }
    }
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {}, // Those are boring
                _ => {
                    warn!("thread: Someone dropped an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}

fn walk_table(table: AnyLuaValue, names: &[String]) -> Option<AnyLuaValue> {
    if let Some(name) = names.first() {
        if let AnyLuaValue::LuaArray(arr) = table {
            for (key, val) in arr {
                if let AnyLuaValue::LuaString(key_str) = key {
                    if *key_str == *name {
                        return walk_table(val, &names[1..]);
                    }
                }
            }
            return None;
        }
        else {
            return None;
        }
    }
    else {
        return Some(table); // ???
    }
}

/*
fn set_value<'l>(lua: &'l mut Lua, mut table: PushGuard<LuaTable<Lua>>,
             names: &[String], val: AnyLuaValue) {
    // Should not be reached!
    if names.len() == 0 {
        return;
    }
    // The last name is the name of the value
    else if names.len() == 1 {
        table.set(names[0].clone(), val);
    }
    else {
        let maybe_table = table.get::<_, _>(names[0]);
        match maybe_table {
            Some(new_table) => {
                set_value(&mut lua, new_table, &names[1..], val);
            }
            None => { return; }
        }
    }
}*/

/// Converts a Json map into an AnyLuaValue
pub fn json_to_lua(json: Json) -> AnyLuaValue {
    match json {
        Json::String(val)  => AnyLuaValue::LuaString(val),
        Json::Boolean(val) => AnyLuaValue::LuaBoolean(val),
        Json::F64(val)     => AnyLuaValue::LuaNumber(val),
        Json::I64(val)     => AnyLuaValue::LuaNumber((val as i32) as f64),
        Json::U64(val)     => AnyLuaValue::LuaNumber((val as u32) as f64),
        Json::Null         => AnyLuaValue::LuaNil,
        Json::Array(vals)  => {
            let mut lua_arr = Vec::with_capacity(vals.len());
            for (ix, val) in vals.into_iter().enumerate() {
                lua_arr.push((AnyLuaValue::LuaNumber(ix as f64 + 1.0),
                              json_to_lua(val)));
            }
            AnyLuaValue::LuaArray(lua_arr)
        },
        Json::Object(vals) => {
            let mut lua_table = Vec::with_capacity(vals.len());
            for (key, val) in vals.into_iter() {
                lua_table.push((AnyLuaValue::LuaString(key),
                                json_to_lua(val)));
            }
            AnyLuaValue::LuaArray(lua_table)
        }
    }
}

/// Converts an `AnyLuaValue` to a `Json`.
///
/// For an already-matched `LuaArray`, use `lua_array_to_json`.
///
/// For a `LuaArray` that should be mapped to a `JsonObject`,
/// use `lua_object_to_json`.
pub fn lua_to_json(lua: AnyLuaValue) -> Result<Json, ()> {
    match lua {
        AnyLuaValue::LuaNil => Ok(Json::Null),
        AnyLuaValue::LuaString(val) => Ok(Json::String(val)),
        AnyLuaValue::LuaNumber(val) => Ok(Json::F64(val)),
        AnyLuaValue::LuaBoolean(val) => Ok(Json::Boolean(val)),
        AnyLuaValue::LuaArray(arr) => lua_array_to_json(arr),
        AnyLuaValue::LuaOther => Err(())
    }
}

pub fn lua_array_to_json(arr: Vec<(AnyLuaValue, AnyLuaValue)>)
                         -> Result<Json, ()> {
    // Check if every key is a number
    let mut counter = 0.0; // Account for first index?

    for &(ref key, ref _val) in &arr {
        match *key {
            AnyLuaValue::LuaNumber(num) => {
                counter += num;
            }
            AnyLuaValue::LuaString(_) => {
                break;
            }
            // Non-string keys are not allowed
            _ => {
                return Err(());
            }
        }
    }

    // Gauss' trick
    let desired_sum = ((arr.len()) * (arr.len() + 1)) / 2;
    if counter != desired_sum as f64 {
        return lua_object_to_json(arr);
    }

    let mut json_arr: Vec<Json> = Vec::with_capacity(arr.len());

    for (_key, val) in arr.into_iter() {
        let lua_val = try!(lua_to_json(val));
        json_arr.push(lua_val);
    }
    Ok(Json::Array(json_arr))
}

pub fn lua_object_to_json(obj: Vec<(AnyLuaValue, AnyLuaValue)>)
                          -> Result<Json, ()> {
    let mut json_obj: BTreeMap<String, Json> = BTreeMap::new();

    for (key, val) in obj.into_iter() {
        match key {
            AnyLuaValue::LuaString(text) => {
                json_obj.insert(text, try!(lua_to_json(val)));
            },
            AnyLuaValue::LuaNumber(ix) => {
                json_obj.insert(format!("{}", ix), try!(lua_to_json(val)));
            }
            _ => { return Err(()); }
        }
    }
    Ok(Json::Object(json_obj))
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 The 6502-rs Developers
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the names of the copyright holders nor the names of any
//    contributors may be used to endorse or promote products derived from this
//    software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

use log;

use std;

use address::{AddressDiff};
use instruction;
use instruction::{DecodedInstr};
use memory::Memory;
use registers::{ Registers, Status, StatusArgs };
use registers::{ PS_NEGATIVE, PS_OVERFLOW, PS_ZERO, PS_CARRY };

pub struct Machine {
    pub registers: Registers,
    pub memory:    Memory
}

impl Machine {
    pub fn new() -> Machine {
    	Machine{
    	    registers: Registers::new(),
    	    memory:    Memory::new()
    	}
    }

    pub fn reset(&mut self) {
    	*self = Machine::new();
    }

    pub fn fetch_next_and_decode(&mut self) -> Option<DecodedInstr> {
        let x: u8 = self.memory.get_byte(self.registers.program_counter);

        match instruction::OPCODES[x as uint] {
            Some((instr, am)) => {
                let extra_bytes = am.extra_bytes();
                let num_bytes = AddressDiff(1) + extra_bytes;

                let data_start = self.registers.program_counter
                               + AddressDiff(1);

                let slice = self.memory.get_slice(data_start, extra_bytes);
                let am_out = am.process(self, slice);

                // Increment program counter
                self.registers.program_counter =
                    self.registers.program_counter + num_bytes;

                Some((instr, am_out))
            }
            _ => None
        }
    }

    pub fn execute_instruction(&mut self, decoded_instr: DecodedInstr) {
        match decoded_instr {
            (instruction::ADC, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "add with carry immediate: {}", val);
                self.add_with_carry(val as i8);
            },
            (instruction::ADC, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr) as i8;
                log!(log::DEBUG, "add with carry. address: {}. value: {}",
                                 addr, val);
                self.add_with_carry(val);
            },

            (instruction::DEX, instruction::UseImplied) => {
                self.dec_x();
            }

            (instruction::LDA, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load A immediate: {}", val);
                self.load_accumulator(val as i8);
            },
            (instruction::LDA, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load A. address: {}. value: {}", addr, val);
                self.load_accumulator(val as i8);
            },

            (instruction::LDX, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load X immediate: {}", val);
                self.load_x_register(val as i8);
            },
            (instruction::LDX, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load X. address: {}. value: {}", addr, val);
                self.load_x_register(val as i8);
            },

            (instruction::LDY, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load Y immediate: {}", val);
                self.load_y_register(val as i8);
            },
            (instruction::LDY, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load Y. address: {}. value: {}", addr, val);
                self.load_y_register(val as i8);
            },

            (instruction::NOP, _) => {
                log!(log::DEBUG, "nop instr");
            },
            (_, _) => {
                log!(log::DEBUG, "attempting to execute unimplemented \
                                  instruction");
            },
        };
    }

    pub fn run(&mut self) {
        loop {
            if let Some(decoded_instr) = self.fetch_next_and_decode() {
                self.execute_instruction(decoded_instr);
            } else {
                break
            }
        }
    }

    fn load_register_with_flags(register: &mut i8,
                                status: &mut Status,
                                value: i8) {
        *register = value;

        let is_zero = value == 0;
        let is_negative = value < 0;

        status.set_with_mask(
            PS_ZERO | PS_NEGATIVE,
            Status::new(StatusArgs { zero: is_zero,
                                     negative: is_negative,
                                     ..StatusArgs::none() } ));
    }

    pub fn load_x_register(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.index_x,
                                          &mut self.registers.status,
                                          value);
    }

    pub fn load_y_register(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.index_y,
                                          &mut self.registers.status,
                                          value);
    }

    pub fn load_accumulator(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.accumulator,
                                          &mut self.registers.status,
                                          value);
    }

    // TODO akeeton: Implement binary-coded decimal.
    pub fn add_with_carry(&mut self, value: i8) {
        let a_before: i8 = self.registers.accumulator;
        let c_before: i8 = self.registers.status.get_carry();
        let a_after: i8 = a_before + c_before + value;

        debug_assert_eq!(a_after as u8, a_before as u8 + c_before as u8
                                        + value as u8);

        let did_carry = (a_after as u8) < (a_before as u8);

        let did_overflow   =
        	   (a_before < 0 && value < 0 && a_after >= 0)
        	|| (a_before > 0 && value > 0 && a_after <= 0);

        let mask = PS_CARRY | PS_OVERFLOW;

        self.registers.status.set_with_mask(mask,
            Status::new(StatusArgs { carry: did_carry,
                                     overflow: did_overflow,
                                     ..StatusArgs::none() } ));

        self.load_accumulator(a_after);

        log!(log::DEBUG, "accumulator: {}", self.registers.accumulator);
    }

    pub fn dec_x(&mut self) {
        let val = self.registers.index_x;
        self.load_x_register(val - 1);
    }
}

impl std::fmt::Show for Machine {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "Machine Dump:\n\nAccumulator: {}",
               self.registers.accumulator)
    }
}

#[test]
fn add_with_carry_test() {

    let mut machine = Machine::new();

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, 1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(-1);
    assert_eq!(machine.registers.accumulator, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    true);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, 2);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    let mut machine = Machine::new();

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, 127);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(-127);
    assert_eq!(machine.registers.accumulator, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),     true);
    assert_eq!(machine.registers.status.contains(PS_ZERO),      true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.registers.status.remove(PS_CARRY);
    machine.add_with_carry(-128);
    assert_eq!(machine.registers.accumulator, -128);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, -1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    let mut machine = Machine::new();

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, 127);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, -128);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW),  true);
}

#[test]
fn dec_x_test() {
    let mut machine = Machine::new();

    machine.dec_x();
    assert_eq!(machine.registers.index_x, -1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.dec_x();
    assert_eq!(machine.registers.index_x, -2);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.load_x_register(5);
    machine.dec_x();
    assert_eq!(machine.registers.index_x, 4);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.dec_x();
    machine.dec_x();
    machine.dec_x();
    machine.dec_x();

    assert_eq!(machine.registers.index_x, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.dec_x();
    assert_eq!(machine.registers.index_x, -1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);
}

#[test]
fn dec_x_test() {
    let mut machine = Machine::new();

    machine.dec_x();
    assert_eq!(machine.registers.index_x, -1);
    assert_eq!(machine.registers.status.contains(ps_carry),    false);
    assert_eq!(machine.registers.status.contains(ps_zero),     false);
    assert_eq!(machine.registers.status.contains(ps_negative), true);
    assert_eq!(machine.registers.status.contains(ps_overflow), false);

    machine.dec_x();
    assert_eq!(machine.registers.index_x, -2);
    assert_eq!(machine.registers.status.contains(ps_carry),    false);
    assert_eq!(machine.registers.status.contains(ps_zero),     false);
    assert_eq!(machine.registers.status.contains(ps_negative), true);
    assert_eq!(machine.registers.status.contains(ps_overflow), false);

    machine.load_x_register(5);
    machine.dec_x();
    assert_eq!(machine.registers.index_x, 4);
    assert_eq!(machine.registers.status.contains(ps_carry),    false);
    assert_eq!(machine.registers.status.contains(ps_zero),     false);
    assert_eq!(machine.registers.status.contains(ps_negative), false);
    assert_eq!(machine.registers.status.contains(ps_overflow), false);

    machine.dec_x();
    machine.dec_x();
    machine.dec_x();
    machine.dec_x();

    assert_eq!(machine.registers.index_x, 0);
    assert_eq!(machine.registers.status.contains(ps_carry),    false);
    assert_eq!(machine.registers.status.contains(ps_zero),     true);
    assert_eq!(machine.registers.status.contains(ps_negative), false);
    assert_eq!(machine.registers.status.contains(ps_overflow), false);

    machine.dec_x();
    assert_eq!(machine.registers.index_x, -1);
    assert_eq!(machine.registers.status.contains(ps_carry),    false);
    assert_eq!(machine.registers.status.contains(ps_zero),     false);
    assert_eq!(machine.registers.status.contains(ps_negative), true);
    assert_eq!(machine.registers.status.contains(ps_overflow), false);
}
<commit_msg>Remove duplicate test from weird merge pull idk<commit_after>// Copyright (C) 2014 The 6502-rs Developers
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the names of the copyright holders nor the names of any
//    contributors may be used to endorse or promote products derived from this
//    software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

use log;

use std;

use address::{AddressDiff};
use instruction;
use instruction::{DecodedInstr};
use memory::Memory;
use registers::{ Registers, Status, StatusArgs };
use registers::{ PS_NEGATIVE, PS_OVERFLOW, PS_ZERO, PS_CARRY };

pub struct Machine {
    pub registers: Registers,
    pub memory:    Memory
}

impl Machine {
    pub fn new() -> Machine {
    	Machine{
    	    registers: Registers::new(),
    	    memory:    Memory::new()
    	}
    }

    pub fn reset(&mut self) {
    	*self = Machine::new();
    }

    pub fn fetch_next_and_decode(&mut self) -> Option<DecodedInstr> {
        let x: u8 = self.memory.get_byte(self.registers.program_counter);

        match instruction::OPCODES[x as uint] {
            Some((instr, am)) => {
                let extra_bytes = am.extra_bytes();
                let num_bytes = AddressDiff(1) + extra_bytes;

                let data_start = self.registers.program_counter
                               + AddressDiff(1);

                let slice = self.memory.get_slice(data_start, extra_bytes);
                let am_out = am.process(self, slice);

                // Increment program counter
                self.registers.program_counter =
                    self.registers.program_counter + num_bytes;

                Some((instr, am_out))
            }
            _ => None
        }
    }

    pub fn execute_instruction(&mut self, decoded_instr: DecodedInstr) {
        match decoded_instr {
            (instruction::ADC, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "add with carry immediate: {}", val);
                self.add_with_carry(val as i8);
            },
            (instruction::ADC, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr) as i8;
                log!(log::DEBUG, "add with carry. address: {}. value: {}",
                                 addr, val);
                self.add_with_carry(val);
            },

            (instruction::DEX, instruction::UseImplied) => {
                self.dec_x();
            }

            (instruction::LDA, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load A immediate: {}", val);
                self.load_accumulator(val as i8);
            },
            (instruction::LDA, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load A. address: {}. value: {}", addr, val);
                self.load_accumulator(val as i8);
            },

            (instruction::LDX, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load X immediate: {}", val);
                self.load_x_register(val as i8);
            },
            (instruction::LDX, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load X. address: {}. value: {}", addr, val);
                self.load_x_register(val as i8);
            },

            (instruction::LDY, instruction::UseImmediate(val)) => {
                log!(log::DEBUG, "load Y immediate: {}", val);
                self.load_y_register(val as i8);
            },
            (instruction::LDY, instruction::UseAddress(addr)) => {
                let val = self.memory.get_byte(addr);
                log!(log::DEBUG, "load Y. address: {}. value: {}", addr, val);
                self.load_y_register(val as i8);
            },

            (instruction::NOP, _) => {
                log!(log::DEBUG, "nop instr");
            },
            (_, _) => {
                log!(log::DEBUG, "attempting to execute unimplemented \
                                  instruction");
            },
        };
    }

    pub fn run(&mut self) {
        loop {
            if let Some(decoded_instr) = self.fetch_next_and_decode() {
                self.execute_instruction(decoded_instr);
            } else {
                break
            }
        }
    }

    fn load_register_with_flags(register: &mut i8,
                                status: &mut Status,
                                value: i8) {
        *register = value;

        let is_zero = value == 0;
        let is_negative = value < 0;

        status.set_with_mask(
            PS_ZERO | PS_NEGATIVE,
            Status::new(StatusArgs { zero: is_zero,
                                     negative: is_negative,
                                     ..StatusArgs::none() } ));
    }

    pub fn load_x_register(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.index_x,
                                          &mut self.registers.status,
                                          value);
    }

    pub fn load_y_register(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.index_y,
                                          &mut self.registers.status,
                                          value);
    }

    pub fn load_accumulator(&mut self, value: i8) {
        Machine::load_register_with_flags(&mut self.registers.accumulator,
                                          &mut self.registers.status,
                                          value);
    }

    // TODO akeeton: Implement binary-coded decimal.
    pub fn add_with_carry(&mut self, value: i8) {
        let a_before: i8 = self.registers.accumulator;
        let c_before: i8 = self.registers.status.get_carry();
        let a_after: i8 = a_before + c_before + value;

        debug_assert_eq!(a_after as u8, a_before as u8 + c_before as u8
                                        + value as u8);

        let did_carry = (a_after as u8) < (a_before as u8);

        let did_overflow   =
        	   (a_before < 0 && value < 0 && a_after >= 0)
        	|| (a_before > 0 && value > 0 && a_after <= 0);

        let mask = PS_CARRY | PS_OVERFLOW;

        self.registers.status.set_with_mask(mask,
            Status::new(StatusArgs { carry: did_carry,
                                     overflow: did_overflow,
                                     ..StatusArgs::none() } ));

        self.load_accumulator(a_after);

        log!(log::DEBUG, "accumulator: {}", self.registers.accumulator);
    }

    pub fn dec_x(&mut self) {
        let val = self.registers.index_x;
        self.load_x_register(val - 1);
    }
}

impl std::fmt::Show for Machine {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "Machine Dump:\n\nAccumulator: {}",
               self.registers.accumulator)
    }
}

#[test]
fn add_with_carry_test() {

    let mut machine = Machine::new();

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, 1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(-1);
    assert_eq!(machine.registers.accumulator, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    true);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, 2);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    let mut machine = Machine::new();

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, 127);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(-127);
    assert_eq!(machine.registers.accumulator, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),     true);
    assert_eq!(machine.registers.status.contains(PS_ZERO),      true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.registers.status.remove(PS_CARRY);
    machine.add_with_carry(-128);
    assert_eq!(machine.registers.accumulator, -128);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, -1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    let mut machine = Machine::new();

    machine.add_with_carry(127);
    assert_eq!(machine.registers.accumulator, 127);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.add_with_carry(1);
    assert_eq!(machine.registers.accumulator, -128);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE),  true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW),  true);
}

#[test]
fn dec_x_test() {
    let mut machine = Machine::new();

    machine.dec_x();
    assert_eq!(machine.registers.index_x, -1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.dec_x();
    assert_eq!(machine.registers.index_x, -2);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.load_x_register(5);
    machine.dec_x();
    assert_eq!(machine.registers.index_x, 4);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.dec_x();
    machine.dec_x();
    machine.dec_x();
    machine.dec_x();

    assert_eq!(machine.registers.index_x, 0);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     true);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);

    machine.dec_x();
    assert_eq!(machine.registers.index_x, -1);
    assert_eq!(machine.registers.status.contains(PS_CARRY),    false);
    assert_eq!(machine.registers.status.contains(PS_ZERO),     false);
    assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
    assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);
}
<|endoftext|>


<commit_before>use ast::Script;
use compile::ExecutableDevEnv;
use run::{ Execution, ExecutionEvent, Error as RunError, StartStopError };

use std::collections::HashMap;
use std::path::{ Path as FilePath, PathBuf as FilePathBuf };

use foxbox_taxonomy::api::ResultMap;
use foxbox_taxonomy::parse::*;
use foxbox_taxonomy::util::{ Id };

use rusqlite;
use transformable_channels::mpsc::{ channel, ExtSender, TransformableSender };

/// A ScriptManager error.
#[derive(Serialize, Debug)]
pub enum Error {
    /// The script you requested (by ID) does not exist.
    NoSuchScriptError,

    /// There was an error executing some SQL.
    SQLError(String),

    /// There was an error attempting to run a script. (See `run.rs`.)
    RunError(RunError),

    /// There was an error parsing the script's JSON.
    ParseError(String),
}

/// A type for ensuring type-safety (Id<ScriptId>).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Hash, Eq)]
pub struct ScriptId;

/// ScriptManager stores a persistent database of scripts and executes them.
/// Each script can be individually enabled or disabled.
/// When a script is enabled, it is always running (unless an error occured during launch).
/// Script sources are stored as JSON strings in a SQLite database.
pub struct ScriptManager<Env, T> where Env: ExecutableDevEnv + Clone + 'static {
    env: Env,

    /// The path to the SQLite file to store, e.g. "./database.sqlite"
    path: FilePathBuf,

    /// A map to track currently-executing scripts.
    runners: HashMap<Id<ScriptId>, Execution<Env>>,

    /// The tx end of the channel passed to ScriptManager::new()
    tx: Box<T>,
}

impl<Env, T> ScriptManager<Env, T>
    where Env: ExecutableDevEnv + Clone + 'static,
          T: ExtSender<(Id<ScriptId>, ExecutionEvent)> + TransformableSender<(Id<ScriptId>, ExecutionEvent)> {

    /// Create a ScriptManager using a SQLite database file with the given path, i.e. filename.
    /// If the database file does not exist, it will be created.
    ///
    /// NOTE: You MUST consume the contents of `tx` to prevent memory leaks.
    ///
    /// The database stores the raw script source, but only after the source has been parsed
    /// to ensure validity.
    pub fn new(env: Env, path: &FilePath, tx: Box<T>) -> Result<Self, Error> {

        let connection = try!(rusqlite::Connection::open(&path));
        try!(connection.execute("CREATE TABLE IF NOT EXISTS scripts (
            id          TEXT NOT NULL PRIMARY KEY,
            source      TEXT NOT NULL,
            is_enabled  BOOL NOT NULL DEFAULT 1
        )", &[]));

        Ok(ScriptManager {
            path: path.to_owned(),
            env: env,
            runners: HashMap::new(),
            tx: tx
        })
    }

    /// Load and launch all existing scripts from the database.
    pub fn load(&mut self) -> Result<ResultMap<Id<ScriptId>, (), Error>, Error> {
        let connection = try!(rusqlite::Connection::open(&self.path));
        let mut result_map = HashMap::new();
        let mut stmt = try!(connection.prepare("SELECT id, source, is_enabled FROM scripts"));
        let rows = try!(stmt.query(&[]));

        for result_row in rows {
            let row = try!(result_row);
            let id_string: String = try!(row.get_checked(0));
            let id: Id<ScriptId> = Id::new(&id_string);
            let source: String = try!(row.get_checked(1));
            let is_enabled: bool = try!(row.get_checked(2));

            if is_enabled {
                result_map.insert(
                    id.clone(),
                    self.start_script(&id, &source).map(|_| ()));
            }
        }
        Ok(result_map)
    }

    /// Attempt to add a new script. The script will be executed and persisted to disk.
    /// The ID is chosen by the consumer and must be unique.
    pub fn put(&mut self, id: &Id<ScriptId>, source: &String) -> Result<(), Error> {
        try!(self.start_script(&id, &source));

        let connection = try!(rusqlite::Connection::open(&self.path));
        connection.execute("INSERT OR REPLACE INTO scripts (id, source, is_enabled)
                VALUES ($1, $2, $3)", &[&id.to_string(), source, &1])
            .map(|_| ()).map_err(From::from)
    }

    /// Enable or disable a script, starting or stopping the script if necessary.
    pub fn set_enabled(&mut self, id: &Id<ScriptId>, enabled: bool) -> Result<(), Error> {
        let source = try!(self.get_source(id));
        let is_running = self.runners.contains_key(&id);
        match (enabled, is_running) {
            (false, true) => {
                if let Some(mut runner) = self.runners.remove(&id) {
                    let (tx, rx) = channel();
                    runner.stop(move |result| {
                        let _ = tx.send(result);
                    });
                    if let Err(_) = rx.recv() {
                        return Err(Error::RunError(RunError::StartStopError(StartStopError::ThreadError)));
                    }
                }

                let connection = try!(rusqlite::Connection::open(&self.path));
                try!(connection.execute("UPDATE scripts SET is_enabled = 0 WHERE id = $1",
                                        &[&id.to_string()]));
            },
            (true, false) => {
                try!(self.start_script(id, &source));
                let connection = try!(rusqlite::Connection::open(&self.path));
                try!(connection.execute("UPDATE scripts SET is_enabled = 1 WHERE id = $1",
                                        &[&id.to_string()]));
            },
            _ => {
                // Nothing to do.
            }
        }

        Ok(())
    }

    /// Remove a script entirely, stopping it if necessary.
    /// If the script cannot be stopped (due to an error), it will not be removed.
    pub fn remove(&mut self, id: &Id<ScriptId>) -> Result<(), Error> {
        try!(self.set_enabled(id, false));
        let connection = try!(rusqlite::Connection::open(&self.path));
        connection.execute("DELETE FROM scripts WHERE id = $1", &[&id.to_string()])
            .map(|_| ())
            .map_err(From::from)
    }

    /// Remove all scripts, stopping any running scripts.
    /// (If any scripts fail to stop, we store and return those errors in a Vec<Error> so that
    /// we ensure that the database always gets wiped.)
    pub fn remove_all(&mut self) -> Result<Vec<Error>, Error> {
        // Get a copy of the keys (so that we don't borow `self` twice).
        let keys: Vec<Id<ScriptId>> = self.runners.keys().cloned().collect();
        let mut errors = Vec::new();
        // Remove any running scripts, storing errors for later return so that we
        // for sure end up nuking the database at the end.
        for id in keys {
            if let Err(err) = self.remove(&id) {
                errors.push(err);
            }
        }
        // Nuke the scripts database.
        let connection = try!(rusqlite::Connection::open(&self.path));
        try!(connection.execute("DELETE FROM scripts", &[])
                .map(|_| ()));
        Ok(errors)
    }

    /// Get the number of currently-running scripts.
    pub fn get_running_count(&self) -> usize {
        self.runners.len()
    }

    /// Get the source for a script with the given id.
    fn get_source(&self, id: &Id<ScriptId>) -> Result<String, Error> {
        let connection = try!(rusqlite::Connection::open(&self.path));
        let mut stmt = try!(connection.prepare("SELECT source FROM scripts WHERE id = $1"));
        let mut rows = try!(stmt.query(&[&id.to_string()]));
        let first_row = try!(try!(rows.nth(0).ok_or(Error::NoSuchScriptError)));
        Ok(try!(first_row.get_checked(0)))
    }

    /// Return true if the script is enabled.
    pub fn is_enabled(&self, id: &Id<ScriptId>) -> bool {
        self.runners.contains_key(id)
    }

    /// Execute a script. Returns an error if the script is already running,
    /// or it won't parse, or it won't compile.
    fn start_script(&mut self, id: &Id<ScriptId>, source: &String) -> Result<(), Error> {
        if self.runners.contains_key(id) {
            return Err(Error::RunError(RunError::StartStopError(StartStopError::AlreadyRunning)));
        }
        let mut runner = Execution::<Env>::new();
        let tx_id = id.clone();
        let tx = self.tx.map(move |event| {
            (tx_id.clone(), event)
        });
        let parsed_source = try!(Script::from_str(source));
        try!(runner.start(self.env.clone(), parsed_source, tx));
        self.runners.insert(id.clone(), runner);
        Ok(())
    }
}

impl From<rusqlite::Error> for Error {
    fn from(err: rusqlite::Error) -> Error {
        Error::SQLError(format!("{:?}", err))
    }
}

impl From<RunError> for Error {
    fn from(err: RunError) -> Error {
        Error::RunError(err)
    }
}

impl From<ParseError> for Error {
    fn from(err: ParseError) -> Error {
        Error::ParseError(format!("{:?}", err))
    }
}
<commit_msg>Insert results into ScriptManager even if the script is disabled.<commit_after>use ast::Script;
use compile::ExecutableDevEnv;
use run::{ Execution, ExecutionEvent, Error as RunError, StartStopError };

use std::collections::HashMap;
use std::path::{ Path as FilePath, PathBuf as FilePathBuf };

use foxbox_taxonomy::api::ResultMap;
use foxbox_taxonomy::parse::*;
use foxbox_taxonomy::util::{ Id };

use rusqlite;
use transformable_channels::mpsc::{ channel, ExtSender, TransformableSender };

/// A ScriptManager error.
#[derive(Serialize, Debug)]
pub enum Error {
    /// The script you requested (by ID) does not exist.
    NoSuchScriptError,

    /// There was an error executing some SQL.
    SQLError(String),

    /// There was an error attempting to run a script. (See `run.rs`.)
    RunError(RunError),

    /// There was an error parsing the script's JSON.
    ParseError(String),
}

/// A type for ensuring type-safety (Id<ScriptId>).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Hash, Eq)]
pub struct ScriptId;

/// ScriptManager stores a persistent database of scripts and executes them.
/// Each script can be individually enabled or disabled.
/// When a script is enabled, it is always running (unless an error occured during launch).
/// Script sources are stored as JSON strings in a SQLite database.
pub struct ScriptManager<Env, T> where Env: ExecutableDevEnv + Clone + 'static {
    env: Env,

    /// The path to the SQLite file to store, e.g. "./database.sqlite"
    path: FilePathBuf,

    /// A map to track currently-executing scripts.
    runners: HashMap<Id<ScriptId>, Execution<Env>>,

    /// The tx end of the channel passed to ScriptManager::new()
    tx: Box<T>,
}

impl<Env, T> ScriptManager<Env, T>
    where Env: ExecutableDevEnv + Clone + 'static,
          T: ExtSender<(Id<ScriptId>, ExecutionEvent)> + TransformableSender<(Id<ScriptId>, ExecutionEvent)> {

    /// Create a ScriptManager using a SQLite database file with the given path, i.e. filename.
    /// If the database file does not exist, it will be created.
    ///
    /// NOTE: You MUST consume the contents of `tx` to prevent memory leaks.
    ///
    /// The database stores the raw script source, but only after the source has been parsed
    /// to ensure validity.
    pub fn new(env: Env, path: &FilePath, tx: Box<T>) -> Result<Self, Error> {

        let connection = try!(rusqlite::Connection::open(&path));
        try!(connection.execute("CREATE TABLE IF NOT EXISTS scripts (
            id          TEXT NOT NULL PRIMARY KEY,
            source      TEXT NOT NULL,
            is_enabled  BOOL NOT NULL DEFAULT 1
        )", &[]));

        Ok(ScriptManager {
            path: path.to_owned(),
            env: env,
            runners: HashMap::new(),
            tx: tx
        })
    }

    /// Load and launch all existing scripts from the database.
    pub fn load(&mut self) -> Result<ResultMap<Id<ScriptId>, (), Error>, Error> {
        let connection = try!(rusqlite::Connection::open(&self.path));
        let mut result_map = HashMap::new();
        let mut stmt = try!(connection.prepare("SELECT id, source, is_enabled FROM scripts"));
        let rows = try!(stmt.query(&[]));

        for result_row in rows {
            let row = try!(result_row);
            let id_string: String = try!(row.get_checked(0));
            let id: Id<ScriptId> = Id::new(&id_string);
            let source: String = try!(row.get_checked(1));
            let is_enabled: bool = try!(row.get_checked(2));

            if is_enabled {
                result_map.insert(
                    id.clone(),
                    self.start_script(&id, &source).map(|_| ()));
            } else {
                result_map.insert(id.clone(), Ok(()));
            }
        }
        Ok(result_map)
    }

    /// Attempt to add a new script. The script will be executed and persisted to disk.
    /// The ID is chosen by the consumer and must be unique.
    pub fn put(&mut self, id: &Id<ScriptId>, source: &String) -> Result<(), Error> {
        try!(self.start_script(&id, &source));

        let connection = try!(rusqlite::Connection::open(&self.path));
        connection.execute("INSERT OR REPLACE INTO scripts (id, source, is_enabled)
                VALUES ($1, $2, $3)", &[&id.to_string(), source, &1])
            .map(|_| ()).map_err(From::from)
    }

    /// Enable or disable a script, starting or stopping the script if necessary.
    pub fn set_enabled(&mut self, id: &Id<ScriptId>, enabled: bool) -> Result<(), Error> {
        let source = try!(self.get_source(id));
        let is_running = self.runners.contains_key(&id);
        match (enabled, is_running) {
            (false, true) => {
                if let Some(mut runner) = self.runners.remove(&id) {
                    let (tx, rx) = channel();
                    runner.stop(move |result| {
                        let _ = tx.send(result);
                    });
                    if let Err(_) = rx.recv() {
                        return Err(Error::RunError(RunError::StartStopError(StartStopError::ThreadError)));
                    }
                }

                let connection = try!(rusqlite::Connection::open(&self.path));
                try!(connection.execute("UPDATE scripts SET is_enabled = 0 WHERE id = $1",
                                        &[&id.to_string()]));
            },
            (true, false) => {
                try!(self.start_script(id, &source));
                let connection = try!(rusqlite::Connection::open(&self.path));
                try!(connection.execute("UPDATE scripts SET is_enabled = 1 WHERE id = $1",
                                        &[&id.to_string()]));
            },
            _ => {
                // Nothing to do.
            }
        }

        Ok(())
    }

    /// Remove a script entirely, stopping it if necessary.
    /// If the script cannot be stopped (due to an error), it will not be removed.
    pub fn remove(&mut self, id: &Id<ScriptId>) -> Result<(), Error> {
        try!(self.set_enabled(id, false));
        let connection = try!(rusqlite::Connection::open(&self.path));
        connection.execute("DELETE FROM scripts WHERE id = $1", &[&id.to_string()])
            .map(|_| ())
            .map_err(From::from)
    }

    /// Remove all scripts, stopping any running scripts.
    /// (If any scripts fail to stop, we store and return those errors in a Vec<Error> so that
    /// we ensure that the database always gets wiped.)
    pub fn remove_all(&mut self) -> Result<Vec<Error>, Error> {
        // Get a copy of the keys (so that we don't borow `self` twice).
        let keys: Vec<Id<ScriptId>> = self.runners.keys().cloned().collect();
        let mut errors = Vec::new();
        // Remove any running scripts, storing errors for later return so that we
        // for sure end up nuking the database at the end.
        for id in keys {
            if let Err(err) = self.remove(&id) {
                errors.push(err);
            }
        }
        // Nuke the scripts database.
        let connection = try!(rusqlite::Connection::open(&self.path));
        try!(connection.execute("DELETE FROM scripts", &[])
                .map(|_| ()));
        Ok(errors)
    }

    /// Get the number of currently-running scripts.
    pub fn get_running_count(&self) -> usize {
        self.runners.len()
    }

    /// Get the source for a script with the given id.
    pub fn get_source(&self, id: &Id<ScriptId>) -> Result<String, Error> {
        let connection = try!(rusqlite::Connection::open(&self.path));
        let mut stmt = try!(connection.prepare("SELECT source FROM scripts WHERE id = $1"));
        let mut rows = try!(stmt.query(&[&id.to_string()]));
        let first_row = try!(try!(rows.nth(0).ok_or(Error::NoSuchScriptError)));
        Ok(try!(first_row.get_checked(0)))
    }

    /// Return true if the script is enabled.
    pub fn is_enabled(&self, id: &Id<ScriptId>) -> bool {
        self.runners.contains_key(id)
    }

    /// Execute a script. Returns an error if the script is already running,
    /// or it won't parse, or it won't compile.
    fn start_script(&mut self, id: &Id<ScriptId>, source: &String) -> Result<(), Error> {
        if self.runners.contains_key(id) {
            return Err(Error::RunError(RunError::StartStopError(StartStopError::AlreadyRunning)));
        }
        let mut runner = Execution::<Env>::new();
        let tx_id = id.clone();
        let tx = self.tx.map(move |event| {
            (tx_id.clone(), event)
        });
        let parsed_source = try!(Script::from_str(source));
        try!(runner.start(self.env.clone(), parsed_source, tx));
        self.runners.insert(id.clone(), runner);
        Ok(())
    }
}

impl From<rusqlite::Error> for Error {
    fn from(err: rusqlite::Error) -> Error {
        Error::SQLError(format!("{:?}", err))
    }
}

impl From<RunError> for Error {
    fn from(err: RunError) -> Error {
        Error::RunError(err)
    }
}

impl From<ParseError> for Error {
    fn from(err: ParseError) -> Error {
        Error::ParseError(format!("{:?}", err))
    }
}
<|endoftext|>


<commit_before>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
use comm;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta(field: &mut Field, depth: u32, last_pos: Pos, player: Player, trajectories_pruning: &TrajectoriesPruning, mut alpha: i32, beta: i32, empty_board: &mut Vec<u32>) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  if moves.is_empty() {
    return field.score(player);
  }
  for pos in moves {
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::new_from_last(field, enemy, depth - 1, empty_board, trajectories_pruning, pos);
    let mut cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -alpha - 1, -alpha, empty_board);
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, empty_board);
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel(field: &mut Field, player: Player, depth: u32, alpha: i32, beta: i32, trajectories_pruning: &TrajectoriesPruning, empty_board: &mut Vec<u32>, best_move: &mut Option<Pos>) -> i32 {
  info!(target: MINIMAX_STR, "Starting parellel alpha beta with depth {}, player {} and beta {}.", depth, player, beta);
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  debug!(target: MINIMAX_STR, "Moves in consideration: {:?}.", moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>());
  let (producer, consumer) = comm::spmc::unbounded::new();
  for pos in moves {
    producer.send(pos).ok();
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let atomic_best_move = AtomicUsize::new(0);
  let mut guards = Vec::with_capacity(threads_count);
    for _ in 0 .. threads_count {
    guards.push(thread::scoped(|| {
      let local_consumer = consumer.clone();
      let mut local_field = field.clone();
      let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
      let enemy = player.next();
      while let Some(pos) = local_consumer.recv_async().ok() {
        local_field.put_point(pos, player);
        let next_trajectories_pruning = TrajectoriesPruning::new_from_last(&mut local_field, enemy, depth - 1, &mut local_empty_board, &trajectories_pruning, pos);
        let cur_alpha = atomic_alpha.load(Ordering::SeqCst) as i32;
        if cur_alpha >= beta {
          break;
        }
        let mut cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -cur_alpha - 1, -cur_alpha, &mut local_empty_board);
        if cur_estimation > cur_alpha {
          cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, &mut local_empty_board);
        }
        local_field.undo();
        loop {
          let last_pos = atomic_best_move.load(Ordering::SeqCst);
          let last_alpha = atomic_alpha.load(Ordering::SeqCst);
          if cur_estimation > last_alpha as i32 {
            if atomic_alpha.compare_and_swap(last_alpha, cur_estimation as isize, Ordering::SeqCst) == last_alpha && atomic_best_move.compare_and_swap(last_pos, pos, Ordering::SeqCst) == last_pos {
              debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
              break;
            }
          } else {
            debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation > cur_alpha { if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" } } else { "Upper bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
            break;
          }
        }
      }
    }));
  }
  drop(guards);
  let result = atomic_best_move.load(Ordering::SeqCst);
  if result != 0 {
    let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
    info!(target: MINIMAX_STR, "Best move is ({}, {}), estimation is {}.", field.to_x(result), field.to_y(result), cur_alpha);
    *best_move = Some(result);
    cur_alpha as i32
  } else {
    field.score(player)
  }
}

pub fn minimax(field: &mut Field, player: Player, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut best_move);
  let enemy = player.next();
  let mut enemy_best_move = None;
  let enemy_trajectories_pruning = TrajectoriesPruning::new_from_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
  info!(target: MINIMAX_STR, "Calculating of enemy estimation with upper bound {}. Player is {}", -estimation + 1, enemy);
  if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move) < estimation {
    info!(target: MINIMAX_STR,  "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.", best_move.map(|pos| (field.to_x(pos), field.to_y(pos))), estimation);
    best_move
  } else {
    info!(target: MINIMAX_STR,  "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.", estimation);
    None
  }
}
<commit_msg>Small fix.<commit_after>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
use comm;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta(field: &mut Field, depth: u32, last_pos: Pos, player: Player, trajectories_pruning: &TrajectoriesPruning, mut alpha: i32, beta: i32, empty_board: &mut Vec<u32>) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  if moves.is_empty() {
    return field.score(player);
  }
  for pos in moves {
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::new_from_last(field, enemy, depth - 1, empty_board, trajectories_pruning, pos);
    let mut cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -alpha - 1, -alpha, empty_board);
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, empty_board);
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel(field: &mut Field, player: Player, depth: u32, alpha: i32, beta: i32, trajectories_pruning: &TrajectoriesPruning, empty_board: &mut Vec<u32>, best_move: &mut Option<Pos>) -> i32 {
  info!(target: MINIMAX_STR, "Starting parellel alpha beta with depth {}, player {} and beta {}.", depth, player, beta);
  if depth == 0 {
    *best_move = None;
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  debug!(target: MINIMAX_STR, "Moves in consideration: {:?}.", moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>());
  if moves.is_empty() {
    *best_move = None;
    return field.score(player);
  }
  let (producer, consumer) = comm::spmc::unbounded::new();
  for pos in moves {
    producer.send(pos).ok();
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let atomic_best_move = AtomicUsize::new(0);
  let mut guards = Vec::with_capacity(threads_count);
    for _ in 0 .. threads_count {
    guards.push(thread::scoped(|| {
      let local_consumer = consumer.clone();
      let mut local_field = field.clone();
      let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
      let enemy = player.next();
      while let Some(pos) = local_consumer.recv_async().ok() {
        local_field.put_point(pos, player);
        let next_trajectories_pruning = TrajectoriesPruning::new_from_last(&mut local_field, enemy, depth - 1, &mut local_empty_board, &trajectories_pruning, pos);
        let cur_alpha = atomic_alpha.load(Ordering::SeqCst) as i32;
        if cur_alpha >= beta {
          break;
        }
        let mut cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -cur_alpha - 1, -cur_alpha, &mut local_empty_board);
        if cur_estimation > cur_alpha {
          cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, &mut local_empty_board);
        }
        local_field.undo();
        loop {
          let last_pos = atomic_best_move.load(Ordering::SeqCst);
          let last_alpha = atomic_alpha.load(Ordering::SeqCst);
          if cur_estimation > last_alpha as i32 {
            if atomic_alpha.compare_and_swap(last_alpha, cur_estimation as isize, Ordering::SeqCst) == last_alpha && atomic_best_move.compare_and_swap(last_pos, pos, Ordering::SeqCst) == last_pos {
              debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
              break;
            }
          } else {
            debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation > cur_alpha { if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" } } else { "Upper bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
            break;
          }
        }
      }
    }));
  }
  drop(guards);
  let result = atomic_best_move.load(Ordering::SeqCst);
  if result != 0 {
    info!(target: MINIMAX_STR, "Best move is ({}, {}).", field.to_x(result), field.to_y(result));
    *best_move = Some(result);
  } else {
    info!(target: MINIMAX_STR, "Best move is not found.");
    *best_move = None;
  }
  let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
  info!(target: MINIMAX_STR, "Estimation is {}.", cur_alpha);
  cur_alpha as i32
}

pub fn minimax(field: &mut Field, player: Player, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut best_move);
  let enemy = player.next();
  let mut enemy_best_move = best_move;
  let enemy_trajectories_pruning = TrajectoriesPruning::new_from_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
  info!(target: MINIMAX_STR, "Calculating of enemy estimation with upper bound {}. Player is {}", -estimation + 1, enemy);
  if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move) < estimation {
    info!(target: MINIMAX_STR,  "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.", best_move.map(|pos| (field.to_x(pos), field.to_y(pos))), estimation);
    best_move
  } else {
    info!(target: MINIMAX_STR,  "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.", estimation);
    None
  }
}
<|endoftext|>


<commit_before>//! Implements the HTTPMU and lower layers of the `UPnP` standard.
//!
//! This module deals with primitives for working with external libraries to write
//! data to UDP sockets as a stream, and read data from UDP sockets as packets.

use std::io::{self, ErrorKind};
use std::net::{ToSocketAddrs, UdpSocket};
use std::net::{SocketAddr, IpAddr};

#[cfg(not(windows))]
use net2::unix::UnixUdpBuilderExt;
use net2::{UdpSocketExt, UdpBuilder};

pub mod connector;
pub mod packet;
pub mod sender;

pub enum IpVersionMode {
    V4Only,
    V6Only,
    Any,
}

impl IpVersionMode {
    pub fn from_addr<A: ToSocketAddrs>(addr: A) -> io::Result<Self> {
        match try!(addr_from_trait(addr)) {
            SocketAddr::V4(_) => Ok(IpVersionMode::V4Only),
            SocketAddr::V6(_) => Ok(IpVersionMode::V6Only),
        }
    }
}

/// Accept a type implementing `ToSocketAddrs` and tries to extract the first address.
pub fn addr_from_trait<A: ToSocketAddrs>(addr: A) -> io::Result<SocketAddr> {
    let mut sock_iter = try!(addr.to_socket_addrs());

    match sock_iter.next() {
        Some(n) => Ok(n),
        None => Err(io::Error::new(ErrorKind::InvalidInput, "Failed To Parse SocketAddr")),
    }
}

/// Bind to a `UdpSocket`, setting `SO_REUSEADDR` on the underlying socket before binding.
pub fn bind_reuse<A: ToSocketAddrs>(local_addr: A) -> io::Result<UdpSocket> {
    let local_addr = try!(addr_from_trait(local_addr));

    let builder = match local_addr {
        SocketAddr::V4(_) => try!(UdpBuilder::new_v4()),
        SocketAddr::V6(_) => try!(UdpBuilder::new_v6()),
    };

    try!(reuse_port(&builder));
    builder.bind(local_addr)
}

#[cfg(windows)]
fn reuse_port(builder: &UdpBuilder) -> io::Result<()> {
    // Allow wildcards + specific to not overlap
    try!(builder.reuse_address(true));
    Ok(())
}

#[cfg(not(windows))]
fn reuse_port(builder: &UdpBuilder) -> io::Result<()> {
    // Allow wildcards + specific to not overlap
    try!(builder.reuse_address(true));
    // Allow multiple listeners on the same port
    try!(builder.reuse_port(true));
    Ok(())
}

/// Join a multicast address on the current `UdpSocket`.
pub fn join_multicast(sock: &UdpSocket, iface: &SocketAddr, mcast_addr: &IpAddr) -> io::Result<()> {
    match (iface, mcast_addr) {
        (&SocketAddr::V4(ref i), &IpAddr::V4(ref m)) => sock.join_multicast_v4(m, i.ip()),
        (&SocketAddr::V6(ref i), &IpAddr::V6(ref m)) => sock.join_multicast_v6(m, i.scope_id()),
        _ => {
            Err(io::Error::new(ErrorKind::InvalidInput,
                               "Multicast And Interface Addresses Are Not The Same Version"))
        }
    }
}

/// Leave a multicast address on the current `UdpSocket`.
#[allow(dead_code)] // TODO: call this from somewhere?
pub fn leave_multicast(sock: &UdpSocket, iface_addr: &SocketAddr, mcast_addr: &SocketAddr) -> io::Result<()> {
    match (iface_addr, mcast_addr) {
        (&SocketAddr::V4(ref i), &SocketAddr::V4(ref m)) => sock.leave_multicast_v4(m.ip(), i.ip()),
        (&SocketAddr::V6(ref i), &SocketAddr::V6(ref m)) => sock.leave_multicast_v6(m.ip(), i.scope_id()),
        _ => {
            Err(io::Error::new(ErrorKind::InvalidInput,
                               "Multicast And Interface Addresses Are Not The Same Version"))
        }
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn positive_addr_from_trait() {
        super::addr_from_trait("192.168.0.1:0").unwrap();
    }

    #[test]
    #[should_panic]
    fn negative_addr_from_trait() {
        super::addr_from_trait("192.168.0.1").unwrap();
    }
}
<commit_msg>Remove Unused Import<commit_after>//! Implements the HTTPMU and lower layers of the `UPnP` standard.
//!
//! This module deals with primitives for working with external libraries to write
//! data to UDP sockets as a stream, and read data from UDP sockets as packets.

use std::io::{self, ErrorKind};
use std::net::{ToSocketAddrs, UdpSocket};
use std::net::{SocketAddr, IpAddr};

#[cfg(not(windows))]
use net2::unix::UnixUdpBuilderExt;
use net2::{UdpBuilder};

pub mod connector;
pub mod packet;
pub mod sender;

pub enum IpVersionMode {
    V4Only,
    V6Only,
    Any,
}

impl IpVersionMode {
    pub fn from_addr<A: ToSocketAddrs>(addr: A) -> io::Result<Self> {
        match try!(addr_from_trait(addr)) {
            SocketAddr::V4(_) => Ok(IpVersionMode::V4Only),
            SocketAddr::V6(_) => Ok(IpVersionMode::V6Only),
        }
    }
}

/// Accept a type implementing `ToSocketAddrs` and tries to extract the first address.
pub fn addr_from_trait<A: ToSocketAddrs>(addr: A) -> io::Result<SocketAddr> {
    let mut sock_iter = try!(addr.to_socket_addrs());

    match sock_iter.next() {
        Some(n) => Ok(n),
        None => Err(io::Error::new(ErrorKind::InvalidInput, "Failed To Parse SocketAddr")),
    }
}

/// Bind to a `UdpSocket`, setting `SO_REUSEADDR` on the underlying socket before binding.
pub fn bind_reuse<A: ToSocketAddrs>(local_addr: A) -> io::Result<UdpSocket> {
    let local_addr = try!(addr_from_trait(local_addr));

    let builder = match local_addr {
        SocketAddr::V4(_) => try!(UdpBuilder::new_v4()),
        SocketAddr::V6(_) => try!(UdpBuilder::new_v6()),
    };

    try!(reuse_port(&builder));
    builder.bind(local_addr)
}

#[cfg(windows)]
fn reuse_port(builder: &UdpBuilder) -> io::Result<()> {
    // Allow wildcards + specific to not overlap
    try!(builder.reuse_address(true));
    Ok(())
}

#[cfg(not(windows))]
fn reuse_port(builder: &UdpBuilder) -> io::Result<()> {
    // Allow wildcards + specific to not overlap
    try!(builder.reuse_address(true));
    // Allow multiple listeners on the same port
    try!(builder.reuse_port(true));
    Ok(())
}

/// Join a multicast address on the current `UdpSocket`.
pub fn join_multicast(sock: &UdpSocket, iface: &SocketAddr, mcast_addr: &IpAddr) -> io::Result<()> {
    match (iface, mcast_addr) {
        (&SocketAddr::V4(ref i), &IpAddr::V4(ref m)) => sock.join_multicast_v4(m, i.ip()),
        (&SocketAddr::V6(ref i), &IpAddr::V6(ref m)) => sock.join_multicast_v6(m, i.scope_id()),
        _ => {
            Err(io::Error::new(ErrorKind::InvalidInput,
                               "Multicast And Interface Addresses Are Not The Same Version"))
        }
    }
}

/// Leave a multicast address on the current `UdpSocket`.
#[allow(dead_code)] // TODO: call this from somewhere?
pub fn leave_multicast(sock: &UdpSocket, iface_addr: &SocketAddr, mcast_addr: &SocketAddr) -> io::Result<()> {
    match (iface_addr, mcast_addr) {
        (&SocketAddr::V4(ref i), &SocketAddr::V4(ref m)) => sock.leave_multicast_v4(m.ip(), i.ip()),
        (&SocketAddr::V6(ref i), &SocketAddr::V6(ref m)) => sock.leave_multicast_v6(m.ip(), i.scope_id()),
        _ => {
            Err(io::Error::new(ErrorKind::InvalidInput,
                               "Multicast And Interface Addresses Are Not The Same Version"))
        }
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn positive_addr_from_trait() {
        super::addr_from_trait("192.168.0.1:0").unwrap();
    }

    #[test]
    #[should_panic]
    fn negative_addr_from_trait() {
        super::addr_from_trait("192.168.0.1").unwrap();
    }
}
<|endoftext|>