parity-wasm/examples/tictactoe.rs

298 lines
6.6 KiB
Rust
Raw Normal View History

2017-12-17 18:29:06 +03:00
extern crate parity_wasm;
use std::env;
use std::fmt;
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-10 18:42:28 +03:00
Error as InterpreterError, ModuleInstance, ModuleRef,
Externals, RuntimeValue, TableRef, MemoryRef, GlobalRef,
2018-01-11 15:16:30 +03:00
FuncRef, TryInto, ModuleImportResolver, FuncInstance, HostError,
ImportsBuilder,
};
2018-01-10 18:42:28 +03:00
use parity_wasm::validation::validate_module;
2018-01-08 15:10:50 +03:00
use parity_wasm::elements::{Error as DeserializationError};
2018-01-10 18:42:28 +03:00
use parity_wasm::validation::{Error as 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)
}
}
2018-01-10 18:42:28 +03:00
impl HostError for Error {}
2017-12-17 18:29:06 +03:00
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 {
board: [Option<Player>; 9],
2017-12-17 18:29:06 +03:00
}
impl Game {
pub fn new() -> Game {
Game {
board: [None; 9],
2017-12-17 18:29:06 +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);
}
if self.board[idx as usize] != None {
2017-12-17 18:29:06 +03:00
return Err(Error::AlreadyOccupied);
}
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);
}
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> {
if self.board[i1].is_none() {
2017-12-17 18:29:06 +03:00
return None;
}
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.
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,
game: &'a mut tictactoe::Game,
2017-12-17 18:29:06 +03:00
}
2018-01-10 18:42:28 +03:00
const SET_FUNC_INDEX: usize = 0;
const GET_FUNC_INDEX: usize = 1;
impl<'a> Externals for Runtime<'a> {
fn invoke_index(
&mut self,
2018-01-10 18:42:28 +03:00
index: usize,
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")
}
}
}
2018-01-11 12:59:23 +03:00
struct RuntimeModuleImportResolver;
2018-01-11 12:59:23 +03:00
impl<'a> ModuleImportResolver for RuntimeModuleImportResolver {
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
}
2018-01-11 15:16:30 +03:00
fn instantiate(path: &str) -> Result<ModuleRef, Error> {
2018-01-08 15:10:50 +03:00
let module = parity_wasm::deserialize_file(path)?;
2018-01-10 18:42:28 +03:00
let validated_module = validate_module(module)?;
2018-01-08 15:10:50 +03:00
2018-01-11 15:16:30 +03:00
let mut imports = ImportsBuilder::new();
imports.push_resolver("env", &RuntimeModuleImportResolver);
let instance = ModuleInstance::new(&validated_module, &imports)?
.assert_no_start();
2017-12-17 18:29:06 +03:00
Ok(instance)
}
fn play(
x_instance: ModuleRef,
o_instance: ModuleRef,
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),
};
{
let mut runtime = Runtime {
player: turn_of,
game: game,
};
let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?;
}
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() {
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;
}
// 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");
let result = play(x_instance, o_instance, &mut game);
2017-12-17 18:29:06 +03:00
println!("result = {:?}, game = {:#?}", result, game);
}