2017-12-17 18:29:06 +03:00
|
|
|
extern crate parity_wasm;
|
|
|
|
|
|
|
|
use std::env;
|
|
|
|
use std::fmt;
|
|
|
|
use std::rc::Rc;
|
2018-01-08 15:10:50 +03:00
|
|
|
use parity_wasm::elements::{FunctionType, ValueType, TableType, GlobalType, MemoryType};
|
2017-12-18 15:18:53 +03:00
|
|
|
use parity_wasm::interpreter::{
|
2018-01-05 17:52:29 +03:00
|
|
|
Error as InterpreterError, ModuleInstance, UserError,
|
|
|
|
HostFuncIndex, Externals, RuntimeValue, GlobalInstance, TableInstance, MemoryInstance,
|
2018-01-08 15:10:50 +03:00
|
|
|
TableRef, MemoryRef, GlobalRef, FuncRef, TryInto, ImportResolver, FuncInstance
|
2017-12-20 13:06:38 +03:00
|
|
|
};
|
2018-01-08 15:10:50 +03:00
|
|
|
use parity_wasm::elements::{Error as DeserializationError};
|
|
|
|
use parity_wasm::ValidationError;
|
2017-12-17 18:29:06 +03:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
OutOfRange,
|
|
|
|
AlreadyOccupied,
|
|
|
|
Interpreter(InterpreterError),
|
2018-01-08 15:10:50 +03:00
|
|
|
Deserialize(DeserializationError),
|
|
|
|
Validation(ValidationError),
|
2017-12-17 18:29:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{:?}", self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<InterpreterError> for Error {
|
|
|
|
fn from(e: InterpreterError) -> Self {
|
|
|
|
Error::Interpreter(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-08 15:10:50 +03:00
|
|
|
impl From<DeserializationError> for Error {
|
|
|
|
fn from(e: DeserializationError) -> Error {
|
|
|
|
Error::Deserialize(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ValidationError> for Error {
|
|
|
|
fn from(e: ValidationError) -> Error {
|
|
|
|
Error::Validation(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-17 18:29:06 +03:00
|
|
|
impl UserError 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),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Player {
|
|
|
|
pub fn into_i32(maybe_player: Option<Player>) -> i32 {
|
|
|
|
match maybe_player {
|
|
|
|
None => 0,
|
|
|
|
Some(Player::X) => 1,
|
|
|
|
Some(Player::O) => 2,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Game {
|
2017-12-20 13:06:38 +03:00
|
|
|
board: [Option<Player>; 9],
|
2017-12-17 18:29:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Game {
|
|
|
|
pub fn new() -> Game {
|
|
|
|
Game {
|
2017-12-20 13:06:38 +03:00
|
|
|
board: [None; 9],
|
2017-12-17 18:29:06 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-20 13:06:38 +03:00
|
|
|
pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> {
|
2017-12-17 18:29:06 +03:00
|
|
|
if idx < 0 || idx > 9 {
|
|
|
|
return Err(Error::OutOfRange);
|
|
|
|
}
|
2017-12-20 13:06:38 +03:00
|
|
|
if self.board[idx as usize] != None {
|
2017-12-17 18:29:06 +03:00
|
|
|
return Err(Error::AlreadyOccupied);
|
|
|
|
}
|
2017-12-20 13:06:38 +03:00
|
|
|
self.board[idx as usize] = Some(player);
|
2017-12-17 18:29:06 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get(&self, idx: i32) -> Result<Option<Player>, Error> {
|
|
|
|
if idx < 0 || idx > 9 {
|
|
|
|
return Err(Error::OutOfRange);
|
|
|
|
}
|
2017-12-20 13:06:38 +03:00
|
|
|
Ok(self.board[idx as usize])
|
2017-12-17 18:29:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn game_result(&self) -> Option<GameResult> {
|
|
|
|
// 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<Player> {
|
2017-12-20 13:06:38 +03:00
|
|
|
if self.board[i1].is_none() {
|
2017-12-17 18:29:06 +03:00
|
|
|
return None;
|
|
|
|
}
|
2017-12-20 13:06:38 +03:00
|
|
|
if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] {
|
|
|
|
return self.board[i1];
|
2017-12-17 18:29:06 +03:00
|
|
|
}
|
|
|
|
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.
|
2017-12-20 13:06:38 +03:00
|
|
|
let all_occupied = self.board.iter().all(|&cell| cell.is_some());
|
2017-12-17 18:29:06 +03:00
|
|
|
if all_occupied {
|
|
|
|
Some(GameResult::Draw)
|
|
|
|
} else {
|
|
|
|
// Nah, there are still empty cells left.
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Runtime<'a> {
|
|
|
|
player: tictactoe::Player,
|
2017-12-20 13:06:38 +03:00
|
|
|
game: &'a mut tictactoe::Game,
|
2017-12-17 18:29:06 +03:00
|
|
|
}
|
|
|
|
|
2018-01-05 17:52:29 +03:00
|
|
|
const SET_FUNC_INDEX: HostFuncIndex = 0;
|
|
|
|
const GET_FUNC_INDEX: HostFuncIndex = 1;
|
|
|
|
|
|
|
|
impl<'a> Externals for Runtime<'a> {
|
|
|
|
fn invoke_index(
|
|
|
|
&mut self,
|
|
|
|
index: HostFuncIndex,
|
|
|
|
args: &[RuntimeValue],
|
|
|
|
) -> Result<Option<RuntimeValue>, InterpreterError> {
|
|
|
|
match index {
|
|
|
|
SET_FUNC_INDEX => {
|
|
|
|
let idx: i32 = args[0].try_into().unwrap();
|
|
|
|
self.game.set(idx, self.player)?;
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
GET_FUNC_INDEX => {
|
|
|
|
let idx: i32 = args[0].try_into().unwrap();
|
|
|
|
let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?);
|
|
|
|
Ok(Some(val.into()))
|
|
|
|
}
|
|
|
|
_ => panic!("unknown function index")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_signature(&self, index: HostFuncIndex, sig: &FunctionType) -> bool {
|
|
|
|
match index {
|
|
|
|
SET_FUNC_INDEX => {
|
|
|
|
sig.params() == &[ValueType::I32] && sig.return_type() == None
|
|
|
|
}
|
|
|
|
GET_FUNC_INDEX => {
|
|
|
|
sig.params() == &[ValueType::I32] && sig.return_type() == Some(ValueType::I32)
|
|
|
|
}
|
|
|
|
_ => panic!("unknown function index")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn memory_by_index(&self, _index: usize) -> &MemoryInstance {
|
|
|
|
panic!("host module doesn't export any memories")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn table_by_index(&self, _index: usize) -> &TableInstance {
|
|
|
|
panic!("host module doesn't export any tables")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn global_by_index(&self, _index: usize) -> &GlobalInstance {
|
|
|
|
panic!("host module doesn't export any globals")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct RuntimeImportResolver;
|
|
|
|
|
|
|
|
impl<'a> ImportResolver for RuntimeImportResolver {
|
|
|
|
fn resolve_func(
|
|
|
|
&self,
|
|
|
|
field_name: &str,
|
|
|
|
_func_type: &FunctionType,
|
|
|
|
) -> Result<FuncRef, InterpreterError> {
|
|
|
|
let func_ref = match field_name {
|
|
|
|
"set" => {
|
|
|
|
FuncInstance::alloc_host(FunctionType::new(vec![ValueType::I32], None), SET_FUNC_INDEX)
|
|
|
|
},
|
|
|
|
"get" => FuncInstance::alloc_host(FunctionType::new(vec![ValueType::I32], Some(ValueType::I32)), GET_FUNC_INDEX),
|
|
|
|
_ => return Err(
|
|
|
|
InterpreterError::Function(
|
|
|
|
format!("host module doesn't export function with name {}", field_name)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
};
|
|
|
|
Ok(func_ref)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_global(
|
|
|
|
&self,
|
|
|
|
_field_name: &str,
|
|
|
|
_global_type: &GlobalType,
|
|
|
|
) -> Result<GlobalRef, InterpreterError> {
|
|
|
|
Err(
|
|
|
|
InterpreterError::Global("host module doesn't export any globals".to_owned())
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_memory(
|
|
|
|
&self,
|
|
|
|
_field_name: &str,
|
|
|
|
_memory_type: &MemoryType,
|
|
|
|
) -> Result<MemoryRef, InterpreterError> {
|
|
|
|
Err(
|
|
|
|
InterpreterError::Global("host module doesn't export any memories".to_owned())
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_table(
|
|
|
|
&self,
|
|
|
|
_field_name: &str,
|
|
|
|
_table_type: &TableType,
|
|
|
|
) -> Result<TableRef, InterpreterError> {
|
|
|
|
Err(
|
|
|
|
InterpreterError::Global("host module doesn't export any tables".to_owned())
|
|
|
|
)
|
|
|
|
}
|
2017-12-19 20:46:05 +03:00
|
|
|
}
|
|
|
|
|
2017-12-20 13:06:38 +03:00
|
|
|
fn instantiate(
|
2018-01-08 15:10:50 +03:00
|
|
|
path: &str,
|
2017-12-19 20:46:05 +03:00
|
|
|
) -> Result<Rc<ModuleInstance>, Error> {
|
2018-01-08 15:10:50 +03:00
|
|
|
let module = parity_wasm::deserialize_file(path)?;
|
|
|
|
let validated_module = parity_wasm::validate_module(module)?;
|
|
|
|
|
|
|
|
let instance = ModuleInstance::new(&validated_module)
|
2018-01-05 17:52:29 +03:00
|
|
|
.with_import("env", &RuntimeImportResolver)
|
|
|
|
.assert_no_start()?;
|
2017-12-17 18:29:06 +03:00
|
|
|
|
|
|
|
Ok(instance)
|
|
|
|
}
|
|
|
|
|
2018-01-05 17:52:29 +03:00
|
|
|
fn play(
|
|
|
|
x_instance: Rc<ModuleInstance>,
|
|
|
|
o_instance: Rc<ModuleInstance>,
|
|
|
|
game: &mut tictactoe::Game,
|
2017-12-17 18:29:06 +03:00
|
|
|
) -> Result<tictactoe::GameResult, Error> {
|
|
|
|
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),
|
|
|
|
};
|
|
|
|
|
2017-12-20 13:06:38 +03:00
|
|
|
{
|
|
|
|
let mut runtime = Runtime {
|
|
|
|
player: turn_of,
|
|
|
|
game: game,
|
|
|
|
};
|
2018-01-05 17:52:29 +03:00
|
|
|
let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?;
|
2017-12-20 13:06:38 +03:00
|
|
|
}
|
2017-12-17 18:29:06 +03:00
|
|
|
|
|
|
|
match game.game_result() {
|
|
|
|
Some(game_result) => break game_result,
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
turn_of = next_turn_of;
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(game_result)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
2017-12-20 13:06:38 +03:00
|
|
|
let mut game = tictactoe::Game::new();
|
2017-12-17 18:29:06 +03:00
|
|
|
|
|
|
|
let args: Vec<_> = env::args().collect();
|
|
|
|
if args.len() < 3 {
|
|
|
|
println!("Usage: {} <x player module> <y player module>", args[0]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-05 17:52:29 +03:00
|
|
|
// Instantiate modules of X and O players.
|
2018-01-08 15:10:50 +03:00
|
|
|
let x_instance = instantiate(&args[1]).expect("X player module to load");
|
|
|
|
let o_instance = instantiate(&args[2]).expect("Y player module to load");
|
2018-01-05 17:52:29 +03:00
|
|
|
|
|
|
|
let result = play(x_instance, o_instance, &mut game);
|
2017-12-17 18:29:06 +03:00
|
|
|
println!("result = {:?}, game = {:#?}", result, game);
|
|
|
|
}
|