//! A simple tic tac toe implementation. //! //! You specify two wasm modules with a certain ABI and this //! program instantiates these modules and runs a module //! on turn-by-turn basis. //! extern crate parity_wasm; extern crate wasmi; extern crate wasmi_derive; use std::env; use std::fmt; use std::fs::File; use wasmi::{Error as InterpreterError, HostError, ImportsBuilder, ModuleInstance, ModuleRef}; #[derive(Debug)] pub enum Error { OutOfRange, AlreadyPlayed, AlreadyOccupied, Interpreter(InterpreterError), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) } } impl From for Error { fn from(e: InterpreterError) -> Self { Error::Interpreter(e) } } impl HostError for Error {} mod tictactoe { use super::Error; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Player { X, O, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum GameResult { Draw, Won(Player), } #[derive(Debug)] pub struct Game { board: [Option; 9], } impl Game { pub fn new() -> Game { Game { board: [None; 9] } } pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> { if idx < 0 || idx > 9 { return Err(Error::OutOfRange); } if self.board[idx as usize] != None { return Err(Error::AlreadyOccupied); } self.board[idx as usize] = Some(player); Ok(()) } pub fn get(&self, idx: i32) -> Result, Error> { if idx < 0 || idx > 9 { return Err(Error::OutOfRange); } Ok(self.board[idx as usize]) } pub fn game_result(&self) -> Option { // 0, 1, 2 // 3, 4, 5 // 6, 7, 8 let patterns = &[ // Rows (0, 1, 2), (3, 4, 5), (6, 7, 8), // Columns (0, 3, 6), (1, 4, 7), (2, 5, 8), // Diagonals (0, 4, 8), (2, 4, 6), ]; // Returns Some(player) if all cells contain same Player. let all_same = |i1: usize, i2: usize, i3: usize| -> Option { if self.board[i1].is_none() { return None; } if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] { return self.board[i1]; } None }; for &(i1, i2, i3) in patterns { if let Some(player) = all_same(i1, i2, i3) { return Some(GameResult::Won(player)); } } // Ok, there is no winner. Check if it's draw. let all_occupied = self.board.iter().all(|&cell| cell.is_some()); if all_occupied { Some(GameResult::Draw) } else { // Nah, there are still empty cells left. None } } } } struct Runtime<'a> { player: tictactoe::Player, made_turn: bool, game: &'a mut tictactoe::Game, } #[wasmi_derive::derive_externals] impl<'a> Runtime<'a> { /// Puts a mark of the current player on the given cell. /// /// Traps if the index is out of bounds of game field or if the player /// already made its turn. pub fn set(&mut self, idx: i32) -> Result<(), Error> { if self.made_turn { return Err(Error::AlreadyPlayed); } self.game.set(idx, self.player)?; Ok(()) } /// Returns the player index at the specified cell. /// /// 0 - unoccupied /// 1 - player X /// 2 - player O /// /// Traps if the index is out of bounds of game field. pub fn get(&self, idx: i32) -> Result { use tictactoe::Player; Ok(match self.game.get(idx)? { None => 0, Some(Player::X) => 1, Some(Player::O) => 2, }) } } fn instantiate(path: &str) -> Result { let module = { use std::io::prelude::*; let mut file = File::open(path).unwrap(); let mut wasm_buf = Vec::new(); file.read_to_end(&mut wasm_buf).unwrap(); wasmi::Module::from_buffer(&wasm_buf)? }; let instance = { let resolver = Runtime::resolver(); let mut imports = ImportsBuilder::new(); imports.push_resolver("env", &resolver); ModuleInstance::new(&module, &imports)?.assert_no_start() }; Ok(instance) } fn play( x_instance: ModuleRef, o_instance: ModuleRef, game: &mut tictactoe::Game, ) -> Result { let mut turn_of = tictactoe::Player::X; let game_result = loop { let (instance, next_turn_of) = match turn_of { tictactoe::Player::X => (&x_instance, tictactoe::Player::O), tictactoe::Player::O => (&o_instance, tictactoe::Player::X), }; { let mut runtime = Runtime { player: turn_of, game: game, made_turn: false, }; let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?; } match game.game_result() { Some(game_result) => break game_result, None => {} } turn_of = next_turn_of; }; Ok(game_result) } fn main() { let mut game = tictactoe::Game::new(); let args: Vec<_> = env::args().collect(); if args.len() < 3 { println!("Usage: {} ", args[0]); return; } // Instantiate modules of X and O players. let x_instance = instantiate(&args[1]).expect("X player module to load"); let o_instance = instantiate(&args[2]).expect("Y player module to load"); let result = play(x_instance, o_instance, &mut game); println!("result = {:?}, game = {:#?}", result, game); }