├── gomoku.pdf ├── src ├── display │ ├── mod.rs │ ├── mouse.rs │ ├── draw.rs │ └── console.rs ├── board │ ├── mod.rs │ ├── tile.rs │ ├── team.rs │ ├── fn_str.rs │ ├── parse.rs │ ├── test_win.rs │ ├── go_board.rs │ ├── test_capture.rs │ └── test_free_threes.rs ├── ai │ ├── mod.rs │ ├── turn.rs │ ├── move_to_evaluate.rs │ ├── test_move_to_evaluate.rs │ ├── heuristic.rs │ ├── decision.rs │ └── test_decision.rs ├── main.rs ├── cli.yml ├── one_test.rs ├── cmd_option.rs └── bench.rs ├── .gitignore ├── .travis.yml ├── input ├── Cargo.toml ├── README.md ├── bench_result └── Cargo.lock /gomoku.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbersac/gomoku_42/HEAD/gomoku.pdf -------------------------------------------------------------------------------- /src/display/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod console; 2 | pub mod draw; 3 | pub mod mouse; 4 | 5 | pub use self::console::{Console}; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | output 2 | *.bk 3 | ._gomoku.pdf 4 | 5 | # Compiled files 6 | *.o 7 | *.so 8 | *.rlib 9 | *.dll 10 | *.a 11 | *.dylib 12 | *.la 13 | 14 | # Executables 15 | *.exe 16 | 17 | # Generated by Cargo 18 | /target/ 19 | /.rust/ 20 | /.rust/* 21 | 22 | # Swap 23 | *.rs.swp 24 | *.swp 25 | -------------------------------------------------------------------------------- /src/board/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod tile; 2 | mod go_board; 3 | mod team; 4 | mod parse; 5 | mod fn_str; 6 | #[cfg(test)] 7 | mod test_win; 8 | #[cfg(test)] 9 | mod test_free_threes; 10 | #[cfg(test)] 11 | mod test_capture; 12 | 13 | pub use self::go_board::{GoBoard}; 14 | pub use self::tile::{Tile}; 15 | pub use self::team::{Team}; 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudo: false 3 | language: rust 4 | rust: 5 | - 1.3.0 6 | - stable 7 | - nightly 8 | - beta 9 | 10 | matrix: 11 | allow_failures: 12 | - rust: 1.3.0 13 | - rust: stable 14 | - rust: beta 15 | 16 | install: 17 | - brew install sdl2 freetype 18 | 19 | os: 20 | - osx 21 | 22 | branches: 23 | only: 24 | - master 25 | - algo 26 | 27 | env: 28 | - ARCH=x86_64 29 | - ARCH=i686 30 | 31 | script: 32 | - cargo build 33 | - cargo test 34 | 35 | addons: 36 | apt: 37 | packages: 38 | - gcc-multilib 39 | -------------------------------------------------------------------------------- /src/ai/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate std; 2 | 3 | mod decision; 4 | mod heuristic; 5 | mod turn; 6 | #[cfg(test)] 7 | mod test_decision; 8 | #[cfg(test)] 9 | mod test_move_to_evaluate; 10 | mod move_to_evaluate; 11 | 12 | pub use self::decision::{Decision}; 13 | pub use self::heuristic::heuristic; 14 | 15 | pub const INFINITE: i32 = std::i32::MAX; 16 | pub const NINFINITE: i32 = std::i32::MIN; 17 | 18 | /// Return the opposite of the value. Special case for infinity. 19 | pub fn neg_infinite(value: i32) -> i32 { 20 | if value == INFINITE { 21 | NINFINITE 22 | } else if value == NINFINITE { 23 | INFINITE 24 | } else { 25 | -value 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ai/turn.rs: -------------------------------------------------------------------------------- 1 | pub type SortFn = fn( 2 | acc: ((usize, usize), i32), 3 | item: &((usize, usize), i32) 4 | ) -> ((usize, usize), i32); 5 | 6 | #[derive(Debug, PartialEq, Clone)] 7 | pub enum Turn { 8 | /// Player is looking to maximise the value of the heuristic 9 | Player, 10 | /// Adversary is looking to minimise the value of the heuristic 11 | Adversary 12 | } 13 | 14 | impl Turn { 15 | pub fn other(&self) -> Turn { 16 | match *self { 17 | Turn::Player => Turn::Adversary, 18 | Turn::Adversary => Turn::Player, 19 | } 20 | } 21 | 22 | pub fn sign_alternation(&self) -> i32 { 23 | match *self { 24 | Turn::Player => -1, 25 | Turn::Adversary => 1, 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /input: -------------------------------------------------------------------------------- 1 | 19 2 | . . . . . . . . . . . . . . . . . . . 3 | . . . . . . . . . . . . . . . . . . . 4 | . . . . . . . . . . . . . . . . . . . 5 | . . . . . . . . . . . . . . . . . . . 6 | . . . . . . . . B . B . . . . . . . . 7 | . . . . . . . W B W . . . B . . . . . 8 | . . . . . . W . W . B . W B . . . . . 9 | . . . . . . . W W W W B . . . . . . . 10 | . . . . . . B . W . B . B . . . . . . 11 | . . . . . . . W B B . W . W . . . . . 12 | . . . . . . B . W . . . . . . . . . . 13 | . . . . . . . . . W . . . . . . . . . 14 | . . . . . . . . . . . . . . . . . . . 15 | . . . . . . . . . . . . . . . . . . . 16 | . . . . . . . . . . . . . . . . . . . 17 | . . . . . . . . . . . . . . . . . . . 18 | . . . . . . . . . . . . . . . . . . . 19 | . . . . . . . . . . . . . . . . . . . 20 | . . . . . . . . . . . . . . . . . . . 21 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_features)] 2 | #![feature(test)] 3 | 4 | #[macro_use] 5 | extern crate clap; 6 | extern crate chrono; 7 | 8 | mod board; 9 | mod display; 10 | mod ai; 11 | mod cmd_option; 12 | #[cfg(test)] 13 | mod one_test; 14 | #[cfg(test)] 15 | mod bench; 16 | 17 | use clap::App; 18 | use display::{Console}; 19 | use cmd_option::CmdOption; 20 | 21 | fn main() { 22 | // options 23 | let yml = load_yaml!("cli.yml"); 24 | let m = App::from_yaml(yml).get_matches(); 25 | let opts = CmdOption::parse(&m); 26 | 27 | let mut game: Console = Console::new( 28 | opts.init_map, 29 | opts.layers, 30 | (opts.player, opts.friend), 31 | opts.info, 32 | opts.debug_map, 33 | opts.human_help, 34 | ); 35 | 36 | game.start(); 37 | } 38 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gomoku" 3 | version = "0.1.0" 4 | authors = [ 5 | "Guillaume Bersac ", 6 | "adjivas " 7 | ] 8 | description = "Game of Gomoku from 42 school" 9 | repository = "https://github.com/gbersac/gomoku_42.git" 10 | build = "build.rs" 11 | 12 | [dependencies.piston] 13 | version = "0.13.0" 14 | 15 | [dependencies.piston2d-opengl_graphics] 16 | version = "0.14.0" 17 | 18 | [dependencies.piston2d-graphics] 19 | version = "0.10.0" 20 | 21 | [dependencies.pistoncore-sdl2_window] 22 | version = "0.16.0" 23 | optional = true 24 | 25 | [dependencies.pistoncore-glfw_window] 26 | version = "0.12.0" 27 | optional = true 28 | 29 | [dependencies.pistoncore-glutin_window] 30 | version = "0.15.0" 31 | optional = true 32 | 33 | [features] 34 | default = ["include_sdl2"] 35 | include_sdl2 = ["pistoncore-sdl2_window"] 36 | include_glfw = ["pistoncore-glfw_window"] 37 | include_glutin = ["pistoncore-glutin_window"] 38 | 39 | [dependencies.clap] 40 | version = "2" 41 | features = ["yaml"] 42 | 43 | [dependencies] 44 | chrono = "0.2.16" 45 | -------------------------------------------------------------------------------- /src/cli.yml: -------------------------------------------------------------------------------- 1 | # This file describe the command line interface options 2 | name: Gomoku 3 | about: A Gomoku game implementation with awesome ai ! 4 | author: gbersac adjivas 5 | 6 | args: 7 | - player: 8 | help: Define wether teams are played by ai or human. 9 | short: p 10 | long: player 11 | takes_value: true 12 | possible_values: [ ai, human ] 13 | - friend: 14 | help: Define wether teams are friend by ai or human. 15 | short: f 16 | long: friend 17 | takes_value: true 18 | possible_values: [ ai, human ] 19 | - layers: 20 | help: The number of layers in the ai tree. 21 | long: layers 22 | takes_value: true 23 | short: l 24 | - nohelper: 25 | help: No helper for human players. 26 | long: nohelper 27 | short: n 28 | - debug: 29 | help: print information about map. 30 | long: debug 31 | short: d 32 | - init_map: 33 | help: init the map with the board described in the file which is the argument. 34 | long: init-map 35 | short: i 36 | takes_value: true 37 | -------------------------------------------------------------------------------- /src/ai/move_to_evaluate.rs: -------------------------------------------------------------------------------- 1 | use board::{GoBoard, Tile}; 2 | 3 | fn test_pawn( 4 | board: &GoBoard, 5 | (x, y): (usize, usize), 6 | vec_coord: &Vec<(usize, usize)> 7 | ) -> bool { 8 | board.check_index((x, y)) && 9 | board.get((x, y)) == Tile::FREE && 10 | vec_coord.iter().find(|&r| *r == (x, y)).is_none() 11 | } 12 | 13 | fn get_neighbors(x: usize, y: usize) -> Vec<(usize, usize)> { 14 | if x > 0 && y > 0 { 15 | return vec!((x - 1, y), (x, y - 1), (x - 1, y - 1), (x + 1, y - 1), 16 | (x + 1, y), (x, y + 1), (x + 1, y + 1), (x - 1, y + 1)); 17 | } 18 | let mut to_return = vec!((x + 1, y), (x, y + 1), (x + 1, y + 1)); 19 | if x > 0 { 20 | to_return.extend(vec!((x - 1, y), (x - 1, y + 1))); 21 | } 22 | if y > 0 { 23 | to_return.extend(vec!((x, y - 1), (x + 1, y - 1))); 24 | } 25 | to_return 26 | } 27 | 28 | pub fn move_to_evaluate(board: &GoBoard) -> Vec<(usize, usize)> { 29 | let mut to_return = Vec::new(); 30 | for (y, line) in board.tiles.iter().enumerate() { 31 | for (x, tile) in line.iter().enumerate() { 32 | if tile.is_pawn() { 33 | // I don't really understand why x and y need to be reversed. 34 | let neighbors = get_neighbors(y, x); 35 | for neighbor in neighbors { 36 | if !test_pawn(board, neighbor, &to_return) { 37 | continue ; 38 | } 39 | to_return.push(neighbor); 40 | } 41 | } 42 | } 43 | } 44 | to_return 45 | } 46 | -------------------------------------------------------------------------------- /src/one_test.rs: -------------------------------------------------------------------------------- 1 | use board::{GoBoard, Team}; 2 | use ai::{Decision, heuristic}; 3 | 4 | fn stupid_heuristic(board: &GoBoard, team: Team) -> i32 { 5 | 42 6 | } 7 | 8 | #[test] 9 | fn one_test_minmax() { 10 | let s = r#"19 11 | . . . . . . . . . . . . . . . . . . . 12 | . . . . . . . . . . . . . . . . . . . 13 | . . . . . . . . . . . . . . . . . . . 14 | . . . . . . . . . . . . . . . . . . . 15 | . . . . . . . . . . . . . . . . . . . 16 | . . . . . . . . . . . . . . . . . . . 17 | . . . . . . . . . . . . . . . . . . . 18 | . . . . . . . . W . . . . . . . . . . 19 | . . . . . . . . . . . . . . . . . . . 20 | . . . . . . . . . . . . . . . . . . . 21 | . . . . . . . . . . . . . . . . . . . 22 | . . . . . . . . . . . . . . . . . . . 23 | . . . . . . . . . . . . . . . . . . . 24 | . . . . . . . . . . . . . . . . . . . 25 | . . . . . . . . . . . . . . . . . . . 26 | . . . . . . . . . . . . . . . . . . . 27 | . . . . . . . . . . . . . . . . . . . 28 | . . . . . . . . . . . . . . . . . . . 29 | . . . . . . . . . . . . . . . . . . . 30 | "#; 31 | let mut board = GoBoard::parse_with_size(&s.to_string()); 32 | let (team_b, team_w) = Team::new_teams(); 33 | let result = Decision::get_optimal_move(&mut board, 34 | &(team_b, team_w.clone()), 35 | team_w, 36 | 3, 37 | stupid_heuristic); 38 | } 39 | -------------------------------------------------------------------------------- /src/board/tile.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Formatter, Display, Error}; 2 | 3 | #[derive(Debug, PartialEq, Clone)] 4 | pub enum Tile { 5 | BLACK, 6 | WHITE, 7 | FREE, 8 | } 9 | 10 | impl Default for Tile { 11 | fn default() -> Self { 12 | Tile::FREE 13 | } 14 | } 15 | 16 | impl Tile { 17 | #[allow(dead_code)] 18 | pub fn from_str(s: &str) -> Tile { 19 | match s { 20 | "B" | "b" => Tile::BLACK, 21 | "W" | "w" => Tile::WHITE, 22 | _ => Tile::FREE, 23 | } 24 | } 25 | 26 | pub fn is_pawn(&self) -> bool { 27 | *self == Tile::BLACK || *self == Tile::WHITE 28 | } 29 | 30 | /// Return the tile of the ennemy if there is one, return the tile itself 31 | /// otherwise. 32 | 33 | pub fn ennemy(&self) -> Tile { 34 | match *self { 35 | Tile::BLACK => Tile::WHITE, 36 | Tile::WHITE => Tile::BLACK, 37 | _ => self.clone(), 38 | } 39 | } 40 | } 41 | 42 | impl Display for Tile { 43 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 44 | match self { 45 | &Tile::BLACK => { 46 | let _ = write!(f, "B"); 47 | } 48 | &Tile::WHITE => { 49 | let _ = write!(f, "W"); 50 | } 51 | &Tile::FREE => { 52 | let _ = write!(f, "."); 53 | } 54 | }; 55 | Ok(()) 56 | } 57 | } 58 | 59 | impl Copy for Tile {} 60 | -------------------------------------------------------------------------------- /src/board/team.rs: -------------------------------------------------------------------------------- 1 | use board::Tile; 2 | 3 | #[derive(Debug, PartialEq, Clone, Copy)] 4 | pub struct Team { 5 | color: Tile, 6 | captured: u32, 7 | } 8 | 9 | impl Team { 10 | 11 | /// The `new` constructor function returns the Team. 12 | 13 | pub fn new(color: Tile) -> Team { 14 | Team { 15 | color: color, 16 | captured: 0, 17 | } 18 | } 19 | 20 | /// Create all the teams of the game. 21 | 22 | pub fn new_teams() -> (Team, Team) { 23 | (Team::new(Tile::BLACK), Team::new(Tile::WHITE)) 24 | } 25 | 26 | pub fn captured(&self) -> u32 { 27 | self.captured 28 | } 29 | 30 | pub fn get_tile(&self) -> Tile { 31 | self.color 32 | } 33 | 34 | pub fn get_ennemy_tile(&self) -> Tile { 35 | self.color.ennemy() 36 | } 37 | 38 | pub fn add_captured(&mut self, nb_captured: u32) { 39 | self.captured += nb_captured; 40 | } 41 | } 42 | 43 | use std::fmt::{Formatter, Display, Error}; 44 | 45 | impl Display for Team 46 | { 47 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> 48 | { 49 | let _ = match self.get_tile() { 50 | Tile::BLACK => write!(f, "black"), 51 | Tile::WHITE => write!(f, "white"), 52 | _ => panic!("forbiden team tile"), 53 | }; 54 | Ok(()) 55 | } 56 | } 57 | 58 | impl Default for Team { 59 | 60 | /// The `new` constructor function returns the interface team. 61 | 62 | fn default () -> Self { 63 | Team { 64 | color: Tile::default(), 65 | captured: 0, 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/board/fn_str.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | /// Like the c function atoi, trim space and tabs at the beginning of the string 4 | /// and then parse to the T integer type the following number chars. 5 | /// 6 | /// Return an error the first non space char is different from a number. 7 | pub fn atoi(s: &str) -> Result 8 | where T: FromStr 9 | { 10 | let mut number_has_began = false; 11 | let mut nbr_str = String::new(); 12 | 13 | for c in s.chars() { 14 | if !number_has_began && c.is_numeric() { 15 | number_has_began = true; 16 | } 17 | if number_has_began { 18 | if c.is_numeric() { 19 | nbr_str.push(c); 20 | } else { 21 | break; 22 | } 23 | } 24 | } 25 | 26 | if nbr_str.len() > 0 { 27 | Ok(nbr_str.parse::().ok().unwrap()) 28 | } else { 29 | Err("Cannot parse the string to integer") 30 | } 31 | } 32 | 33 | 34 | 35 | #[cfg(test)] 36 | mod test { 37 | use super::*; 38 | 39 | fn one_atoi_test(s: &str, expected: i32) { 40 | let nbr: i32 = atoi(s).unwrap(); 41 | assert!(nbr == expected); 42 | } 43 | 44 | #[test] 45 | fn test_atoi() { 46 | one_atoi_test("5", 5); 47 | one_atoi_test("20", 20); 48 | one_atoi_test("555", 555); 49 | one_atoi_test(" 555", 555); 50 | one_atoi_test(" 555pppp", 555); 51 | } 52 | 53 | #[test] 54 | #[should_panic] 55 | fn test_atoi2() { 56 | one_atoi_test("", 5); 57 | } 58 | 59 | #[test] 60 | #[should_panic] 61 | fn test_atoi3() { 62 | one_atoi_test("aaa", 5); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/cmd_option.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::Read; 3 | use display::console::Player; 4 | use clap; 5 | use board::{GoBoard}; 6 | 7 | const DEFAULT_PLAYER: &'static str = "ai"; 8 | const DEFAULT_FRIEND: &'static str = "ai"; 9 | const DEFAULT_LAYERS: &'static str = "3"; 10 | const DEFAULT_INFO: &'static str = "true"; 11 | 12 | pub fn file_as_string(file_name: &str) -> String { 13 | let mut f = File::open(file_name).unwrap(); 14 | let mut s = String::new(); 15 | let _ = f.read_to_string(&mut s); 16 | s 17 | } 18 | 19 | #[derive(Debug)] 20 | pub struct CmdOption { 21 | pub player: Player, 22 | pub friend: Player, 23 | pub layers: u32, 24 | pub human_help: bool, 25 | pub info: bool, 26 | pub debug_map: bool, 27 | pub init_map: GoBoard 28 | } 29 | 30 | impl CmdOption { 31 | pub fn parse(m: &clap::ArgMatches) -> CmdOption { 32 | let playero = m.value_of("player").unwrap_or(DEFAULT_PLAYER); 33 | let friendo = m.value_of("friend").unwrap_or(DEFAULT_FRIEND); 34 | let layerso = m.value_of("layers").unwrap_or(DEFAULT_LAYERS); 35 | let no_helpo = !m.is_present("nohelper"); 36 | let infoo = m.value_of("info").unwrap_or(DEFAULT_INFO); 37 | let debug_mapo = m.is_present("debug_map"); 38 | let init_map = if m.is_present("init_map") { 39 | let file_name = m.value_of("init_map").unwrap(); 40 | let file_str = file_as_string(file_name); 41 | GoBoard::parse_with_size(&file_str.to_string()) 42 | } else { 43 | GoBoard::default() 44 | }; 45 | if layerso.parse::().is_err() { 46 | panic!("Layers cli option must be an unsigned interger."); 47 | } 48 | CmdOption { 49 | player: Player::from_str(playero), 50 | friend: Player::from_str(friendo), 51 | layers: layerso.parse::().unwrap(), 52 | human_help: no_helpo, 53 | info: infoo.parse::().unwrap(), 54 | debug_map: debug_mapo, 55 | init_map: init_map 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoGame42 2 | 3 | [![travis-badge][]][travis] [![docs-badge][]][docs] 4 | 5 | [travis-badge]: https://travis-ci.org/gbersac/gomoku_42.svg?style=flat-square 6 | [travis]: https://travis-ci.org/gbersac/gomoku_42 7 | [docs-badge]: https://img.shields.io/badge/API-docs-blue.svg?style=flat-square 8 | [docs]: https://gbersac.github.io/gomoku_42/gomoku 9 | 10 | A gomoku game with an **artificial intelligence**. The goban has a width of 19 tiles. Developped using the [rust programming language](https://www.rust-lang.org/). 11 | 12 | #### ScreenShot: 13 | ![Screen Shot][display-screenshot] 14 | 15 | [display-screenshot]: https://raw.githubusercontent.com/gbersac/gomoku_42/gh-pages/screenshot.apng 16 | 17 | #### How to play: 18 | ```shell 19 | # Between human: 20 | cargo run --release -- --layers 4 --friend human --player human 21 | # Human with ai: 22 | cargo run --release -- --layers 4 --friend human --player ai 23 | ``` 24 | 25 | #### Cargo'git-Dependencies: 26 | ```shell 27 | sdl2 glfw glutin 28 | \ | / 29 | opengl_graphics graphics (window) 30 | \ / | 31 | (2d) (core) 32 | \ / 33 | clap chrono piston 34 | \ | / 35 | gomoku 36 | ``` 37 | 38 | #### Directory-Tree: 39 | ```shell 40 | . 41 | |__ Cargo.lock 42 | |__ Cargo.toml 43 | |__ README.md 44 | |__ main.rs 45 | |__ one_test.rs 46 | \__ src 47 | |__ cli.yml 48 | |__ cmd_option.rs 49 | |__ one_test.rs 50 | |__ bench.rs 51 | |__ display 52 | │ |__ console.rs 53 | │ |__ draw.rs 54 | │ |__ mod.rs 55 | │ \__ mouse.rs 56 | |__ ai 57 | | |__ decision.rs 58 | | |__ heuristic.rs 59 | | |__ mod.rs 60 | | |__ move_to_evaluate.rs 61 | | |__ test_decision.rs 62 | | |__ test_move_to_evaluate.rs 63 | | \__ turn.rs 64 | \__ board 65 | |__ fn_str.rs 66 | |__ go_board.rs 67 | |__ mod.rs 68 | |__ parse.rs 69 | |__ team.rs 70 | |__ test_capture.rs 71 | |__ test_free_threes.rs 72 | |__ test_win.rs 73 | \__ tile.rs 74 | ``` 75 | -------------------------------------------------------------------------------- /src/board/parse.rs: -------------------------------------------------------------------------------- 1 | use board::{Tile, GoBoard}; 2 | use board::fn_str; 3 | 4 | impl GoBoard { 5 | fn split_one_line(line: &str) -> Vec { 6 | line.split(' ') 7 | .map(|x| x.trim()) 8 | .filter(|x| x.len() > 0) 9 | .map(|x| {Tile::from_str(x)}) 10 | .collect() 11 | } 12 | 13 | fn split_into_lines(to_parse: &String) -> Vec<&str> { 14 | to_parse.split('\n') 15 | .map(|x| x.trim()) 16 | .filter(|x| x.len() > 0) 17 | .filter(|x| x.chars().next() != "#".chars().next()) 18 | .collect::>() 19 | } 20 | 21 | /// Parse a string which describe the inital state of the npuzzle board. 22 | fn execute_parse(size: usize, lines: &Vec<&str>) 23 | -> GoBoard { 24 | 25 | // get all the tiles from the lines 26 | let mut tiles = Vec::with_capacity(size * size); 27 | for line in lines { 28 | let ntiles : Vec = GoBoard::split_one_line(line); 29 | tiles.extend(ntiles); 30 | } 31 | 32 | //create board, no check 33 | let mut board : GoBoard = Default::default(); 34 | let size = board.get_size(); 35 | for x in 0..size { 36 | for y in 0..size { 37 | board.set_raw((x, y), tiles[y * size + x].clone()); 38 | } 39 | } 40 | board 41 | } 42 | 43 | /// This function also parse the size of the Board. 44 | /// This function should only be used in test because there is no test. 45 | pub fn parse_with_size(to_parse: &String) -> GoBoard { 46 | let lines = GoBoard::split_into_lines(to_parse); 47 | let size = fn_str::atoi::(lines[0]).unwrap(); 48 | let lines_reduce = (&lines[1..]).to_vec(); 49 | GoBoard::execute_parse(size, &lines_reduce) 50 | } 51 | } 52 | 53 | #[cfg(test)] 54 | mod test { 55 | use super::*; 56 | use board::{GoBoard, Tile}; 57 | 58 | #[test] 59 | fn test_parse() { 60 | let mut str1 = r#"5 61 | . . . . . . . . . . . . . . . . . . . 62 | . . . . . . . . . . . . . . . . . . . 63 | W . . . . . . . . . . . . . . . . . . 64 | . . . . . . . . . . . . . . . . . . . 65 | . . . . . . . . . . . . . . . . . . . 66 | . . . . . . . . . . . . . . . . . . . 67 | . . . . . . . . . . . . . . . . . . . 68 | . . . W . . . . . . . . . . . . . . . 69 | . . . . W . . . . . . . . . . . . . . 70 | . . . . . W . . . . . . . . . . . . . 71 | . . . . . . W . . . . . . . . . . . . 72 | . . . . . . . W . . . . . . . . . . . 73 | . . . . . . . . . . . . . . . . . . . 74 | . . . . . . . . . . . . . . . . . . . 75 | . . . . . . . . . . . . . . . . . . . 76 | . . . . . . . . . . . . . . . . . . . 77 | . . . . . . . . . . . . . . . . . . . 78 | . . . . . . . . . . . . . . . . . . . 79 | . . . . . . . . . . . . . . . . . . . 80 | "#; 81 | let board = GoBoard::parse_with_size(&str1.to_string()); 82 | assert!(board.get((0, 2)) == Tile::WHITE); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/display/mouse.rs: -------------------------------------------------------------------------------- 1 | extern crate piston; 2 | 3 | use std::fmt::{Formatter, Display, Error}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Mouse { 7 | overed: bool, 8 | coordinate_cell: (u32, u32), 9 | dimension: (u32, u32), 10 | } 11 | 12 | impl Mouse { 13 | pub fn new ( 14 | sizes: (u32, u32), 15 | ) -> Self { 16 | let mut event: Self = Default::default(); 17 | 18 | event.set_dimension(sizes); 19 | event 20 | } 21 | 22 | pub fn set_dimension ( 23 | &mut self, 24 | dimension: (u32, u32), 25 | ) { 26 | self.dimension = dimension; 27 | } 28 | 29 | pub fn get_dimension ( 30 | &self, 31 | ) -> (u32, u32) { 32 | self.dimension 33 | } 34 | 35 | pub fn set_coordinate ( 36 | &mut self, 37 | coordinate: (u32, u32), 38 | ) { 39 | self.coordinate_cell = coordinate; 40 | } 41 | 42 | pub fn get_coordinate ( 43 | &self, 44 | ) -> (u32, u32) { 45 | self.coordinate_cell 46 | } 47 | 48 | fn set_over ( 49 | &mut self, 50 | mouse: bool, 51 | ) { 52 | self.overed = mouse; 53 | } 54 | 55 | pub fn get_over ( 56 | &mut self, 57 | ) -> bool { 58 | self.overed 59 | } 60 | 61 | pub fn check_inside_window ( 62 | &mut self, 63 | coordinate: (u32, u32), 64 | length: u32, 65 | ) -> Option<(u32, u32)> { 66 | let mouse:bool = 0u32 < coordinate.0 67 | && coordinate.0 < self.dimension.0 68 | && 0u32 < coordinate.1 69 | && coordinate.1 < self.dimension.1; 70 | 71 | if mouse { 72 | self.set_over(true); 73 | let coordinate_cell_new = ( 74 | coordinate.0 / {self.dimension.0 / length}, 75 | coordinate.1 / {self.dimension.1 / length} 76 | ); 77 | if coordinate_cell_new.0 != self.coordinate_cell.0 78 | || coordinate_cell_new.1 != self.coordinate_cell.1 { 79 | return Some(coordinate_cell_new); 80 | } 81 | } 82 | else { 83 | self.set_over(false); 84 | } 85 | None 86 | } 87 | } 88 | 89 | impl Default for Mouse { 90 | fn default() -> Self { 91 | Mouse { 92 | overed: false, 93 | coordinate_cell: (0u32, 0u32), 94 | dimension: (0u32, 0u32), 95 | } 96 | } 97 | } 98 | 99 | impl Display for Mouse { 100 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 101 | let _ = write!(f, stringify!(self.coordinate_cell.width)); 102 | let _ = write!(f, stringify!(self.coordinate_cell.height)); 103 | Ok(()) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /bench_result: -------------------------------------------------------------------------------- 1 | # commit 2ba335a8075c52632a4927342d83781a4a9ada8f save help_coordinate 2 | test bench::heuristic_bench ... bench: 11,342 ns/iter (+/- 661) 3 | test bench::heuristic_bench_easy ... bench: 11,598 ns/iter (+/- 706) 4 | test bench::minmax_3_layer_bench ... bench: 11,366 ns/iter (+/- 599) 5 | test bench::minmax_3_layers_easy ... bench: 197,907 ns/iter (+/- 10,536) 6 | 7 | # with multi threading 8 | test bench::heuristic_bench ... bench: 10,844 ns/iter (+/- 1,030) 9 | test bench::heuristic_bench_easy ... bench: 11,067 ns/iter (+/- 255) 10 | test bench::minmax_3_layer_bench ... bench: 10,022 ns/iter (+/- 1,024) 11 | test bench::minmax_3_layers_easy ... bench: 468,164 ns/iter (+/- 19,368) 12 | 13 | # 06d6c296b2ae25a25e8c6b6a91b1a4c6bc3521dd optimize moves_to_evaluate 14 | test bench::heuristic_bench ... bench: 11,649 ns/iter (+/- 780) 15 | test bench::heuristic_bench_easy ... bench: 11,542 ns/iter (+/- 460) 16 | test bench::minmax_3_layer_bench ... bench: 11,540 ns/iter (+/- 590) 17 | test bench::minmax_3_layers_easy ... bench: 94,748 ns/iter (+/- 4,149) 18 | 19 | # 67b62e3fa31b235017edc29574268b9528deb392 change heuristic 20 | test bench::heuristic_bench ... bench: 1,342 ns/iter (+/- 170) 21 | test bench::heuristic_bench_easy ... bench: 491 ns/iter (+/- 36) 22 | test bench::minmax_3_layer_bench ... bench: 6,374,271 ns/iter (+/- 297,251) 23 | test bench::minmax_3_layers_easy ... bench: 87,516 ns/iter (+/- 8,355) 24 | with real 3 layers : 25 | test bench::minmax_3_layer_bench ... bench: 90,790,476 ns/iter (+/- 11,079,548) 26 | test bench::minmax_3_layers_easy ... bench: 331,040 ns/iter (+/- 29,996) 27 | 28 | # 403908a327c356fa6356ae35ffcfb6d4fee11bfb decision: replace set by set_raw 29 | test bench::heuristic_bench ... bench: 1,356 ns/iter (+/- 183) 30 | test bench::heuristic_bench_easy ... bench: 490 ns/iter (+/- 88) 31 | test bench::minmax_3_layer_bench ... bench: 3,777,035 ns/iter (+/- 308,674) 32 | test bench::minmax_3_layers_easy ... bench: 42,561 ns/iter (+/- 3,785) 33 | 34 | # 28b4cea2ca895ce5c21f2f5097993c81b3f96d47 search on one more layer 35 | test bench::heuristic_bench ... bench: 1,341 ns/iter (+/- 55) 36 | test bench::heuristic_bench_easy ... bench: 488 ns/iter (+/- 45) 37 | test bench::minmax_3_layer_bench ... bench: 83,343,485 ns/iter (+/- 5,979,305) 38 | test bench::minmax_3_layers_easy ... bench: 198,344 ns/iter (+/- 19,146) 39 | 40 | # be4828c6fb21cc8e431af49a04913b1ca99f0176 test for win at every layer 41 | test bench::heuristic_bench ... bench: 1,460 ns/iter (+/- 64) 42 | test bench::heuristic_bench_easy ... bench: 534 ns/iter (+/- 23) 43 | test bench::minmax_3_layer_bench ... bench: 91,982,437 ns/iter (+/- 760,047) 44 | test bench::minmax_3_layers_easy ... bench: 352,250 ns/iter (+/- 31,564) 45 | 46 | # 98a98be9c91313ef4c14ab5ba221c2668e38f389 with threads (-1 layer) 47 | test bench::heuristic_bench ... bench: 1,471 ns/iter (+/- 58) 48 | test bench::heuristic_bench_easy ... bench: 556 ns/iter (+/- 92) 49 | test bench::minmax_3_layer_bench ... bench: 3,125,631 ns/iter (+/- 403,182) 50 | test bench::minmax_3_layers_easy ... bench: 206,309 ns/iter (+/- 9,133) 51 | 52 | # with end heuristic 53 | test bench::heuristic_bench ... bench: 3,996 ns/iter (+/- 269) 54 | test bench::heuristic_bench_easy ... bench: 579 ns/iter (+/- 29) 55 | test bench::minmax_3_layer_bench ... bench: 2,930,355 ns/iter (+/- 425,474) 56 | test bench::minmax_3_layers_easy ... bench: 207,115 ns/iter (+/- 10,582) 57 | -------------------------------------------------------------------------------- /src/bench.rs: -------------------------------------------------------------------------------- 1 | extern crate test; 2 | 3 | use board::{GoBoard, Team}; 4 | use ai::{Decision, heuristic}; 5 | use ai; 6 | 7 | fn stupid_heuristic(board: &GoBoard, team: Team) -> i32 { 8 | 42 9 | } 10 | 11 | #[bench] 12 | fn minmax_3_layers_easy(b: &mut test::Bencher) { 13 | let s = r#"19 14 | . . . . . . . . . . . . . . . . . . . 15 | . . . . . . . . . . . . . . . . . . . 16 | . . . . . . . . . . . . . . . . . . . 17 | . . . . . . . . . . . . . . . . . . . 18 | . . . . . . . . . . . . . . . . . . . 19 | . . . . . . . . . . . . . . . . . . . 20 | . . . . . . . . . . . . . . . . . . . 21 | . . . . . . . . W . . . . . . . . . . 22 | . . . . . . . . . . . . . . . . . . . 23 | . . . . . . . . . . . . . . . . . . . 24 | . . . . . . . . . . . . . . . . . . . 25 | . . . . . . . . . . . . . . . . . . . 26 | . . . . . . . . . . . . . . . . . . . 27 | . . . . . . . . . . . . . . . . . . . 28 | . . . . . . . . . . . . . . . . . . . 29 | . . . . . . . . . . . . . . . . . . . 30 | . . . . . . . . . . . . . . . . . . . 31 | . . . . . . . . . . . . . . . . . . . 32 | . . . . . . . . . . . . . . . . . . . 33 | "#; 34 | let mut board = GoBoard::parse_with_size(&s.to_string()); 35 | let (team_b, team_w) = Team::new_teams(); 36 | b.iter(|| { 37 | Decision::get_optimal_move(&mut board, 38 | &(team_b, team_w.clone()), 39 | team_w, 40 | 3, 41 | stupid_heuristic); 42 | }) 43 | } 44 | 45 | #[bench] 46 | fn minmax_3_layer_bench(b: &mut test::Bencher) { 47 | let s = r#"19 48 | . . . . . . . . . . . . . . . . . . . 49 | . B . . . . . . . . . . . . . . . . . 50 | . . W . B . . . W . . . . . . . . . . 51 | . . . W . . . . . . . . . . . . . . . 52 | . . . . W . . . . . . . . . . . . . . 53 | . . . . . B . . . . . . . . . . . . . 54 | . . . . . . . . . . . . . . . . . . . 55 | . . . . . W B W B W B W W . . . . . . 56 | . . . . . . . . . W . . . . . . . . . 57 | . . . . . . . . . . W . . . . . . . . 58 | . . . . . . . . . B . B . . . . . . . 59 | . . . . . . . . B . . . B . . . . . . 60 | . . . . . . . B . . . . . W . . . . . 61 | . . . . . . W . . . . . . . W . . . . 62 | . . . . . . . . . . . . . . . . . . . 63 | . . . . . . . . . . . . . . . . . . . 64 | . . . . . . . . . . . . . . . . . . . 65 | . . . . . . . . . . . . . . . . . . . 66 | . . . . . . . . . . . . . . . . . . . 67 | "#; 68 | let mut board = GoBoard::parse_with_size(&s.to_string()); 69 | let (team_b, team_w) = Team::new_teams(); 70 | b.iter(|| { 71 | Decision::get_optimal_move(&mut board, 72 | &(team_b, team_w.clone()), 73 | team_w, 74 | 3, 75 | stupid_heuristic); 76 | }) 77 | } 78 | 79 | #[bench] 80 | fn heuristic_bench_easy(b: &mut test::Bencher) { 81 | let s = r#"19 82 | . . . . . . . . . . . . . . . . . . . 83 | . . . . . . . . . . . . . . . . . . . 84 | . . . . . . . . . . . . . . . . . . . 85 | . . . . . . . . . . . . . . . . . . . 86 | . . . . . . . . . . . . . . . . . . . 87 | . . . . . . . . . . . . . . . . . . . 88 | . . . . . . . . . . . . . . . . . . . 89 | . . . . . . . . W . . . . . . . . . . 90 | . . . . . . . . . . . . . . . . . . . 91 | . . . . . . . . . . . . . . . . . . . 92 | . . . . . . . . . . . . . . . . . . . 93 | . . . . . . . . . . . . . . . . . . . 94 | . . . . . . . . . . . . . . . . . . . 95 | . . . . . . . . . . . . . . . . . . . 96 | . . . . . . . . . . . . . . . . . . . 97 | . . . . . . . . . . . . . . . . . . . 98 | . . . . . . . . . . . . . . . . . . . 99 | . . . . . . . . . . . . . . . . . . . 100 | . . . . . . . . . . . . . . . . . . . 101 | "#; 102 | let mut board = GoBoard::parse_with_size(&s.to_string()); 103 | let (_, team_w) = Team::new_teams(); 104 | b.iter(|| { 105 | heuristic(&mut board, team_w.clone()); 106 | }) 107 | } 108 | 109 | #[bench] 110 | fn heuristic_bench(b: &mut test::Bencher) { 111 | let s = r#"19 112 | . . . . . . . . . . . . . . . . . . . 113 | . B . . . . . . . . . . . . . . . . . 114 | . . W . B . . . W . . . . . . . . . . 115 | . . . W . . . . . . . . . . . . . . . 116 | . . . . W . . . . . . . . . . . . . . 117 | . . . . . B . . . . . . . . . . . . . 118 | . . . . . . . . . . . . . . . . . . . 119 | . . . . . W B W B W B W W . . . . . . 120 | . . . . . . . . . W . . . . . . . . . 121 | . . . . . . . . . . W . . . . . . . . 122 | . . . . . . . . . B . B . . . . . . . 123 | . . . . . . . . B . . . B . . . . . . 124 | . . . . . . . B . . . . . W . . . . . 125 | . . . . . . W . . . . . . . W . . . . 126 | . . . . . . . . . . . . . . . . . . . 127 | . . . . . . . . . . . . . . . . . . . 128 | . . . . . . . . . . . . . . . . . . . 129 | . . . . . . . . . . . . . . . . . . . 130 | . . . . . . . . . . . . . . . . . . . 131 | "#; 132 | let mut board = GoBoard::parse_with_size(&s.to_string()); 133 | let (_, team_w) = Team::new_teams(); 134 | b.iter(|| { 135 | heuristic(&mut board, team_w.clone()); 136 | }) 137 | } 138 | -------------------------------------------------------------------------------- /src/display/draw.rs: -------------------------------------------------------------------------------- 1 | extern crate std; 2 | extern crate piston; 3 | extern crate graphics; 4 | #[cfg(feature = "include_sdl2")] 5 | extern crate sdl2_window; 6 | #[cfg(feature = "include_glfw")] 7 | extern crate glfw_window; 8 | #[cfg(feature = "include_glutin")] 9 | extern crate glutin_window; 10 | 11 | use self::graphics::Context; 12 | use self::graphics::Graphics; 13 | use self::graphics::types::Color; 14 | use self::graphics::draw_state::DrawState; 15 | use self::piston::window::Size; 16 | 17 | use board::Tile; 18 | use board::GoBoard; 19 | 20 | const BORDER_SIZE : f64 = 0.5f64; 21 | const ORANGE: graphics::types::Color = [0.97647065f32, 0.9450981f32, 0.854902f32, 1f32]; 22 | const BLACK: graphics::types::Color = [0f32, 0f32, 0f32, 1f32]; 23 | const WHITE: graphics::types::Color = [1f32, 1f32, 1f32, 1f32]; 24 | const OVER: graphics::types::Color = [1f32, 1f32, 1f32, 0.7f32]; 25 | 26 | /// The `draw_tile_color` function draws a tile according to a coordinate, 27 | /// dimension and color. 28 | 29 | fn draw_tile_color ( 30 | color: Color, 31 | dimension: Size, 32 | coordinate: [u32; 2], 33 | (context, g): (&Context, &mut G), 34 | ) where G: Graphics { 35 | let _coordinate: Size = Size::from(coordinate); 36 | 37 | graphics::ellipse ( 38 | color, 39 | graphics::ellipse::circle ( 40 | {dimension.width * _coordinate.width + dimension.width / 2u32} as f64, 41 | {dimension.height * _coordinate.height + dimension.height / 2u32} as f64, 42 | {std::cmp::min(dimension.width, dimension.height) / 3u32} as f64, 43 | ), 44 | context.transform, 45 | g 46 | ); 47 | } 48 | 49 | /// The `draw_border_color` function removes the border grid. 50 | 51 | fn draw_border_color ( 52 | color: Color, 53 | dimension: Size, 54 | max: u32, 55 | (context, g): (&Context, &mut G), 56 | ) where G: Graphics { 57 | let rect_border = graphics::Rectangle::new_border(color, { 58 | std::cmp::min(dimension.width, dimension.height) / 2u32 59 | } as f64 - BORDER_SIZE); 60 | 61 | rect_border.draw ( 62 | [ 63 | 0f64, 64 | 0f64, 65 | {dimension.width * max} as f64, 66 | {dimension.height * max} as f64, 67 | ], 68 | &context.draw_state, context.transform, g 69 | ); 70 | } 71 | 72 | 73 | /// The `draw_line_color` function draws a cell line. 74 | 75 | fn draw_line_color ( 76 | color: Color, 77 | dimension: Size, 78 | coordinate: [u32; 2], 79 | (context, g): (&Context, &mut G), 80 | ) where G: Graphics { 81 | let _coordinate: Size = Size::from(coordinate); 82 | let line_border = graphics::Line::new ( 83 | color, 84 | BORDER_SIZE, 85 | ); 86 | 87 | line_border.draw ( 88 | [ 89 | {dimension.width * _coordinate.width + dimension.width / 2u32} as f64, 90 | {dimension.height * _coordinate.height} as f64, 91 | {dimension.width * _coordinate.width + dimension.width / 2u32} as f64, 92 | {dimension.height * _coordinate.height + dimension.height} as f64, 93 | ], 94 | &DrawState::new(), 95 | context.transform, 96 | g 97 | ); 98 | line_border.draw ( 99 | [ 100 | {dimension.width * _coordinate.width} as f64, 101 | {dimension.height * _coordinate.height + dimension.height / 2u32} as f64, 102 | {dimension.width * _coordinate.width + dimension.width} as f64, 103 | {dimension.height * _coordinate.height + dimension.height / 2u32} as f64, 104 | ], 105 | &DrawState::new(), 106 | context.transform, 107 | g 108 | ); 109 | } 110 | 111 | /// The `draw_render` function draws the grid! 112 | 113 | pub fn draw_render ( 114 | board: &GoBoard, 115 | dimension: Size, 116 | limit: u32, 117 | (context, g): (&Context, &mut G), 118 | ) where G: Graphics { 119 | for x in 0..limit { 120 | for y in 0..limit { 121 | draw_line_color(BLACK, dimension, [x, y], (&context, g)); //5F5845 122 | } 123 | } 124 | draw_border_color(ORANGE, dimension, limit, (&context, g)); 125 | for x in 0..limit { 126 | for y in 0..limit { 127 | match board.get((x as usize, y as usize)) { 128 | Tile::WHITE => draw_tile_color(WHITE, dimension, [x, y], (&context, g)), 129 | Tile::BLACK => draw_tile_color(BLACK, dimension, [x, y], (&context, g)), 130 | _ => {}, 131 | } 132 | } 133 | } 134 | } 135 | 136 | /// The `draw_over` function draws the over tile. 137 | 138 | pub fn draw_over ( 139 | board: &GoBoard, 140 | dimension: Size, 141 | (x, y): (u32, u32), 142 | (context, g): (&Context, &mut G), 143 | ) where G: Graphics { 144 | match board.get((x as usize, y as usize)) { 145 | Tile::FREE => { 146 | draw_tile_color(OVER, dimension, [x, y], (&context, g)) 147 | }, 148 | _ => {}, 149 | } 150 | } 151 | 152 | /// The `draw_over` function draws the help tile. 153 | 154 | pub fn draw_help ( 155 | board: &GoBoard, 156 | dimension: Size, 157 | (x, y): (u32, u32), 158 | (context, g): (&Context, &mut G), 159 | ) where G: Graphics { 160 | match board.get((x as usize, y as usize)) { 161 | Tile::FREE => { 162 | draw_tile_color(OVER, dimension, [x, y], (&context, g)) 163 | }, 164 | _ => {}, 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/board/test_win.rs: -------------------------------------------------------------------------------- 1 | use board::{GoBoard, Tile}; 2 | 3 | fn test_win(s: &str, x: usize, y: usize, is_win: Option) { 4 | let board = GoBoard::parse_with_size(&s.to_string()); 5 | println!("Test\n{}", board); 6 | assert!(board.is_win(x, y) == is_win); 7 | } 8 | 9 | #[test] 10 | fn test_win_little() { 11 | let mut s = r#"5 12 | . . . . . . . . . . . . . . . . . . . 13 | . . . . . . . . . . . . . . . . . . . 14 | . . . . . . . . . . . . . . . . . . . 15 | . . . . . . . . . . . . . . . . . . . 16 | . . . . . . . . . . . . . . . . . . . 17 | . . . . . . . . . . . . . . . . . . . 18 | . . . . . . . . . . . . . . . . . . . 19 | . . . . . . . . . . . . . . . . . . . 20 | . . . . . . . . . . . . . . . . . . . 21 | . . . . . . . . . . . . . . . . . . . 22 | . . . . . . . . . . . . . . . . . . . 23 | . . . . . . . . . . . . . . . . . . . 24 | . . . . . . . . . . . . . . . . . . . 25 | . . . . . . . . . . . . . . . . . . . 26 | . . . . . . . . . . . . . . . . . . . 27 | . . . . . . . . . . . . . . . . . . . 28 | . . . . . . . . . . . . . . . . . . . 29 | . . . . . . . . . . . . . . . . . . . 30 | . . . . . . . . . . . . . . . . . . . 31 | "#; 32 | test_win(s, 1, 1, None); 33 | let mut s = r#"5 34 | . . . . . . . . . . . . . . . . . . . 35 | . . . . . . . . . . . . . . . . . . . 36 | W W W W W . . . . . . . . . . . . . . 37 | . . . . . . . . . . . . . . . . . . . 38 | . . . . . . . . . . . . . . . . . . . 39 | . . . . . . . . . . . . . . . . . . . 40 | . . . . . . . . . . . . . . . . . . . 41 | . . . . . . . . . . . . . . . . . . . 42 | . . . . . . . . . . . . . . . . . . . 43 | . . . . . . . . . . . . . . . . . . . 44 | . . . . . . . . . . . . . . . . . . . 45 | . . . . . . . . . . . . . . . . . . . 46 | . . . . . . . . . . . . . . . . . . . 47 | . . . . . . . . . . . . . . . . . . . 48 | . . . . . . . . . . . . . . . . . . . 49 | . . . . . . . . . . . . . . . . . . . 50 | . . . . . . . . . . . . . . . . . . . 51 | . . . . . . . . . . . . . . . . . . . 52 | . . . . . . . . . . . . . . . . . . . 53 | "#; 54 | test_win(s, 1, 1, None); 55 | test_win(s, 2, 2, Some(Tile::WHITE)); 56 | test_win(s, 4, 2, Some(Tile::WHITE)); 57 | let mut s = r#"5 58 | W . . . . . . . . . . . . . . . . . . 59 | . W . . . . . . . . . . . . . . . . . 60 | W W W . W . . . . . . . . . . . . . . 61 | . . . W . . . . . . . . . . . . . . . 62 | . . . . . . . . . . . . . . . . . . . 63 | . . . . . . . . . . . . . . . . . . . 64 | . . . . . . . . . . . . . . . . . . . 65 | . . . . . . . . . . . . . . . . . . . 66 | . . . . . . . . . . . . . . . . . . . 67 | . . . . . . . . . . . . . . . . . . . 68 | . . . . . . . . . . . . . . . . . . . 69 | . . . . . . . . . . . . . . . . . . . 70 | . . . . . . . . . . . . . . . . . . . 71 | . . . . . . . . . . . . . . . . . . . 72 | . . . . . . . . . . . . . . . . . . . 73 | . . . . . . . . . . . . . . . . . . . 74 | . . . . . . . . . . . . . . . . . . . 75 | . . . . . . . . . . . . . . . . . . . 76 | . . . . . . . . . . . . . . . . . . . 77 | "#; 78 | test_win(s, 2, 2, None); 79 | let mut s = r#"5 80 | W . . . . . . . . . . . . . . . . . . 81 | . W . . . . . . . . . . . . . . . . . 82 | W W W . W . . . . . . . . . . . . . . 83 | . . . W . . . . . . . . . . . . . . . 84 | . . . . W . . . . . . . . . . . . . . 85 | . . . . . . . . . . . . . . . . . . . 86 | . . . . . . . . . . . . . . . . . . . 87 | . . . . . . . . . . . . . . . . . . . 88 | . . . . . . . . . . . . . . . . . . . 89 | . . . . . . . . . . . . . . . . . . . 90 | . . . . . . . . . . . . . . . . . . . 91 | . . . . . . . . . . . . . . . . . . . 92 | . . . . . . . . . . . . . . . . . . . 93 | . . . . . . . . . . . . . . . . . . . 94 | . . . . . . . . . . . . . . . . . . . 95 | . . . . . . . . . . . . . . . . . . . 96 | . . . . . . . . . . . . . . . . . . . 97 | . . . . . . . . . . . . . . . . . . . 98 | . . . . . . . . . . . . . . . . . . . 99 | "#; 100 | test_win(s, 2, 2, Some(Tile::WHITE)); 101 | let mut s = r#"5 102 | W . . . W . . . . . . . . . . . . . . 103 | . W . W . . . . . . . . . . . . . . . 104 | W W W . W . . . . . . . . . . . . . . 105 | . W . . . . . . . . . . . . . . . . . 106 | W . . . W . . . . . . . . . . . . . . 107 | . . . . . . . . . . . . . . . . . . . 108 | . . . . . . . . . . . . . . . . . . . 109 | . . . . . . . . . . . . . . . . . . . 110 | . . . . . . . . . . . . . . . . . . . 111 | . . . . . . . . . . . . . . . . . . . 112 | . . . . . . . . . . . . . . . . . . . 113 | . . . . . . . . . . . . . . . . . . . 114 | . . . . . . . . . . . . . . . . . . . 115 | . . . . . . . . . . . . . . . . . . . 116 | . . . . . . . . . . . . . . . . . . . 117 | . . . . . . . . . . . . . . . . . . . 118 | . . . . . . . . . . . . . . . . . . . 119 | . . . . . . . . . . . . . . . . . . . 120 | . . . . . . . . . . . . . . . . . . . 121 | "#; 122 | test_win(s, 2, 2, Some(Tile::WHITE)); 123 | let mut s = r#"5 124 | W . W . . . . . . . . . . . . . . . . 125 | . W W W . . . . . . . . . . . . . . . 126 | W W W . W . . . . . . . . . . . . . . 127 | . . W . . . . . . . . . . . . . . . . 128 | W . W . W . . . . . . . . . . . . . . 129 | . . . . . . . . . . . . . . . . . . . 130 | . . . . . . . . . . . . . . . . . . . 131 | . . . . . . . . . . . . . . . . . . . 132 | . . . . . . . . . . . . . . . . . . . 133 | . . . . . . . . . . . . . . . . . . . 134 | . . . . . . . . . . . . . . . . . . . 135 | . . . . . . . . . . . . . . . . . . . 136 | . . . . . . . . . . . . . . . . . . . 137 | . . . . . . . . . . . . . . . . . . . 138 | . . . . . . . . . . . . . . . . . . . 139 | . . . . . . . . . . . . . . . . . . . 140 | . . . . . . . . . . . . . . . . . . . 141 | . . . . . . . . . . . . . . . . . . . 142 | . . . . . . . . . . . . . . . . . . . 143 | "#; 144 | test_win(s, 2, 2, Some(Tile::WHITE)); 145 | } 146 | 147 | #[test] 148 | fn test_win_big() { 149 | let mut s = r#"19 150 | . . . . . . . . . . . . . . . . . . . 151 | . . . . . . . . . . . . . . . . . . . 152 | . . . . . . . . . . . . . . . . . . . 153 | . . . . . . . . . . . . . . . . . . . 154 | . . . . . . . . . . . . . . . . . . . 155 | . . . . . . . . . . . . . . . . . . . 156 | . . . . . . . . . . . . . . . . . . . 157 | . . . W . . . . . . . . . . . . . . . 158 | . . . . W . . . . . . . . . . . . . . 159 | . . . . . W . . . . . . . . . . . . . 160 | . . . . . . W . . . . . . . . . . . . 161 | . . . . . . . W . . . . . . . . . . . 162 | . . . . . . . . . . . . . . . . . . . 163 | . . . . . . . . . . . . . . . . . . . 164 | . . . . . . . . . . . . . . . . . . . 165 | . . . . . . . . . . . . . . . . . . . 166 | . . . . . . . . . . . . . . . . . . . 167 | . . . . . . . . . . . . . . . . . . . 168 | . . . . . . . . . . . . . . . . . . . 169 | "#; 170 | test_win(s, 3, 7, Some(Tile::WHITE)); 171 | test_win(s, 2, 7, None); 172 | } 173 | -------------------------------------------------------------------------------- /src/ai/test_move_to_evaluate.rs: -------------------------------------------------------------------------------- 1 | use board::{GoBoard, Tile, Team}; 2 | use super::move_to_evaluate::move_to_evaluate; 3 | 4 | fn test_one(s: &str, nb_result: usize) { 5 | let board = GoBoard::parse_with_size(&s.to_string()); 6 | let (team_b, team_w) = Team::new_teams(); 7 | println!("Test\n{}", board); 8 | let result = move_to_evaluate(&board); 9 | println!("Move to evaluate {:?}, {}, expect {}", 10 | result, result.len(), nb_result); 11 | assert!(result.len() == nb_result); 12 | } 13 | 14 | #[test] 15 | fn test_move_to_evaluate() { 16 | 17 | let s = r#"19 18 | . . . . . . . . . . . . . . . . . . . 19 | . . . . . . . . . . . . . . . . . . . 20 | . . . . . . . . . . . . . . . . . . . 21 | . . . . . . . . . . . . . . . . . . . 22 | . . . . . . . . . . . . . . . . . . . 23 | . . . . . . . . . . . . . . . . . . . 24 | . . . . . . . . . . . . . . . . . . . 25 | . . . . . . . . . . . . . . . . . . . 26 | . . . . . . . . . . . . . . . . . . . 27 | . . . . . . . . . . . . . . . . . . . 28 | . . . . . . . . . . . . . . . . . . . 29 | . . . . . . . . . . . . . . . . . . . 30 | . . . . . . . . . . . . . . . . . . . 31 | . . . . . . . . . . . . . . . . . . . 32 | . . . . . . . . . . . . . . . . . . . 33 | . . . . . . . . . . . . . . . . . . . 34 | . . . . . . . . . . . . . . . . . . . 35 | . . . . . . . . . . . . . . . . . . . 36 | . . . . . . . . . . . . . . . . . . . 37 | "#; 38 | test_one(s, 0); 39 | 40 | let s = r#"19 41 | W . . . . . . . . . . . . . . . . . . 42 | . . . . . . . . . . . . . . . . . . . 43 | . . . . . . . . . . . . . . . . . . . 44 | . . . . . . . . . . . . . . . . . . . 45 | . . . . . . . . . . . . . . . . . . . 46 | . . . . . . . . . . . . . . . . . . . 47 | . . . . . . . . . . . . . . . . . . . 48 | . . . . . . . . . . . . . . . . . . . 49 | . . . . . . . . . . . . . . . . . . . 50 | . . . . . . . . . . . . . . . . . . . 51 | . . . . . . . . . . . . . . . . . . . 52 | . . . . . . . . . . . . . . . . . . . 53 | . . . . . . . . . . . . . . . . . . . 54 | . . . . . . . . . . . . . . . . . . . 55 | . . . . . . . . . . . . . . . . . . . 56 | . . . . . . . . . . . . . . . . . . . 57 | . . . . . . . . . . . . . . . . . . . 58 | . . . . . . . . . . . . . . . . . . . 59 | . . . . . . . . . . . . . . . . . . . 60 | "#; 61 | test_one(s, 3); 62 | 63 | let s = r#"19 64 | . . . . . . . . . . . . . . . . . . . 65 | . W . . . . . . . . . . . . . . . . . 66 | . . . . . . . . . . . . . . . . . . . 67 | . . . . . . . . . . . . . . . . . . . 68 | . . . . . . . . . . . . . . . . . . . 69 | . . . . . . . . . . . . . . . . . . . 70 | . . . . . . . . . . . . . . . . . . . 71 | . . . . . . . . . . . . . . . . . . . 72 | . . . . . . . . . . . . . . . . . . . 73 | . . . . . . . . . . . . . . . . . . . 74 | . . . . . . . . . . . . . . . . . . . 75 | . . . . . . . . . . . . . . . . . . . 76 | . . . . . . . . . . . . . . . . . . . 77 | . . . . . . . . . . . . . . . . . . . 78 | . . . . . . . . . . . . . . . . . . . 79 | . . . . . . . . . . . . . . . . . . . 80 | . . . . . . . . . . . . . . . . . . . 81 | . . . . . . . . . . . . . . . . . . . 82 | . . . . . . . . . . . . . . . . . . . 83 | "#; 84 | test_one(s, 8); 85 | 86 | let s = r#"19 87 | . . . . . . . . . . . . . . . . . . . 88 | . W . . . . . . . . . . . . . . . . . 89 | . W . . . . . . . . . . . . . . . . . 90 | . . . . . . . . . . . . . . . . . . . 91 | . . . . . . . . . . . . . . . . . . . 92 | . . . . . . . . . . . . . . . . . . . 93 | . . . . . . . . . . . . . . . . . . . 94 | . . . . . . . . . . . . . . . . . . . 95 | . . . . . . . . . . . . . . . . . . . 96 | . . . . . . . . . . . . . . . . . . . 97 | . . . . . . . . . . . . . . . . . . . 98 | . . . . . . . . . . . . . . . . . . . 99 | . . . . . . . . . . . . . . . . . . . 100 | . . . . . . . . . . . . . . . . . . . 101 | . . . . . . . . . . . . . . . . . . . 102 | . . . . . . . . . . . . . . . . . . . 103 | . . . . . . . . . . . . . . . . . . . 104 | . . . . . . . . . . . . . . . . . . . 105 | . . . . . . . . . . . . . . . . . . . 106 | "#; 107 | test_one(s, 10); 108 | 109 | let s = r#"19 110 | . . . . . . . . . . . . . . . . . . . 111 | . W . . . . . . . . . . . . . . . . . 112 | . . . . . . . . . . . . . . . . . . . 113 | . W . . . . . . . . . . . . . . . . . 114 | . . . . . . . . . . . . . . . . . . . 115 | . . . . . . . . . . . . . . . . . . . 116 | . . . . . . . . . . . . . . . . . . . 117 | . . . . . . . . . . . . . . . . . . . 118 | . . . . . . . . . . . . . . . . . . . 119 | . . . . . . . . . . . . . . . . . . . 120 | . . . . . . . . . . . . . . . . . . . 121 | . . . . . . . . . . . . . . . . . . . 122 | . . . . . . . . . . . . . . . . . . . 123 | . . . . . . . . . . . . . . . . . . . 124 | . . . . . . . . . . . . . . . . . . . 125 | . . . . . . . . . . . . . . . . . . . 126 | . . . . . . . . . . . . . . . . . . . 127 | . . . . . . . . . . . . . . . . . . . 128 | . . . . . . . . . . . . . . . . . . . 129 | "#; 130 | test_one(s, 13); 131 | 132 | let s = r#"19 133 | . . . . . . . . . . . . . . . . . . . 134 | . W . . . . . . . . . . . . . . . . . 135 | . . . . . . . . . . . . . . . . . . . 136 | . B . . . . . . . . . . . . . . . . . 137 | . . . . . . . . . . . . . . . . . . . 138 | . . . . . . . . . . . . . . . . . . . 139 | . . . . . . . . . . . . . . . . . . . 140 | . . . . . . . . . . . . . . . . . . . 141 | . . . . . . . . . . . . . . . . . . . 142 | . . . . . . . . . . . . . . . . . . . 143 | . . . . . . . . . . . . . . . . . . . 144 | . . . . . . . . . . . . . . . . . . . 145 | . . . . . . . . . . . . . . . . . . . 146 | . . . . . . . . . . . . . . . . . . . 147 | . . . . . . . . . . . . . . . . . . . 148 | . . . . . . . . . . . . . . . . . . . 149 | . . . . . . . . . . . . . . . . . . . 150 | . . . . . . . . . . . . . . . . . . . 151 | . . . . . . . . . . . . . . . . . . . 152 | "#; 153 | test_one(s, 13); 154 | 155 | let s = r#"19 156 | . . . . . . . . . . . . . . . . . . . 157 | B W . . . . . . . . . . . . . . . . . 158 | . . . . . . . . . . . . . . . . . . . 159 | . . . . . . . . . . . . . . . . . . . 160 | . . . . . . . . . . . . . . . . . . . 161 | . . . . . . . . . . . . . . . . . . . 162 | . . . . . . . . . . . . . . . . . . . 163 | . . . . . . . . . . . . . . . . . . . 164 | . . . . . . . . . . . . . . . . . . . 165 | . . . . . . . . . . . . . . . . . . . 166 | . . . . . . . . . . . . . . . . . . . 167 | . . . . . . . . . . . . . . . . . . . 168 | . . . . . . . . . . . . . . . . . . . 169 | . . . . . . . . . . . . . . . . . . . 170 | . . . . . . . . . . . . . . . . . . . 171 | . . . . . . . . . . . . . . . . . . . 172 | . . . . . . . . . . . . . . . . . . . 173 | . . . . . . . . . . . . . . . . . . . 174 | . . . . . . . . . . . . . . . . . . . 175 | "#; 176 | test_one(s, 7); 177 | 178 | let s = r#"19 179 | . . . . . . . . . . . . . . . . . . . 180 | . . . . . . . . . . . . . . . . . . . 181 | . . . . . . . . . . . . . . . . . . . 182 | . . . . . . . . . . . . . . . . . . . 183 | . . . . . . . . . . . . . . . . . . . 184 | . . . . . . . . . . . . . . . . . . . 185 | . . . . . . . . . . . . . . . . . . . 186 | . . . . . . . . . . . . . . . . . . . 187 | . . . . . . . . . . . . . . . . . . . 188 | . . . . . . . . . . . . . . . . . . . 189 | . . . . . . . . . . . . . . . . . . . 190 | . . . . . . . . . . . . . . . . . . . 191 | . . . . . . . . . . . . . . . . . . . 192 | . . . . . . . . . . . . . . . . . . . 193 | . . . . . . . . . . . . . . . . . . . 194 | . . . . . . . . . . . . . . . . . . . 195 | . . . . . . . . . . . . . . . . . . . 196 | . . . . . . . . . . . . . . . . . . . 197 | . . . . . . . . . . . . . . . . . . W 198 | "#; 199 | test_one(s, 3); 200 | } 201 | 202 | 203 | #[test] 204 | fn test_move_to_evaluate_value() { 205 | 206 | let s = r#"19 207 | . . . . . . . . . . . . . . . . . . . 208 | . . . . . . . . . . . . . . . . . . . 209 | . . . . . . . . . . . . . . . . . . . 210 | . . . . . . . . . . . . . . . . . . . 211 | . . . . . . . . . . . . . . . . . . . 212 | . . . . . . . . . . . . . . . . . . . 213 | . . . . . . . . . . . . . . . . . . . 214 | . . . . . . . . . . . . . . . . . . . 215 | . . . . . . . . . . . . . . . . . . . 216 | . . . . . . . . . . . . . . . . . . . 217 | . . . . . . . . . . . . . . . . . . . 218 | . . . . . . . . . . . . . . . . . . . 219 | . . . . . . . . . . . . . . . . . . . 220 | . . . . . . . . . . . . . . . . . . . 221 | . . . . . . . . . . . . . . . . . . . 222 | . . . . . . . . . . . . . . . . . . . 223 | . . . . . . . . . . . . . . . . . . . 224 | . . . . . . . . . . . . . . . . . . . 225 | . W . . . . . . . . . . . . . . . . . 226 | "#; 227 | let board = GoBoard::parse_with_size(&s.to_string()); 228 | let (team_b, team_w) = Team::new_teams(); 229 | println!("Test\n{}", board); 230 | let result = move_to_evaluate(&board); 231 | println!("Move to evaluate {:?}, {}", result, result.len()); 232 | assert!(result.contains(&(0, 18)) && result.contains(&(1, 17)) && result.contains(&(1, 17))); 233 | } 234 | -------------------------------------------------------------------------------- /src/ai/heuristic.rs: -------------------------------------------------------------------------------- 1 | extern crate std; 2 | 3 | use board::{GoBoard, Team}; 4 | use board::Tile; 5 | use ai; 6 | 7 | pub type HeuristicFn = fn(board: &GoBoard, team: Team) -> i32; 8 | 9 | const WIN: i32 = ai::INFINITE - 100; 10 | 11 | fn check_index(board: &GoBoard, x: i32, y: i32) -> bool { 12 | if x < 0 || y < 0 { 13 | return false; 14 | } 15 | if x >= board.get_size() as i32 || y >= board.get_size() as i32 { 16 | return false; 17 | } 18 | true 19 | } 20 | 21 | /// dx and dy must equal 1 or -1 22 | fn nb_in_line(board: &GoBoard, 23 | x: i32, y: i32, 24 | dx: i32, dy: i32, 25 | team: Tile) 26 | -> i32 { 27 | let mut ttl = 1; 28 | let mut free_extrems = 0; 29 | let mut blank = 0; 30 | 31 | let mut nx = x + dx; 32 | let mut ny = y + dy; 33 | while check_index(board, nx, ny) && 34 | board.get((nx as usize, ny as usize)) == team { 35 | nx += dx; 36 | ny += dy; 37 | ttl += 1; 38 | } 39 | if check_index(board, nx, ny) && 40 | board.get((nx as usize, ny as usize)) != team.ennemy() { 41 | free_extrems += 1; 42 | while check_index(board, nx, ny) && 43 | board.get((nx as usize, ny as usize)) == Tile::FREE && 44 | blank + ttl < 6 { 45 | nx += dx; 46 | ny += dy; 47 | blank += 1; 48 | } 49 | } 50 | 51 | nx = x - dx; 52 | ny = y - dy; 53 | while check_index(board, nx, ny) && 54 | board.get((nx as usize, ny as usize)) == team { 55 | nx -= dx; 56 | ny -= dy; 57 | ttl += 1; 58 | } 59 | if check_index(board, nx, ny) && 60 | board.get((nx as usize, ny as usize)) != team.ennemy() { 61 | free_extrems += 1; 62 | while check_index(board, nx, ny) && 63 | board.get((nx as usize, ny as usize)) == Tile::FREE && 64 | blank + ttl < 6 { 65 | nx -= dx; 66 | ny -= dy; 67 | blank += 1; 68 | } 69 | } 70 | 71 | if blank + ttl < 5 { // if can't expand to victory line, this line is useless 72 | 0 73 | } else if ttl >= 5 { 74 | WIN + (ttl - 5) * free_extrems 75 | } else if free_extrems == 2 && ttl >= 4 { 76 | WIN - 1 77 | } else if ttl > 0 { 78 | ttl * free_extrems 79 | } else { 80 | 0 81 | } 82 | } 83 | 84 | fn tile_value(board: &GoBoard, 85 | x: i32, 86 | y: i32, 87 | team: Tile) 88 | -> i32 { 89 | let mut ttl_tile = 0; 90 | let score_tile = nb_in_line(board, x, y, 1, 1, team); 91 | if score_tile >= WIN - 10 { 92 | return WIN; 93 | } 94 | ttl_tile += score_tile; 95 | let score_tile = nb_in_line(board, x, y, 0, 1, team); 96 | if score_tile >= WIN - 10 { 97 | return WIN; 98 | } 99 | ttl_tile += score_tile; 100 | let score_tile = nb_in_line(board, x, y, 1, 0, team); 101 | if score_tile >= WIN - 10 { 102 | return WIN; 103 | } 104 | ttl_tile += score_tile; 105 | let score_tile = nb_in_line(board, x, y, 1, -1, team); 106 | if score_tile >= WIN - 10 { 107 | return WIN; 108 | } 109 | ttl_tile += score_tile; 110 | ttl_tile 111 | } 112 | 113 | pub fn heuristic(board: &GoBoard, team: Team) -> i32 { 114 | let mut player_score = team.captured() as i32 * team.captured() as i32; 115 | let mut enemy_score = 0; 116 | for x in 0..board.get_size() { 117 | for y in 0..board.get_size() { 118 | match board.get((x, y)) { 119 | Tile::FREE => {}, 120 | t if team.get_tile() == t => { 121 | let tile_score = tile_value(board, x as i32, y as i32, team.get_tile()); 122 | if tile_score >= WIN - 10 { 123 | return tile_score; 124 | } else { 125 | player_score += tile_score; 126 | } 127 | }, 128 | t if team.get_ennemy_tile() == t => { 129 | let tile_score = tile_value(board, x as i32, y as i32, team.get_ennemy_tile()); 130 | if tile_score >= WIN - 10 { 131 | return tile_score; 132 | } 133 | let tile_score = tile_value(board, x as i32, y as i32, team.get_ennemy_tile()); 134 | if tile_score == WIN { 135 | return ai::NINFINITE; 136 | } else { 137 | enemy_score += tile_score; 138 | } 139 | }, 140 | _ => {} 141 | } 142 | } 143 | } 144 | player_score - enemy_score 145 | } 146 | 147 | /// Heuristic for white is greater than black. 148 | #[cfg(test)] 149 | fn test_one(s: &str) { 150 | let board = GoBoard::parse_with_size(&s.to_string()); 151 | let (team_b, team_w) = Team::new_teams(); 152 | println!("Test\n{}", board); 153 | let white = heuristic(&board, team_w); 154 | let black = heuristic(&board, team_b); 155 | println!("Is white {} > black {}", white, black); 156 | assert!(white >black); 157 | } 158 | 159 | /*#[test] 160 | fn test_heuristic() { 161 | let s = r#"19 162 | W . . . . . . . . . . . . . . . . . . 163 | . . . . . . . . . . . . . . . . . . . 164 | . . . . . . . . . . . . . . . . . . . 165 | . . . . . . . . . . . . . . . . . . . 166 | . . . . . . . . . . . . . . . . . . . 167 | . . . . . . . . . . . . . . . . . . . 168 | . . . . . . . . . . . . . . . . . . . 169 | . . . . . . . . . . . . . . . . . . . 170 | . . . . . . . . . . . . . . . . . . . 171 | . . . . . . . . . . . . . . . . . . . 172 | . . . . . . . . . . . . . . . . . . . 173 | . . . . . . . . . . . . . . . . . . . 174 | . . . . . . . . . . . . . . . . . . . 175 | . . . . . . . . . . . . . . . . . . . 176 | . . . . . . . . . . . . . . . . . . . 177 | . . . . . . . . . . . . . . . . . . . 178 | . . . . . . . . . . . . . . . . . . . 179 | . . . . . . . . . . . . . . . . . . . 180 | . . . . . . . . . . . . . . . . . . W 181 | "#; 182 | test_one(s); 183 | 184 | let s = r#"19 185 | . . . . . . . . . . . . . . . . . . . 186 | . . . . . . . . . . . . . . . . . . . 187 | . . . . . . . . . . . . . . . . . . . 188 | . . . . . . . . . . . . . . . . . . . 189 | . . . . . . . . . . . . . . . . . . . 190 | . . . . . . . . . . . . . . . . . . . 191 | . . . . . . . . . . . . . . . . . . . 192 | . . . . . . . . . W . B . . . . . . . 193 | . . . . . . . . . W . . . . . . . . . 194 | . . . . . . . . . W . B . . . . . . . 195 | . . . . . . . . . W . . . . . . . . . 196 | . . . . . . . . . . . B . . . . . . . 197 | . . . . . . . . . . . . . . . . . . . 198 | . . . . . . . . . . . B . . . . . . . 199 | . . . . . . . . . . . . . . . . . . . 200 | . . . . . . . . . . . . . . . . . . . 201 | . . . . . . . . . . . . . . . . . . . 202 | . . . . . . . . . . . . . . . . . . . 203 | . . . . . . . . . . . . . . . . . . . 204 | "#; 205 | test_one(s); 206 | 207 | let s = r#"19 208 | . . . . . . . . . B . . . . . . . . . 209 | . . . . . . . . . B . . . . . . . . . 210 | . . . . . . . . . B . . . . . . . . . 211 | . . . . B . . . . B . . . . . . . . . 212 | . . . . B . . . . . . . . . . . . . . 213 | . . . . B . . . . . . . . . . . . . . 214 | . . . . B . . . . . . B . . . . . . . 215 | . . . . . . . . . W . B . . . . . . . 216 | . . . . . . . . . W . B . . . . . . . 217 | . . . . . . . . . W . B . . . . . . . 218 | . . . . . . . . . W . . . . . . . . . 219 | . . . . . . . . . W . B . . . . . . . 220 | . . . . . . . . . . . B . . . . . . . 221 | . . . . . . . . . . . B . . . . . . . 222 | . . B B B B . . . . . B . . . . . . . 223 | . . . . . . . . . . . . . . . . . . . 224 | . . . . . . . . . . . . . . . . . . . 225 | . . . . . . . . . . . . . . . . . . . 226 | . . . . . . . . . . . . . . . . . . . 227 | "#; 228 | test_one(s); 229 | 230 | let s = r#"19 231 | . . . . . . . . . . . . . . . . . . . 232 | . . . . . . . . . . . . . . . . . . . 233 | . . . . . . . . . . . . . . . . . . . 234 | . . . . . . . . . B . . . . . . . . . 235 | . . . . . . . . . . . W . . . . . . . 236 | . . . . . . . . . . . . . . . . . . . 237 | . . . . . . . . . . . . . . . . . . . 238 | . . . . . . . . . W . B . . . . . . . 239 | . . . . . . . . . W . B . . . . . . . 240 | . . . . . . . . . B . W . . . . . . . 241 | . . . . . . . . . . . . . . . . . . . 242 | . . . . . . . . . . . . . . . . . . . 243 | . . . . . . . . . . . . . . . . . . . 244 | . . . . . . . . . . . . . . . . . . . 245 | . . . . . . . . . . . . . . . . . . . 246 | . . . . . . . . . . . . . . . . . . . 247 | . . . . . . . . . . . . . . . . . . . 248 | . . . . . . . . . . . . . . . . . . . 249 | . . . . . . . . . . . . . . . . . . . 250 | "#; 251 | test_one(s); 252 | } 253 | */ 254 | -------------------------------------------------------------------------------- /src/board/go_board.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use std::fmt::{Formatter, Display, Error}; 3 | use board::{Tile, Team}; 4 | 5 | pub const GO_WIDTH : usize = 19; 6 | const TILES_TO_WIN : usize = 5; 7 | 8 | #[derive(Debug, PartialEq, Clone)] 9 | pub struct GoBoard { 10 | /// The grid 11 | /// 12 | /// First iterate over lines and then element by element. 13 | pub tiles: [[Tile; GO_WIDTH]; GO_WIDTH], // The grid 14 | size: usize, // Side 15 | } 16 | 17 | // test for one free threes pattern match 18 | macro_rules! test_goban_pattern { 19 | ($board:ident, $team:ident, $coords:ident, $($ty:expr => $gap:expr),*) => {{ 20 | let mut result = true; 21 | $( 22 | let expected = match $ty { 23 | "o" => $team.get_tile(), 24 | "e" => $team.get_ennemy_tile(), 25 | "x" => Tile::FREE, 26 | _ => panic!("GoBoard::test_goban_pattern syntax error") 27 | }; 28 | // println!("[{}, {}]{:?} exp {:?}", $coords.0, $coords.1, 29 | // $board.get(($coords.0, $coords.1)), expected); 30 | result = $board.is_exp($coords, $gap, expected) && result; 31 | )* 32 | result 33 | }} 34 | } 35 | 36 | impl GoBoard { 37 | 38 | /// The `get` function returns the tiles coordinates [x; y]. 39 | pub fn get(&self, (x, y): (usize, usize)) -> Tile { 40 | self.tiles[x][y].clone() 41 | } 42 | 43 | fn capture_dir(&mut self, 44 | x: usize, 45 | y: usize, 46 | rightdir: i32, 47 | downdir: i32, 48 | team: &mut Team, 49 | ) { 50 | let coords = (x, y, downdir, rightdir); 51 | let left = test_goban_pattern!(self, team, coords, 52 | "o" => 3, "e" => 2, "e" => 1); 53 | let right = test_goban_pattern!(self, team, coords, 54 | "o" => -3, "e" => -2, "e" => -1); 55 | if left { 56 | self.unset_gap(coords, 1); 57 | self.unset_gap(coords, 2); 58 | team.add_captured(2); 59 | } 60 | if right { 61 | self.unset_gap(coords, -1); 62 | self.unset_gap(coords, -2); 63 | team.add_captured(2); 64 | } 65 | } 66 | 67 | /// Test if playing this tile capture ennemy tiles and remove ennemy tiles 68 | /// and update number of captured tiles in the team if needed. 69 | fn capture(&mut self, x: usize, y: usize, team: &mut Team) { 70 | self.capture_dir(x, y, 1, 0, team); 71 | self.capture_dir(x, y, 0, 1, team); 72 | self.capture_dir(x, y, 1, 1, team); 73 | self.capture_dir(x, y, 1, -1, team); 74 | } 75 | 76 | /// Assigns the value to tiles coordinates [x; y] without any check. 77 | pub fn set_raw(&mut self, (x, y): (usize, usize), tile: Tile) { 78 | self.tiles[x][y] = tile; 79 | } 80 | 81 | /// The `set` function assigns the value to tiles coordinates [x; y] 82 | /// if possible. Return false otherwise. 83 | pub fn set(&mut self, (x, y): (usize, usize), team: &mut Team) -> bool { 84 | if !self.is_allow(x, y, team) { 85 | return false; 86 | } 87 | self.capture(x, y, team); 88 | self.set_raw((x, y), team.get_tile()); 89 | true 90 | } 91 | 92 | /// The `unset` function assigns the FREE 93 | /// to tiles coordinates [x; y]. 94 | pub fn unset(&mut self, cell: (usize, usize)) { 95 | self.set_raw(cell, Tile::FREE); 96 | } 97 | 98 | pub fn unset_gap(&mut self, 99 | coords: (usize, usize, i32, i32), 100 | gap: i32, 101 | ) { 102 | let x = coords.0 as i32 - coords.2 * gap; 103 | let y = coords.1 as i32 - coords.3 * gap; 104 | if x < 0 || y < 0 || !self.check_index((x as usize, y as usize)) { 105 | return ; 106 | } 107 | self.unset((x as usize, y as usize)); 108 | } 109 | 110 | /// The `get_size` function returns the size of 111 | /// the grid side. 112 | 113 | pub fn get_size(&self) -> usize { 114 | self.size 115 | } 116 | 117 | /// The `check_index` function returns a boolean 118 | /// if the index is within the bounds of the board. 119 | pub fn check_index (&self, (x, y): (usize, usize)) -> bool { 120 | x <= self.size - 1 && y <= self.size - 1 121 | } 122 | 123 | fn is_win_recursive ( 124 | &self, x: i32, 125 | y: i32, 126 | downdir: i32, 127 | rightdir: i32, 128 | tile_type: Tile, 129 | ttl: usize, 130 | ) -> usize { 131 | if x < 0 || y < 0 || 132 | !self.check_index((x as usize, y as usize)) || 133 | self.get((x as usize, y as usize)) != tile_type { 134 | return ttl; 135 | } 136 | self.is_win_recursive ( 137 | x - rightdir, 138 | y - downdir, 139 | downdir, 140 | rightdir, 141 | tile_type, 142 | ttl + 1 143 | ) 144 | } 145 | 146 | /// Test if the tile at position [x, y] is winning on the direction 147 | /// [x - rightdir, y - downdir]. 148 | fn is_win_direction ( 149 | &self, 150 | x: usize, 151 | y: usize, 152 | downdir: i32, 153 | rightdir: i32, 154 | ) -> bool { 155 | let tiles_on_dir = self.is_win_recursive ( 156 | x as i32, 157 | y as i32, 158 | downdir, 159 | rightdir, 160 | self.get((x, y)), 161 | 0 162 | ); 163 | let tiles_on_opposite_dir = self.is_win_recursive ( 164 | x as i32, 165 | y as i32, 166 | -downdir, 167 | -rightdir, 168 | self.get((x, y)), 169 | 0 170 | ); 171 | 172 | // since both tiles_on_dir and tiles_on_opposite_dir include the 173 | // begin tiles, we must substract it. 174 | (tiles_on_dir + tiles_on_opposite_dir - 1) >= TILES_TO_WIN 175 | } 176 | 177 | /// Test if the tile at [x, y] is a winning one. 178 | /// 179 | /// The type of the tile represent the winning team 180 | /// (if Tile::WHITE, the white team has won). 181 | pub fn is_win(&self, x: usize, y: usize) -> Option { 182 | if self.get((x, y)) == Tile::FREE { 183 | return None; 184 | } 185 | if self.is_win_direction(x, y, 1, 0) || 186 | self.is_win_direction(x, y, 0, 1) || 187 | self.is_win_direction(x, y, 1, 1) || 188 | self.is_win_direction(x, y, 1, -1) { 189 | return Some(self.get((x, y))) 190 | } 191 | None 192 | } 193 | 194 | /// Return true if the tile which is positionned at gap tiles from the 195 | /// tested tile on the direction defined by coords is of the expected type. 196 | fn is_exp(&self, 197 | coords: (usize, usize, i32, i32), 198 | gap: i32, 199 | expected: Tile, 200 | ) -> bool { 201 | let x = coords.0 as i32 - coords.2 * gap; 202 | let y = coords.1 as i32 - coords.3 * gap; 203 | if x < 0 || y < 0 || !self.check_index((x as usize, y as usize)) { 204 | return false; 205 | } 206 | // println!("x {:?} y {:?} type {:?} expected {:?}", 207 | // x, y, self.get((x as usize, y as usize)), expected); 208 | self.get((x as usize, y as usize)) == expected 209 | } 210 | 211 | /// Return the number of free threes in this direction. 212 | /// Assume that tile[x, y] is free. 213 | fn free_threes_dir(&self, 214 | x: usize, 215 | y: usize, 216 | downdir: i32, 217 | rightdir: i32, 218 | team: &Team, 219 | ) -> u32 { 220 | let mut nb_free_three = 0; 221 | let coords = (x, y, downdir, rightdir); 222 | 223 | // x = Tile::FREE, o = Tile::Team, c = current postion (Tile::FREE) 224 | //rule 1 225 | // for xocox 226 | nb_free_three += test_goban_pattern!(self, team, coords, 227 | "x" => -2, "o" => -1, "o" => 1, "x" => 2) as u32; 228 | 229 | //rule 2 230 | // for xoocx 231 | nb_free_three += test_goban_pattern!(self, team, coords, 232 | "x" => -3, "o" => -2, "o" => -1, "x" => 1) as u32; 233 | // for xoocx (opposite) 234 | nb_free_three += test_goban_pattern!(self, team, coords, 235 | "x" => 3, "o" => 2, "o" => 1, "x" => -1) as u32; 236 | 237 | //rule 3 238 | // for xooxcx 239 | nb_free_three += test_goban_pattern!(self, team, coords, 240 | "x" => -4, "o" => -3, "o" => -2, "x" => -1, "x" => 1) as u32; 241 | // for xooxcx (opposite) 242 | nb_free_three += test_goban_pattern!(self, team, coords, 243 | "x" => 4, "o" => 3, "o" => 2, "x" => 1, "x" => -1) as u32; 244 | 245 | //return 246 | nb_free_three 247 | } 248 | 249 | /// Return true if the free threes rule allow this move. 250 | /// 251 | /// A free-three is an alignement of three stones that, if not immediately 252 | /// blocked, allows for an indefendable alignment of four stones 253 | /// (that’s to say an alignment of four stones with two unobstructed 254 | /// extremities). 255 | fn free_threes(&self, x: usize, y: usize, team: &Team) -> bool { 256 | let nb_free_three = self.free_threes_dir(x, y, 1, 0, team) + 257 | self.free_threes_dir(x, y, 0, 1, team) + 258 | self.free_threes_dir(x, y, 1, 1, team) + 259 | self.free_threes_dir(x, y, 1, -1, team); 260 | nb_free_three < 2 261 | } 262 | 263 | /// Return true if it is allowed to add a tile on the position [x, y]. 264 | /// x and y are supposed to be valid index 265 | pub fn is_allow(&self, x: usize, y: usize, team: &Team) -> bool { 266 | self.get((x, y)) == Tile::FREE && self.free_threes(x, y, team) 267 | } 268 | 269 | pub fn is_empty(&self) -> bool { 270 | for line in self.tiles.iter() { 271 | for tile in line.iter() { 272 | if tile.is_pawn() { 273 | return false; 274 | } 275 | } 276 | } 277 | true 278 | } 279 | 280 | /// Return the (x, y) coordinates out of the index of the tile in the 281 | /// GoBoard::tiles array. 282 | pub fn coord_out_of_index(index: usize) -> (usize, usize) { 283 | (index % GO_WIDTH, index / GO_WIDTH) 284 | } 285 | } 286 | 287 | impl Display for GoBoard { 288 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 289 | let mut to_return = Ok(()); 290 | for y in 0..self.get_size() { 291 | for x in 0..self.get_size() { 292 | to_return = to_return.and(write!(f, "{} ", self.get((x, y)))); 293 | } 294 | to_return = to_return.and(write!(f, "\n")); 295 | } 296 | to_return 297 | } 298 | } 299 | 300 | impl Default for GoBoard { 301 | 302 | /// The `new` constructor function returns the empty board. 303 | 304 | fn default() -> Self { 305 | GoBoard { 306 | tiles: [[Default::default(); GO_WIDTH]; GO_WIDTH], 307 | size: GO_WIDTH, 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/ai/decision.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::sync::mpsc; 3 | use std; 4 | use board::{GoBoard, Team, Tile}; 5 | use ai; 6 | use ai::turn::Turn; 7 | use ai::heuristic::HeuristicFn; 8 | use chrono::{UTC, Duration}; 9 | 10 | const NB_THREAD: usize = 4; 11 | 12 | #[derive(Debug, PartialEq, Clone)] 13 | pub struct Decision { 14 | player: Team, 15 | nb_layers: u32, 16 | nb_node: usize, 17 | nb_final: usize, 18 | time_in_heuristic: Duration, 19 | total_time: Duration, 20 | result: (usize, usize) 21 | } 22 | 23 | impl Decision { 24 | fn playing_team( 25 | turn: &Turn, 26 | teams: &(Team, Team), 27 | player: &Team, 28 | ) -> Team { 29 | match *turn { 30 | Turn::Adversary => { 31 | match player.get_tile() { 32 | Tile::BLACK => teams.1.clone(), 33 | Tile::WHITE => teams.0.clone(), 34 | _ => panic!("No Free tile allowed"), 35 | } 36 | }, 37 | Turn::Player => { 38 | match player.get_tile() { 39 | Tile::BLACK => teams.0.clone(), 40 | Tile::WHITE => teams.1.clone(), 41 | _ => panic!("No Free tile allowed"), 42 | } 43 | }, 44 | } 45 | } 46 | 47 | /// Return the tuple (team black, team white) with the new team 48 | fn updated_team(&(ref tb, ref tw): &(Team, Team), new_team: Team) -> (Team, Team) { 49 | match new_team.get_tile() { 50 | Tile::BLACK => (new_team, tw.clone()), 51 | Tile::WHITE => (tb.clone(), new_team), 52 | Tile::FREE => panic!("error forbiden team tile") 53 | } 54 | } 55 | 56 | fn splitted_moves_to_evaluate(board: &GoBoard) -> Vec> { 57 | let mut moves = super::move_to_evaluate::move_to_evaluate(&board); 58 | let ttl_len = moves.len(); 59 | let mut to_return = Vec::new(); 60 | if moves.len() == 0 { 61 | panic!("No winner !"); 62 | } 63 | 64 | let split = moves.len() / NB_THREAD; 65 | let nb_split = std::cmp::min(NB_THREAD, moves.len()); 66 | for i in 1..nb_split { 67 | let y = moves; 68 | let (n, m) = if i < ttl_len % NB_THREAD { 69 | y.split_at(split + 1) 70 | } else { 71 | y.split_at(split) 72 | }; 73 | moves = m.to_vec(); 74 | to_return.push(n.to_vec()); 75 | } 76 | to_return.push(moves.to_vec()); 77 | to_return 78 | } 79 | 80 | /// launch the recursive for one of the move to evaluate 81 | fn compute_one_move(&mut self, 82 | coords: (usize, usize), 83 | board: &mut GoBoard, 84 | mut playing_team: Team, 85 | teams: (Team, Team), 86 | nb_layers: u32, 87 | turn: Turn, 88 | albet: (i32, i32), 89 | heuristic: HeuristicFn 90 | ) -> ((usize, usize), i32) { 91 | if board.set(coords, &mut playing_team) { 92 | let winning_team = board.is_win(coords.0, coords.1); 93 | if winning_team.is_some() { 94 | let points = if winning_team.unwrap() == playing_team.get_tile() { 95 | (coords, ai::INFINITE - (self.nb_layers - nb_layers) as i32) 96 | } else { 97 | (coords, ai::NINFINITE + (self.nb_layers - nb_layers) as i32) 98 | }; 99 | // println!("layer {} turn {:?} coords {:?} winning team {:?} points {}", 100 | // nb_layers, turn, points.0, winning_team, points.1); 101 | return points; 102 | } 103 | } 104 | let teams = Decision::updated_team(&teams, playing_team.clone()); 105 | let (_, heur) = self.recursive( 106 | board, turn, teams.clone(), nb_layers, albet, heuristic); 107 | (coords, heur) 108 | } 109 | 110 | /// albet: alpha < beta 111 | /// [algo explication](https://en.wikipedia.org/wiki/Negamax) 112 | fn recursive(&mut self, 113 | board: &mut GoBoard, 114 | turn: Turn, 115 | teams: (Team, Team), 116 | nb_layers: u32, 117 | (mut alpha, beta) : (i32, i32), 118 | heuristic: HeuristicFn, 119 | ) -> ((usize, usize), i32) { 120 | self.nb_node += 1; 121 | let playing_team: Team = Decision::playing_team(&turn, &teams, &mut self.player).clone(); 122 | 123 | // if it is a leaf return heuristic value for this board 124 | if nb_layers == 0 { 125 | let updated_player = match self.player.get_tile() { 126 | Tile::BLACK => teams.0, 127 | Tile::WHITE => teams.1, 128 | Tile::FREE => panic!("bad team type"), 129 | }; 130 | // is there moves where the coords value matter for this ? 131 | self.nb_final += 1; 132 | let (coords, value) = ((0, 0), (heuristic)(&board, updated_player)); 133 | return (coords, value * turn.sign_alternation()); 134 | } 135 | 136 | //else recursive to call each childs 137 | 138 | // get potential next moves 139 | let moves = super::move_to_evaluate::move_to_evaluate(&board); 140 | 141 | // best heuristic value for one move set to -infinite 142 | let mut best_value = ai::NINFINITE; 143 | let mut best_coord = (0, 0); 144 | for mov in moves { 145 | let (one_coord, one_val) = self.compute_one_move(mov, 146 | &mut board.clone(), 147 | playing_team.clone(), 148 | teams.clone(), 149 | nb_layers - 1, 150 | turn.other(), 151 | (ai::neg_infinite(beta), ai::neg_infinite(alpha)), 152 | heuristic); 153 | if one_val > best_value { 154 | best_value = one_val; 155 | best_coord = one_coord; 156 | alpha = std::cmp::max(alpha, best_value); 157 | if alpha >= beta { 158 | break ; 159 | } 160 | } 161 | } 162 | (best_coord, ai::neg_infinite(best_value)) 163 | } 164 | 165 | /// albet: alpha < beta 166 | /// [algo explication](https://en.wikipedia.org/wiki/Negamax) 167 | fn one_thread(&mut self, 168 | moves: Vec<(usize, usize)>, 169 | board: &mut GoBoard, 170 | teams: (Team, Team), 171 | nb_layers: u32, 172 | (mut alpha, beta) : (i32, i32), 173 | heuristic: HeuristicFn, 174 | ) -> ((usize, usize), i32) { 175 | self.nb_node += 1; 176 | let playing_team: Team = Decision::playing_team(&Turn::Player, &teams, &mut self.player).clone(); 177 | 178 | // best heuristic value for one move set to -infinite 179 | let mut best_value = ai::NINFINITE; 180 | let mut best_coord = (0, 0); 181 | for mov in moves { 182 | let (one_coord, one_val) = self.compute_one_move(mov, 183 | &mut board.clone(), 184 | playing_team.clone(), 185 | teams.clone(), 186 | nb_layers, 187 | Turn::Adversary, 188 | (ai::neg_infinite(beta), ai::neg_infinite(alpha)), 189 | heuristic); 190 | if one_val > best_value { 191 | best_value = one_val; 192 | best_coord = one_coord; 193 | alpha = std::cmp::max(alpha, best_value); 194 | if alpha >= beta { 195 | break ; 196 | } 197 | } 198 | } 199 | (best_coord, ai::neg_infinite(best_value)) 200 | } 201 | 202 | fn launch_threads(&mut self, 203 | board: &mut GoBoard, 204 | teams: (Team, Team), 205 | nb_layers: u32, 206 | (alpha, beta) : (i32, i32), 207 | heuristic: HeuristicFn, 208 | ) -> ((usize, usize), i32) { 209 | let list_moves = Decision::splitted_moves_to_evaluate(board); 210 | 211 | // best heuristic value for one move set to -infinite 212 | let (tx, rx) = mpsc::channel(); 213 | 214 | //spawn one resolution thread for each move 215 | for mov in &list_moves { 216 | let tx = tx.clone(); 217 | 218 | //clone a lot of stuff so that we could send them to the thread 219 | let mut self_c = self.clone(); 220 | let mut board_c = board.clone(); 221 | let mov_c = mov.clone(); 222 | 223 | thread::spawn(move || { 224 | let res = self_c.one_thread(mov_c, 225 | &mut board_c, 226 | teams.clone(), 227 | nb_layers, 228 | (ai::neg_infinite(beta), 229 | ai::neg_infinite(alpha)), 230 | heuristic); 231 | let _ = tx.send(res); 232 | }); 233 | } 234 | 235 | let mut results = Vec::with_capacity(list_moves.len()); 236 | for _ in 0..list_moves.len() { 237 | let res = rx.recv().unwrap(); 238 | results.push(res); 239 | } 240 | 241 | // select min or max according to convenience 242 | let res = results.iter().min_by_key(|x| x.1); 243 | *(res.unwrap()) 244 | } 245 | 246 | pub fn print_result(&self) { 247 | println!("Time to compute {: >#2}s {}ms for {} layers", 248 | self.total_time.num_seconds(), 249 | self.total_time.num_milliseconds(), 250 | self.nb_layers); 251 | } 252 | 253 | /// Return the coordinates of the move which is considered to maximise the 254 | /// odds of victory for the team. 255 | /// 256 | /// teams the two teams, black first and white second 257 | /// 258 | /// player is the team for which we want to find the optimal move 259 | /// 260 | /// nb_layers is the number of move which is going to be evaluated by the ai 261 | /// to evaluate the best move. The higher will improve the quality of result 262 | /// but also computationnal time. 263 | pub fn get_optimal_move ( 264 | board: &mut GoBoard, 265 | teams: &(Team, Team), 266 | player: Team, 267 | nb_layers: u32, 268 | heuristic: HeuristicFn 269 | ) -> Decision { 270 | let mut dec = Decision { 271 | player: player.clone(), 272 | nb_node: 0, 273 | nb_final: 0, 274 | time_in_heuristic: Duration::zero(), 275 | total_time: Duration::zero(), 276 | result: (0, 0), 277 | nb_layers: nb_layers 278 | }; 279 | if board.is_empty() { 280 | dec.result = (9, 9); 281 | return dec; 282 | } 283 | let begin = UTC::now(); 284 | let (coords, _) = dec.launch_threads(board, *teams, nb_layers - 1, 285 | (ai::NINFINITE, ai::INFINITE), heuristic); 286 | let end = UTC::now(); 287 | dec.result = coords; 288 | dec.total_time = end - begin; 289 | dec 290 | } 291 | 292 | pub fn get_result(&self) -> (usize, usize) { 293 | self.result 294 | } 295 | } 296 | 297 | #[test] 298 | fn test_playing_team() { 299 | let teams = Team::new_teams(); 300 | let tadv = Turn::Adversary; 301 | let tpla = Turn::Player; 302 | let (adv, pla) = Team::new_teams(); // player is white 303 | 304 | assert!(Decision::playing_team(&tpla, &teams, &pla) == pla); 305 | assert!(Decision::playing_team(&tadv, &teams, &pla) == adv); 306 | assert!(Decision::playing_team(&tpla, &teams, &adv) == adv); 307 | assert!(Decision::playing_team(&tadv, &teams, &adv) == pla); 308 | } 309 | -------------------------------------------------------------------------------- /src/ai/test_decision.rs: -------------------------------------------------------------------------------- 1 | use board::{GoBoard, Tile, Team}; 2 | use ai; 3 | use ai::Decision; 4 | use ai::heuristic::HeuristicFn; 5 | 6 | // test which move is the best for the white team 7 | fn test_one(s: &str, heur: HeuristicFn, nb_layers: u32, expected: Vec<(usize, usize)>) { 8 | let mut board = GoBoard::parse_with_size(&s.to_string()); 9 | println!("Test\n{}", board); 10 | let (team_b, team_w) = Team::new_teams(); 11 | let result = 12 | Decision::get_optimal_move(&mut board, &(team_b, team_w.clone()), team_w, nb_layers, heur); 13 | println!("result {:?}, expected {:?}\n", result.get_result(), expected); 14 | 15 | for exp in expected { 16 | if exp == result.get_result() { 17 | return ; 18 | } 19 | } 20 | panic!("Test decision failed"); 21 | } 22 | 23 | // Does not work anymore because we don't update team captured. 24 | fn heur_capture(board: &GoBoard, team: Team) -> i32 { 25 | team.captured() as i32 26 | } 27 | 28 | ///test if the team captured is updated 29 | #[test] 30 | fn test_team_capture() { 31 | let s = r#"19 32 | . . . . . . . . . . . . . . . . . . . 33 | . W B B . . . . . . . . . . . . . . . 34 | . . . . . . . . . . . . . . . . . . . 35 | . . . . . . . . . . . . . . . . . . . 36 | . . . . . . . . . . . . . . . . . . . 37 | . . . . . . . . . . . . . . . . . . . 38 | . . . . . . . . . . . . . . . . . . . 39 | . . . . . . . . . . . . . . . . . . . 40 | . . . . . . . . . . . . . . . . . . . 41 | . . . . . . . . . . . . . . . . . . . 42 | . . . . . . . . . . . . . . . . . . . 43 | . . . . . . . . . . . . . . . . . . . 44 | . . . . . . . . . . . . . . . . . . . 45 | . . . . . . . . . . . . . . . . . . . 46 | . . . . . . . . . . . . . . . . . . . 47 | . . . . . . . . . . . . . . . . . . . 48 | . . . . . . . . . . . . . . . . . . . 49 | . . . . . . . . . . . . . . . . . . . 50 | . . . . . . . . . . . . . . . . . . . 51 | "#; 52 | test_one(s, heur_capture, 1, vec![(4, 1)]); 53 | } 54 | 55 | fn heur_tile_coords(board: &GoBoard, team: Team) -> i32 { 56 | let mut ttl = 0; 57 | for (x, line) in board.tiles.iter().enumerate() { 58 | for (y, tile) in line.iter().enumerate() { 59 | if tile.is_pawn() { 60 | ttl += (y * 19) + x; 61 | } 62 | } 63 | } 64 | ttl as i32 65 | } 66 | 67 | #[test] 68 | fn test_decision() { 69 | let s = r#"19 70 | W . . . . . . . . . . . . . . . . . . 71 | . . . . . . . . . . . . . . . . . . . 72 | . . . . . . . . . . . . . . . . . . . 73 | . . . . . . . . . . . . . . . . . . . 74 | . . . . . . . . . . . . . . . . . . . 75 | . . . . . . . . . . . . . . . . . . . 76 | . . . . . . . . . . . . . . . . . . . 77 | . . . . . . . . . . . . . . . . . . . 78 | . . . . . . . . . . . . . . . . . . . 79 | . . . . . . . . . . . . . . . . . . . 80 | . . . . . . . . . . . . . . . . . . . 81 | . . . . . . . . . . . . . . . . . . . 82 | . . . . . . . . . . . . . . . . . . . 83 | . . . . . . . . . . . . . . . . . . . 84 | . . . . . . . . . . . . . . . . . . . 85 | . . . . . . . . . . . . . . . . . . . 86 | . . . . . . . . . . . . . . . . . . . 87 | . . . . . . . . . . . . . . . . . . . 88 | . . . . . . . . . . . . . . . . . . . 89 | "#; 90 | test_one(s, heur_tile_coords, 2, vec![(1, 1)]); 91 | test_one(s, heur_tile_coords, 3, vec![(1, 1)]); 92 | test_one(s, heur_tile_coords, 4, vec![(1, 1)]); 93 | } 94 | 95 | #[test] 96 | fn test_get_optimal_move() { 97 | let s = r#"19 98 | W . . . . . . . . . . . . . . . . . . 99 | . W . . . . . . . . . . . . . . . . . 100 | . . W . . . . . . . . . . . . . . . . 101 | . . . W . . . . . . . . . . . . . . . 102 | . . . . . . . . . . . . . . . . . . . 103 | . . . . . . . . . . . . . . . . . . . 104 | . . . . . . . . . . . . . . . . . . . 105 | . . . . . . . . . . . . . . . . . . . 106 | . . . . . . . . . . . . . . . . . . . 107 | . . . . . . . . . . . . . . . . . . . 108 | . . . . . . . . . . . . . . . . . . . 109 | . . . . . . . . . . . . . . . . . . . 110 | . . . . . . . . . . . . . . . . . . . 111 | . . . . . . . . . . . . . . . . . . . 112 | . . . . . . . . . . . . . . . . . . . 113 | . . . . . . . . . . . . . . . . . . . 114 | . . . . . . . . . . . . . . . . . . . 115 | . . . . . . . . . . . . . . . . . . . 116 | . . . . . . . . . . . . . . . . . . . 117 | "#; 118 | test_one(s, ai::heuristic, 3, vec![(4, 4)]); 119 | 120 | let s = r#"19 121 | W . . . . . . . . . . . . . . . . . . 122 | . W . . . . . . . . . . . . . . . . . 123 | . . W . . . . . . . . . . . . . . . . 124 | . . . W . . . . . . . . . . . . . . . 125 | . . . . . . . . . . . . . . . . . . . 126 | . . . . . . . . . . . . . . . . . . . 127 | . . . . . . . . . . . . . . . . . . . 128 | . . . . . . W . . . . . . . . . . . . 129 | . . . . . . . W . . . . . . . . . . . 130 | . . . . . . . . W . . . . . . . . . . 131 | . . . . . . . . . . . . . . . . . . . 132 | . . . . . . . . . . . . . . . . . . . 133 | . . . . . . . . . . . . . . . . . . . 134 | . . . . . . . . . . . . . . . . . . . 135 | . . . . . . . . . . . . . . . . . . . 136 | . . . . . . . . . . . . . . . . . . . 137 | . . . . . . . . . . . . . . . . . . . 138 | . . . . . . . . . . . . . . . . . . . 139 | . . . . . . . . . . . . . . . . . . . 140 | "#; 141 | test_one(s, ai::heuristic, 3, vec![(4, 4)]); 142 | 143 | let s = r#"19 144 | W . . . . . . . . . . . . . . . . . . 145 | . W . . . . . . . . . . . . . . . . . 146 | . . W . . . . . . . . . . . . . . . . 147 | . . . . . . . . . . . . . . . . . . . 148 | . . . . . . . . . . . . . . . . . . . 149 | . . . . . . . . . . . . . . . . . . . 150 | . . . . . . . . . . . . . . . . . . . 151 | . . . . . . W . . . . . . . . . . . . 152 | . . . . . . . W . . . . . . . . . . . 153 | . . . . . . . . W . . . . . . . . . . 154 | . . . . . . . . . W . . . . . . . . . 155 | . . . . . . . . . . . . . . . . . . . 156 | . . . . . . . . . . . . . . . . . . . 157 | . . . . . . . . . . . . . . . . . . . 158 | . . . . . . . . . . . . . . . . . . . 159 | . . . . . . . . . . . . . . . . . . . 160 | . . . . . . . . . . . . . . . . . . . 161 | . . . . . . . . . . . . . . . . . . . 162 | . . . . . . . . . . . . . . . . . . . 163 | "#; 164 | test_one(s, ai::heuristic, 3, vec![(10, 11), (5, 6)]); 165 | 166 | let s = r#"19 167 | . . . . . . . . . . . . . . . . . . . 168 | . . . . . . . . . . . . . . . . . . . 169 | . . . . . . . . . . . . . . . . . . . 170 | . . . . . . B W . . . . . . . . . . . 171 | . . . . . B W W . . . . . . . . . . . 172 | . . . . . . W B . . . . . . . . . . . 173 | . . . . B W W W B . . B . . . . . . . 174 | . . . . W . B . W B . . . . . . . . . 175 | . . . B . . . . . B W . . . . . . . . 176 | . . . . . . . W . . . . . . . . . . . 177 | . . . . . . . . . . . . . . . . . . . 178 | . . . . . . . . . . . . . . . . . . . 179 | . . . . . . . . . . . . . . . . . . . 180 | . . . . . . . . . . . . . . . . . . . 181 | . . . . . . . . . . . . . . . . . . . 182 | . . . . . . . . . . . . . . . . . . . 183 | . . . . . . . . . . . . . . . . . . . 184 | . . . . . . . . . . . . . . . . . . . 185 | . . . . . . . . . . . . . . . . . . . 186 | "#; 187 | test_one(s, ai::heuristic, 3, vec![(8, 3)]); 188 | 189 | let s = r#"19 190 | . . . . . . . . . . . . . . . . . . . 191 | . . . . . . . . . . . . . . . . . . . 192 | . . . . . . . . . . . . . . . . . . . 193 | . . . . . . . . . . . . . . . . . . . 194 | . . . . . . . . . . . . . . . . . . . 195 | . . . . . . W . . . . . . . . . . . . 196 | . . . . . . . B . . . . . . . . . . . 197 | . . . . . . W . B . . . . . . . . . . 198 | . . . . . . . W . B . . . . . . . . . 199 | . . . . . . . . W B B . . . . . . . . 200 | . . . . . . . . . W . . . . . . . . . 201 | . . . . . . . . . . B . . . . . . . . 202 | . . . . . . . . . . . . . . . . . . . 203 | . . . . . . . . . . . . . . . . . . . 204 | . . . . . . . . . . . . . . . . . . . 205 | . . . . . . . . . . . . . . . . . . . 206 | . . . . . . . . . . . . . . . . . . . 207 | . . . . . . . . . . . . . . . . . . . 208 | . . . . . . . . . . . . . . . . . . . 209 | "#; 210 | test_one(s, ai::heuristic, 3, vec![(5, 6)]); 211 | 212 | let s = r#"19 213 | . . . . . . . . . . . . . . . . . . . 214 | . . . . . . . . . . . . . . . . . . . 215 | . . . . . . . . . . . . . . . . . . . 216 | . . . . . . . . . . . . . . . . . . . 217 | . . . . . . . . B . B . . . . . . . . 218 | . . . . . . . W B W . . . B . . . . . 219 | . . . . . . W . W . B . W B . . . . . 220 | . . . . . . . W W W W B . . . . . . . 221 | . . . . . . B . W . B . B . . . . . . 222 | . . . . . . . W B B . W . W . . . . . 223 | . . . . . . B . W . . . . . . . . . . 224 | . . . . . . . . . W . . . . . . . . . 225 | . . . . . . . . . . . . . . . . . . . 226 | . . . . . . . . . . . . . . . . . . . 227 | . . . . . . . . . . . . . . . . . . . 228 | . . . . . . . . . . . . . . . . . . . 229 | . . . . . . . . . . . . . . . . . . . 230 | . . . . . . . . . . . . . . . . . . . 231 | . . . . . . . . . . . . . . . . . . . 232 | "#; 233 | test_one(s, ai::heuristic, 3, vec![(6, 7)]); 234 | 235 | // let s = r#"19 236 | // . . . . . . . . . . . . . . . . . . . 237 | // . . . . . . . . . . . . . . . . . . . 238 | // . . . . . . . . . . . . . . . . . . . 239 | // . . . . . . . . . . . . . . . . . . . 240 | // . . . . . . . . . . . . . . . . . . . 241 | // . . . . . . . . . . . . . . . . . . . 242 | // . . . . . . . . . . . . . . . . . . . 243 | // . . . . . . . . . . W . . . . . . . . 244 | // . . . . . . . . B W W W . . . . . . . 245 | // . . . . . . . B W B . . . . . . . . . 246 | // . . . . . . B W B . . . . . . . . . . 247 | // . . . . . . B B . . . . . . . . . . . 248 | // . . . . . . W . . . . . . . . . . . . 249 | // . . . . . . . . . . . . . . . . . . . 250 | // . . . . . . . . . . . . . . . . . . . 251 | // . . . . . . . . . . . . . . . . . . . 252 | // . . . . . . . . . . . . . . . . . . . 253 | // . . . . . . . . . . . . . . . . . . . 254 | // . . . . . . . . . . . . . . . . . . . 255 | // "#; 256 | // test_one(s, ai::heuristic, 3, vec![(9, 7), (5, 11)]); 257 | 258 | // let s = r#"19 259 | // . . . . . . . . . . . . . . . . . . . 260 | // . . . . . . . . . . . . . . . . . . . 261 | // . . . . . . . . . . . . . . . . . . . 262 | // . . . . . . . . . . . . . . . . . . . 263 | // . . . . . . . . . . . . . . . . . . . 264 | // . . . . . . . . W . . . . . . . . . . 265 | // . . . . . . W . W . . . . . . . . . . 266 | // . . . . . . . B W B B . . . . . . . . 267 | // . . . . . . B . B W W W . . . . . . . 268 | // . . . . . . . B W B . . . . . . . . . 269 | // . . . . . . . W B . . . . . . . . . . 270 | // . . . . . W B B . . . . . . . . . . . 271 | // . . . . . . . . . . . . . . . . . . . 272 | // . . . . . . . . . . . . . . . . . . . 273 | // . . . . . . . . . . . . . . . . . . . 274 | // . . . . . . . . . . . . . . . . . . . 275 | // . . . . . . . . . . . . . . . . . . . 276 | // . . . . . . . . . . . . . . . . . . . 277 | // . . . . . . . . . . . . . . . . . . . 278 | // "#; 279 | // test_one(s, ai::heuristic, 3, vec![(5, 7), (9, 11)]); 280 | } 281 | -------------------------------------------------------------------------------- /src/board/test_capture.rs: -------------------------------------------------------------------------------- 1 | use board::{GoBoard, Tile, Team}; 2 | 3 | fn test_one(s: &str, x: usize, y: usize, nb_capture: u32) -> GoBoard { 4 | 5 | let mut board = GoBoard::parse_with_size(&s.to_string()); 6 | let (mut team_b, mut team_w) = Team::new_teams(); 7 | println!("Test\n{}", board); 8 | board.set((x, y), &mut team_w); 9 | println!("expected {} found {}", nb_capture, team_w.captured()); 10 | assert!(team_w.captured() == nb_capture); 11 | board 12 | } 13 | 14 | #[test] 15 | fn test_capture() { 16 | let s = r#"19 17 | . . . . . . . . . . . . . . . . . . . 18 | . . . . . . . . . . . . . . . . . . . 19 | . . . . . . . . . . . . . . . . . . . 20 | . . . . . . . . . . . . . . . . . . . 21 | . . . . . . . . . . . . . . . . . . . 22 | . . . . . . . . . . . . . . . . . . . 23 | . . . . . . . . . . . . . . . . . . . 24 | . . . . . . . . . . . . . . . . . . . 25 | . . . . . . . . . . . . . . . . . . . 26 | . . . . . . . . . . . . . . . . . . . 27 | . . . . . . . . . . . . . . . . . . . 28 | . . . . . . . . . . . . . . . . . . . 29 | . . . . . . . . . . . . . . . . . . . 30 | . . . . . . . . . . . . . . . . . . . 31 | . . . . . . . . . . . . . . . . . . . 32 | . . . . . . . . . . . . . . . . . . . 33 | . . . . . . . . . . . . . . . . . . . 34 | . . . . . . . . . . . . . . . . . . . 35 | . . . . . . . . . . . . . . . . . . . 36 | "#; 37 | test_one(s, 4, 4, 0); 38 | 39 | let s = r#"19 40 | . . . . . . . . . . . . . . . . . . . 41 | . . . . . . . . . . . . . . . . . . . 42 | . . . . . . . . . . . . . . . . . . . 43 | . . . . . B B . . . . . . . . . . . . 44 | . . . . . . . . . . . . . . . . . . . 45 | . . . . . . . . . . . . . . . . . . . 46 | . . . . . . . . . . . . . . . . . . . 47 | . . . . . . . . . . . . . . . . . . . 48 | . . . . . . . . . . . . . . . . . . . 49 | . . . . . . . . . . . . . . . . . . . 50 | . . . . . . . . . . . . . . . . . . . 51 | . . . . . . . . . . . . . . . . . . . 52 | . . . . . . . . . . . . . . . . . . . 53 | . . . . . . . . . . . . . . . . . . . 54 | . . . . . . . . . . . . . . . . . . . 55 | . . . . . . . . . . . . . . . . . . . 56 | . . . . . . . . . . . . . . . . . . . 57 | . . . . . . . . . . . . . . . . . . . 58 | . . . . . . . . . . . . . . . . . . . 59 | "#; 60 | test_one(s, 4, 4, 0); 61 | 62 | let s = r#"19 63 | . . . . . . . . . . . . . . . . . . . 64 | . . . . . . . . . . . . . . . . . . . 65 | . . . . . . . . . . . . . . . . . . . 66 | . . . . . . . . . . . . . . . . . . . 67 | . . . . . B B W . . . . . . . . . . . 68 | . . . . . . . . . . . . . . . . . . . 69 | . . . . . . . . . . . . . . . . . . . 70 | . . . . . . . . . . . . . . . . . . . 71 | . . . . . . . . . . . . . . . . . . . 72 | . . . . . . . . . . . . . . . . . . . 73 | . . . . . . . . . . . . . . . . . . . 74 | . . . . . . . . . . . . . . . . . . . 75 | . . . . . . . . . . . . . . . . . . . 76 | . . . . . . . . . . . . . . . . . . . 77 | . . . . . . . . . . . . . . . . . . . 78 | . . . . . . . . . . . . . . . . . . . 79 | . . . . . . . . . . . . . . . . . . . 80 | . . . . . . . . . . . . . . . . . . . 81 | . . . . . . . . . . . . . . . . . . . 82 | "#; 83 | test_one(s, 4, 4, 2); 84 | 85 | let s = r#"19 86 | . . . . . . . . . . . . . . . . . . . 87 | . . . . . . . . . . . . . . . . . . . 88 | . . . . . . . . . . . . . . . . . . . 89 | . . . . . . . . . . . . . . . . . . . 90 | . W B B . . . . . . . . . . . . . . . 91 | . . . . . . . . . . . . . . . . . . . 92 | . . . . . . . . . . . . . . . . . . . 93 | . . . . . . . . . . . . . . . . . . . 94 | . . . . . . . . . . . . . . . . . . . 95 | . . . . . . . . . . . . . . . . . . . 96 | . . . . . . . . . . . . . . . . . . . 97 | . . . . . . . . . . . . . . . . . . . 98 | . . . . . . . . . . . . . . . . . . . 99 | . . . . . . . . . . . . . . . . . . . 100 | . . . . . . . . . . . . . . . . . . . 101 | . . . . . . . . . . . . . . . . . . . 102 | . . . . . . . . . . . . . . . . . . . 103 | . . . . . . . . . . . . . . . . . . . 104 | . . . . . . . . . . . . . . . . . . . 105 | "#; 106 | test_one(s, 4, 4, 2); 107 | 108 | let s = r#"19 109 | . . . . . . . . . . . . . . . . . . . 110 | . W B B . . . . . . . . . . . . . . . 111 | . . . . . . . . . . . . . . . . . . . 112 | . . . . . . . . . . . . . . . . . . . 113 | . . . . . . . . . . . . . . . . . . . 114 | . . . . . . . . . . . . . . . . . . . 115 | . . . . . . . . . . . . . . . . . . . 116 | . . . . . . . . . . . . . . . . . . . 117 | . . . . . . . . . . . . . . . . . . . 118 | . . . . . . . . . . . . . . . . . . . 119 | . . . . . . . . . . . . . . . . . . . 120 | . . . . . . . . . . . . . . . . . . . 121 | . . . . . . . . . . . . . . . . . . . 122 | . . . . . . . . . . . . . . . . . . . 123 | . . . . . . . . . . . . . . . . . . . 124 | . . . . . . . . . . . . . . . . . . . 125 | . . . . . . . . . . . . . . . . . . . 126 | . . . . . . . . . . . . . . . . . . . 127 | . . . . . . . . . . . . . . . . . . . 128 | "#; 129 | test_one(s, 4, 1, 2); 130 | 131 | } 132 | 133 | fn test_one_diff(s: &str, 134 | s_expected: &str, 135 | x: usize, 136 | y: usize) { 137 | let mut board = GoBoard::parse_with_size(&s.to_string()); 138 | let (mut team_b, mut team_w) = Team::new_teams(); 139 | println!("Test\n{}", board); 140 | board.set((x, y), &mut team_w); 141 | let mut expected = GoBoard::parse_with_size(&s_expected.to_string()); 142 | println!("{}", board); 143 | assert!(board == expected); 144 | } 145 | 146 | #[test] 147 | fn test_capture_delete_tile() { 148 | let s = r#"19 149 | . . . . . . . . . . . . . . . . . . . 150 | . W . . W . . W . . . . . . . . . . . 151 | . . B . B . B . . . . . . . . . . . . 152 | . . . B B B . . . . . . . . . . . . . 153 | . W B B . B B W . . . . . . . . . . . 154 | . . . B B B . . . . . . . . . . . . . 155 | . . B . B . B . . . . . . . . . . . . 156 | . W . . W . . W . . . . . . . . . . . 157 | . . . . . . . . . . . . . . . . . . . 158 | . . . . . . . . . . . . . . . . . . . 159 | . . . . . . . . . . . . . . . . . . . 160 | . . . . . . . . . . . . . . . . . . . 161 | . . . . . . . . . . . . . . . . . . . 162 | . . . . . . . . . . . . . . . . . . . 163 | . . . . . . . . . . . . . . . . . . . 164 | . . . . . . . . . . . . . . . . . . . 165 | . . . . . . . . . . . . . . . . . . . 166 | . . . . . . . . . . . . . . . . . . . 167 | . . . . . . . . . . . . . . . . . . . 168 | "#; 169 | let s_exp = r#"19 170 | . . . . . . . . . . . . . . . . . . . 171 | . W . . W . . W . . . . . . . . . . . 172 | . . . . . . . . . . . . . . . . . . . 173 | . . . . . . . . . . . . . . . . . . . 174 | . W . . W . . W . . . . . . . . . . . 175 | . . . . . . . . . . . . . . . . . . . 176 | . . . . . . . . . . . . . . . . . . . 177 | . W . . W . . W . . . . . . . . . . . 178 | . . . . . . . . . . . . . . . . . . . 179 | . . . . . . . . . . . . . . . . . . . 180 | . . . . . . . . . . . . . . . . . . . 181 | . . . . . . . . . . . . . . . . . . . 182 | . . . . . . . . . . . . . . . . . . . 183 | . . . . . . . . . . . . . . . . . . . 184 | . . . . . . . . . . . . . . . . . . . 185 | . . . . . . . . . . . . . . . . . . . 186 | . . . . . . . . . . . . . . . . . . . 187 | . . . . . . . . . . . . . . . . . . . 188 | . . . . . . . . . . . . . . . . . . . 189 | "#; 190 | let board = test_one_diff(s, s_exp, 4, 4); 191 | 192 | let s = r#"19 193 | . . . . . . . . . . . . . . . . . . . 194 | . . . . . . . W . . . . . . . . . . . 195 | . . . . . . B . . . . . . . . . . . . 196 | . . . . . B . . . . . . . . . . . . . 197 | . . . . . . . . . . . . . . . . . . . 198 | . . . . . . . . . . . . . . . . . . . 199 | . . . . . . . . . . . . . . . . . . . 200 | . . . . . . . . . . . . . . . . . . . 201 | . . . . . . . . . . . . . . . . . . . 202 | . . . . . . . . . . . . . . . . . . . 203 | . . . . . . . . . . . . . . . . . . . 204 | . . . . . . . . . . . . . . . . . . . 205 | . . . . . . . . . . . . . . . . . . . 206 | . . . . . . . . . . . . . . . . . . . 207 | . . . . . . . . . . . . . . . . . . . 208 | . . . . . . . . . . . . . . . . . . . 209 | . . . . . . . . . . . . . . . . . . . 210 | . . . . . . . . . . . . . . . . . . . 211 | . . . . . . . . . . . . . . . . . . . 212 | "#; 213 | let s_exp = r#"19 214 | . . . . . . . . . . . . . . . . . . . 215 | . . . . . . . W . . . . . . . . . . . 216 | . . . . . . . . . . . . . . . . . . . 217 | . . . . . . . . . . . . . . . . . . . 218 | . . . . W . . . . . . . . . . . . . . 219 | . . . . . . . . . . . . . . . . . . . 220 | . . . . . . . . . . . . . . . . . . . 221 | . . . . . . . . . . . . . . . . . . . 222 | . . . . . . . . . . . . . . . . . . . 223 | . . . . . . . . . . . . . . . . . . . 224 | . . . . . . . . . . . . . . . . . . . 225 | . . . . . . . . . . . . . . . . . . . 226 | . . . . . . . . . . . . . . . . . . . 227 | . . . . . . . . . . . . . . . . . . . 228 | . . . . . . . . . . . . . . . . . . . 229 | . . . . . . . . . . . . . . . . . . . 230 | . . . . . . . . . . . . . . . . . . . 231 | . . . . . . . . . . . . . . . . . . . 232 | . . . . . . . . . . . . . . . . . . . 233 | "#; 234 | let board = test_one_diff(s, s_exp, 4, 4); 235 | 236 | let s = r#"19 237 | . . . . . . . . . . . . . . . . . . . 238 | . . . . . . . . . . . . . . . . . . . 239 | . . . . . . . . . . . . . . . . . . . 240 | . . . . . . . . . . . . . . . . . . . 241 | . . . . . B B W . . . . . . . . . . . 242 | . . . . . . . . . . . . . . . . . . . 243 | . . . . . . . . . . . . . . . . . . . 244 | . . . . . . . . . . . . . . . . . . . 245 | . . . . . . . . . . . . . . . . . . . 246 | . . . . . . . . . . . . . . . . . . . 247 | . . . . . . . . . . . . . . . . . . . 248 | . . . . . . . . . . . . . . . . . . . 249 | . . . . . . . . . . . . . . . . . . . 250 | . . . . . . . . . . . . . . . . . . . 251 | . . . . . . . . . . . . . . . . . . . 252 | . . . . . . . . . . . . . . . . . . . 253 | . . . . . . . . . . . . . . . . . . . 254 | . . . . . . . . . . . . . . . . . . . 255 | . . . . . . . . . . . . . . . . . . . 256 | "#; 257 | let s_exp = r#"19 258 | . . . . . . . . . . . . . . . . . . . 259 | . . . . . . . . . . . . . . . . . . . 260 | . . . . . . . . . . . . . . . . . . . 261 | . . . . . . . . . . . . . . . . . . . 262 | . . . . W . . W . . . . . . . . . . . 263 | . . . . . . . . . . . . . . . . . . . 264 | . . . . . . . . . . . . . . . . . . . 265 | . . . . . . . . . . . . . . . . . . . 266 | . . . . . . . . . . . . . . . . . . . 267 | . . . . . . . . . . . . . . . . . . . 268 | . . . . . . . . . . . . . . . . . . . 269 | . . . . . . . . . . . . . . . . . . . 270 | . . . . . . . . . . . . . . . . . . . 271 | . . . . . . . . . . . . . . . . . . . 272 | . . . . . . . . . . . . . . . . . . . 273 | . . . . . . . . . . . . . . . . . . . 274 | . . . . . . . . . . . . . . . . . . . 275 | . . . . . . . . . . . . . . . . . . . 276 | . . . . . . . . . . . . . . . . . . . 277 | "#; 278 | let board = test_one_diff(s, s_exp, 4, 4); 279 | } 280 | -------------------------------------------------------------------------------- /src/board/test_free_threes.rs: -------------------------------------------------------------------------------- 1 | use board::{GoBoard, Tile, Team}; 2 | 3 | fn test_one(s: &str, x: usize, y: usize, is_playable: bool) { 4 | let board = GoBoard::parse_with_size(&s.to_string()); 5 | let (team_b, team_w) = Team::new_teams(); 6 | println!("Test\n{}", board); 7 | assert!(board.is_allow(x, y, &team_w) == is_playable); 8 | } 9 | 10 | #[test] 11 | fn test_free_threes() { 12 | // this one is allow because [0, 0] is near one extremities of the board 13 | let s = r#"19 14 | W . . . . . . . . . . . . . . . . . . 15 | . W . . . . . . . . . . . . . . . . . 16 | . . . . . . . . . . . . . . . . . . . 17 | . . . . W W . . . . . . . . . . . . . 18 | . . . . . . . . . . . . . . . . . . . 19 | . . . . . . . . . . . . . . . . . . . 20 | . . . . . . . . . . . . . . . . . . . 21 | . . . . . . . . . . . . . . . . . . . 22 | . . . . . . . . . . . . . . . . . . . 23 | . . . . . . . . . . . . . . . . . . . 24 | . . . . . . . . . . . . . . . . . . . 25 | . . . . . . . . . . . . . . . . . . . 26 | . . . . . . . . . . . . . . . . . . . 27 | . . . . . . . . . . . . . . . . . . . 28 | . . . . . . . . . . . . . . . . . . . 29 | . . . . . . . . . . . . . . . . . . . 30 | . . . . . . . . . . . . . . . . . . . 31 | . . . . . . . . . . . . . . . . . . . 32 | . . . . . . . . . . . . . . . . . . . 33 | "#; 34 | test_one(s, 3, 3, true); 35 | let s = r#"19 36 | W . . . . . . . . . . . . . . . . . . 37 | . W . . . . . . . . . . . . . . . . . 38 | . . . . . . . . . . . . . . . . . . . 39 | . . B . W W . . . . . . . . . . . . . 40 | . . . . . . . . . . . . . . . . . . . 41 | . . . . . . . . . . . . . . . . . . . 42 | . . . . . . . . . . . . . . . . . . . 43 | . . . . . . . . . . . . . . . . . . . 44 | . . . . . . . . . . . . . . . . . . . 45 | . . . . . . . . . . . . . . . . . . . 46 | . . . . . . . . . . . . . . . . . . . 47 | . . . . . . . . . . . . . . . . . . . 48 | . . . . . . . . . . . . . . . . . . . 49 | . . . . . . . . . . . . . . . . . . . 50 | . . . . . . . . . . . . . . . . . . . 51 | . . . . . . . . . . . . . . . . . . . 52 | . . . . . . . . . . . . . . . . . . . 53 | . . . . . . . . . . . . . . . . . . . 54 | . . . . . . . . . . . . . . . . . . . 55 | "#; 56 | test_one(s, 3, 3, true); 57 | //can't be played because there is already a pawn on the case. 58 | let s = r#"19 59 | . . . . . . . . . . . . . . . . . . . 60 | . W . . . . . . . . . . . . . . . . . 61 | . . W . . . . . . . . . . . . . . . . 62 | . . . . . . . . . . . . . . . . . . . 63 | . . . B . W W . . . . . . . . . . . . 64 | . . . . . . . . . . . . . . . . . . . 65 | . . . . . . . . . . . . . . . . . . . 66 | . . . . . . . . . . . . . . . . . . . 67 | . . . . . . . . . . . . . . . . . . . 68 | . . . . . . . . . . . . . . . . . . . 69 | . . . . . . . . . . . . . . . . . . . 70 | . . . . . . . . . . . . . . . . . . . 71 | . . . . . . . . . . . . . . . . . . . 72 | . . . . . . . . . . . . . . . . . . . 73 | . . . . . . . . . . . . . . . . . . . 74 | . . . . . . . . . . . . . . . . . . . 75 | . . . . . . . . . . . . . . . . . . . 76 | . . . . . . . . . . . . . . . . . . . 77 | . . . . . . . . . . . . . . . . . . . 78 | "#; 79 | test_one(s, 3, 4, false); 80 | let s = r#"19 81 | . . . . . . . . . . . . . . . . . . . 82 | . W . . . . . . . . . . . . . . . . . 83 | . . W . . . . . . . . . . . . . . . . 84 | . . . . . . . . . . . . . . . . . . . 85 | . . . . . W W . . . . . . . . . . . . 86 | . . . . . . . . . . . . . . . . . . . 87 | . . . . . . . . . . . . . . . . . . . 88 | . . . . . . . . . . . . . . . . . . . 89 | . . . . . . . . . . . . . . . . . . . 90 | . . . . . . . . . . . . . . . . . . . 91 | . . . . . . . . . . . . . . . . . . . 92 | . . . . . . . . . . . . . . . . . . . 93 | . . . . . . . . . . . . . . . . . . . 94 | . . . . . . . . . . . . . . . . . . . 95 | . . . . . . . . . . . . . . . . . . . 96 | . . . . . . . . . . . . . . . . . . . 97 | . . . . . . . . . . . . . . . . . . . 98 | . . . . . . . . . . . . . . . . . . . 99 | . . . . . . . . . . . . . . . . . . . 100 | "#; 101 | test_one(s, 4, 4, false); 102 | let s = r#"19 103 | . . . . . . . . . . . . . . . . . . . 104 | . . . . . . . . . . . . . . . . . . . 105 | . . W . . . . . . . . . . . . . . . . 106 | . . . W . . . . . . . . . . . . . . . 107 | . . . . . W W . . . . . . . . . . . . 108 | . . . . . . . . . . . . . . . . . . . 109 | . . . . . . . . . . . . . . . . . . . 110 | . . . . . . . . . . . . . . . . . . . 111 | . . . . . . . . . . . . . . . . . . . 112 | . . . . . . . . . . . . . . . . . . . 113 | . . . . . . . . . . . . . . . . . . . 114 | . . . . . . . . . . . . . . . . . . . 115 | . . . . . . . . . . . . . . . . . . . 116 | . . . . . . . . . . . . . . . . . . . 117 | . . . . . . . . . . . . . . . . . . . 118 | . . . . . . . . . . . . . . . . . . . 119 | . . . . . . . . . . . . . . . . . . . 120 | . . . . . . . . . . . . . . . . . . . 121 | . . . . . . . . . . . . . . . . . . . 122 | "#; 123 | test_one(s, 4, 4, false); 124 | let s = r#"19 125 | . . . . . . . . . . . . . . . . . . . 126 | . W . . . . . . . . . . . . . . . . . 127 | . . W . . . . . . . . . . . . . . . . 128 | . . . W . . . . . . . . . . . . . . . 129 | . . . . . W W . . . . . . . . . . . . 130 | . . . . . . . . . . . . . . . . . . . 131 | . . . . . . . . . . . . . . . . . . . 132 | . . . . . . . . . . . . . . . . . . . 133 | . . . . . . . . . . . . . . . . . . . 134 | . . . . . . . . . . . . . . . . . . . 135 | . . . . . . . . . . . . . . . . . . . 136 | . . . . . . . . . . . . . . . . . . . 137 | . . . . . . . . . . . . . . . . . . . 138 | . . . . . . . . . . . . . . . . . . . 139 | . . . . . . . . . . . . . . . . . . . 140 | . . . . . . . . . . . . . . . . . . . 141 | . . . . . . . . . . . . . . . . . . . 142 | . . . . . . . . . . . . . . . . . . . 143 | . . . . . . . . . . . . . . . . . . . 144 | "#; 145 | test_one(s, 4, 4, true); 146 | } 147 | 148 | #[test] 149 | fn test_free_threes_rule1() { 150 | // give four free threes 151 | let s = r#"19 152 | . . . . . . . . . . . . . . . . . . . 153 | . . . . . . . . . . . . . . . . . . . 154 | . . . . . . . . . . . . . . . . . . . 155 | . . . W W W . . . . . . . . . . . . . 156 | . . . W . W . . . . . . . . . . . . . 157 | . . . W W W . . . . . . . . . . . . . 158 | . . . . . . . . . . . . . . . . . . . 159 | . . . . . . . . . . . . . . . . . . . 160 | . . . . . . . . . . . . . . . . . . . 161 | . . . . . . . . . . . . . . . . . . . 162 | . . . . . . . . . . . . . . . . . . . 163 | . . . . . . . . . . . . . . . . . . . 164 | . . . . . . . . . . . . . . . . . . . 165 | . . . . . . . . . . . . . . . . . . . 166 | . . . . . . . . . . . . . . . . . . . 167 | . . . . . . . . . . . . . . . . . . . 168 | . . . . . . . . . . . . . . . . . . . 169 | . . . . . . . . . . . . . . . . . . . 170 | . . . . . . . . . . . . . . . . . . . 171 | "#; 172 | test_one(s, 4, 4, false); 173 | } 174 | 175 | #[test] 176 | fn test_free_threes_rule2() { 177 | // give two free threes 178 | let s = r#"19 179 | . . . . . . . . . . . . . . . . . . . 180 | . . . . . . . . . . . . . . . . . . . 181 | . . . . W . . . . . . . . . . . . . . 182 | . . . . W . . . . . . . . . . . . . . 183 | . . W W . . . . . . . . . . . . . . . 184 | . . . . . . . . . . . . . . . . . . . 185 | . . . . . . . . . . . . . . . . . . . 186 | . . . . . . . . . . . . . . . . . . . 187 | . . . . . . . . . . . . . . . . . . . 188 | . . . . . . . . . . . . . . . . . . . 189 | . . . . . . . . . . . . . . . . . . . 190 | . . . . . . . . . . . . . . . . . . . 191 | . . . . . . . . . . . . . . . . . . . 192 | . . . . . . . . . . . . . . . . . . . 193 | . . . . . . . . . . . . . . . . . . . 194 | . . . . . . . . . . . . . . . . . . . 195 | . . . . . . . . . . . . . . . . . . . 196 | . . . . . . . . . . . . . . . . . . . 197 | . . . . . . . . . . . . . . . . . . . 198 | "#; 199 | test_one(s, 4, 4, false); 200 | let s = r#"19 201 | . . . . . . . . . . . . . . . . . . . 202 | . . . . . . . . . . . . . . . . . . . 203 | . . . . . . W . . . . . . . . . . . . 204 | . . . . . W . . . . . . . . . . . . . 205 | . . . . . W W . . . . . . . . . . . . 206 | . . . . . . . . . . . . . . . . . . . 207 | . . . . . . . . . . . . . . . . . . . 208 | . . . . . . . . . . . . . . . . . . . 209 | . . . . . . . . . . . . . . . . . . . 210 | . . . . . . . . . . . . . . . . . . . 211 | . . . . . . . . . . . . . . . . . . . 212 | . . . . . . . . . . . . . . . . . . . 213 | . . . . . . . . . . . . . . . . . . . 214 | . . . . . . . . . . . . . . . . . . . 215 | . . . . . . . . . . . . . . . . . . . 216 | . . . . . . . . . . . . . . . . . . . 217 | . . . . . . . . . . . . . . . . . . . 218 | . . . . . . . . . . . . . . . . . . . 219 | . . . . . . . . . . . . . . . . . . . 220 | "#; 221 | test_one(s, 4, 4, false); 222 | let s = r#"19 223 | . . . . . . . . . . . . . . . . . . . 224 | . W . . . . . . . . . . . . . . . . . 225 | . . W . W . W . . . . . . . . . . . . 226 | . . . W W W . . . . . . . . . . . . . 227 | . W W W . W W . . . . . . . . . . . . 228 | . . W W W W . . . . . . . . . . . . . 229 | . W . . W . W . . . . . . . . . . . . 230 | . . . . . . . . . . . . . . . . . . . 231 | . . . . . . . . . . . . . . . . . . . 232 | . . . . . . . . . . . . . . . . . . . 233 | . . . . . . . . . . . . . . . . . . . 234 | . . . . . . . . . . . . . . . . . . . 235 | . . . . . . . . . . . . . . . . . . . 236 | . . . . . . . . . . . . . . . . . . . 237 | . . . . . . . . . . . . . . . . . . . 238 | . . . . . . . . . . . . . . . . . . . 239 | . . . . . . . . . . . . . . . . . . . 240 | . . . . . . . . . . . . . . . . . . . 241 | . . . . . . . . . . . . . . . . . . . 242 | "#; 243 | test_one(s, 4, 4, true); 244 | // assert!(false); 245 | } 246 | 247 | #[test] 248 | fn test_free_threes_rule3() { 249 | // give two free threes 250 | let s = r#"19 251 | . . . . . . . . . . . . . . . . . . . 252 | . . . . W . . . . . . . . . . . . . . 253 | . . . . W . . . . . . . . . . . . . . 254 | . . . . . . . . . . . . . . . . . . . 255 | . W W . . . . . . . . . . . . . . . . 256 | . . . . . . . . . . . . . . . . . . . 257 | . . . . . . . . . . . . . . . . . . . 258 | . . . . . . . . . . . . . . . . . . . 259 | . . . . . . . . . . . . . . . . . . . 260 | . . . . . . . . . . . . . . . . . . . 261 | . . . . . . . . . . . . . . . . . . . 262 | . . . . . . . . . . . . . . . . . . . 263 | . . . . . . . . . . . . . . . . . . . 264 | . . . . . . . . . . . . . . . . . . . 265 | . . . . . . . . . . . . . . . . . . . 266 | . . . . . . . . . . . . . . . . . . . 267 | . . . . . . . . . . . . . . . . . . . 268 | . . . . . . . . . . . . . . . . . . . 269 | . . . . . . . . . . . . . . . . . . . 270 | "#; 271 | test_one(s, 4, 4, false); 272 | let s = r#"19 273 | . . . . . . . . . . . . . . . . . . . 274 | . . . . . . . W . . . . . . . . . . . 275 | . . . . . . W . . . . . . . . . . . . 276 | . . . . . . . . . . . . . . . . . . . 277 | . . . . . . W W . . . . . . . . . . . 278 | . . . . . . . . . . . . . . . . . . . 279 | . . . . . . . . . . . . . . . . . . . 280 | . . . . . . . . . . . . . . . . . . . 281 | . . . . . . . . . . . . . . . . . . . 282 | . . . . . . . . . . . . . . . . . . . 283 | . . . . . . . . . . . . . . . . . . . 284 | . . . . . . . . . . . . . . . . . . . 285 | . . . . . . . . . . . . . . . . . . . 286 | . . . . . . . . . . . . . . . . . . . 287 | . . . . . . . . . . . . . . . . . . . 288 | . . . . . . . . . . . . . . . . . . . 289 | . . . . . . . . . . . . . . . . . . . 290 | . . . . . . . . . . . . . . . . . . . 291 | . . . . . . . . . . . . . . . . . . . 292 | "#; 293 | test_one(s, 4, 4, false); 294 | } 295 | -------------------------------------------------------------------------------- /src/display/console.rs: -------------------------------------------------------------------------------- 1 | extern crate std; 2 | extern crate piston; 3 | extern crate opengl_graphics; 4 | extern crate graphics; 5 | #[cfg(feature = "include_sdl2")] 6 | extern crate sdl2_window; 7 | #[cfg(feature = "include_glfw")] 8 | extern crate glfw_window; 9 | #[cfg(feature = "include_glutin")] 10 | extern crate glutin_window; 11 | 12 | #[cfg(feature = "include_sdl2")] 13 | use self::sdl2_window::Sdl2Window as Window; 14 | #[cfg(feature = "include_glfw")] 15 | use self::glfw_window::GlfwWindow as Window; 16 | #[cfg(feature = "include_glutin")] 17 | use self::glutin_window::GlutinWindow as Window; 18 | 19 | use self::opengl_graphics::GlGraphics; 20 | 21 | use self::piston::input::*; 22 | use self::piston::event_loop::*; 23 | 24 | use display::mouse::Mouse; 25 | use display::draw; 26 | 27 | use board::GoBoard; 28 | use board::Team; 29 | use board::Tile; 30 | 31 | use ai::Decision; 32 | use ai::heuristic; 33 | 34 | const CASE_WIDTH: graphics::types::Resolution = 40; 35 | const ORANGE: graphics::types::Color = [0.97647065f32, 0.9450981f32, 0.854902f32, 1f32]; 36 | 37 | #[derive(Debug, PartialEq, Clone)] 38 | pub enum Player { 39 | Human, 40 | Ai, 41 | } 42 | 43 | impl Player { 44 | pub fn from_str(s: &str) -> Player { 45 | match s { 46 | "ai" => Player::Ai, 47 | "human" => Player::Human, 48 | _ => panic!("Player cli option must be either ai, solo or multi") 49 | } 50 | } 51 | } 52 | 53 | #[derive(Debug, Clone)] 54 | pub struct Console { 55 | board: GoBoard, 56 | event: Mouse, 57 | player: (Team, Player), 58 | friend: (Team, Player), 59 | layer: u32, 60 | turn: u32, // Player one = true, player two = false. 61 | win: bool, 62 | help: bool, 63 | help_decision: (u32, u32), 64 | info: bool, 65 | debug_map: bool, 66 | } 67 | 68 | impl Console { 69 | 70 | /// The `new` constructor function returns the interface console. 71 | 72 | pub fn new ( 73 | board: GoBoard, 74 | layer: u32, 75 | (player, friend): (Player, Player), 76 | info: bool, 77 | debug_map: bool, 78 | help: bool, 79 | ) -> Self { 80 | let size: u32 = board.get_size() as u32; 81 | let (team_player, team_friend) = Team::new_teams(); 82 | 83 | Console { 84 | board: board, 85 | event: Mouse::new((CASE_WIDTH * size, CASE_WIDTH * size)), 86 | player: (team_player, player), 87 | friend: (team_friend, friend), 88 | turn: 0, 89 | layer: layer, 90 | win: false, 91 | help: help, 92 | help_decision: (size/2, size/2), 93 | info: info, 94 | debug_map: debug_map, 95 | } 96 | } 97 | 98 | /// The `get_size` function returns window size. 99 | 100 | fn get_size ( 101 | &self 102 | ) -> piston::window::Size { 103 | let size: graphics::types::Resolution = self.board.get_size ( 104 | ) as graphics::types::Resolution; 105 | let dimension = self.event.get_dimension(); 106 | 107 | piston::window::Size::from([ 108 | dimension.0 / size, 109 | dimension.1 / size, 110 | ]) 111 | } 112 | 113 | /// The `get_turn_is_ai` function returns a boolean if a ai must play. 114 | 115 | fn get_turn_is_ai (&self) -> bool { 116 | match (self.turn % 2 != 0, &self.player, &self.friend) { 117 | (false, _, &(_, Player::Ai)) => true, 118 | (true, &(_, Player::Ai), _) => true, 119 | _ => false, 120 | } 121 | } 122 | 123 | /// The `set` function updates the turn and set the human coordinate. 124 | 125 | fn set (&mut self, event: &Event, team: &mut Team) -> (u32, u32) { 126 | let (x, y) = self.event.get_coordinate(); 127 | 128 | if x < self.board.get_size() as u32 && y < self.board.get_size() as u32 { 129 | if let Some(Button::Mouse(_)) = event.press_args() { 130 | if self.board.set((x as usize, y as usize), team) { 131 | self.turn += 1; 132 | if self.help && self.get_turn_is_ai() == false { 133 | self.help_decision = self.help_optimal_move(); 134 | } 135 | if self.info { 136 | println!("###{} human - {}: {} captured, ({}, {}) played.", 137 | self.turn, team.get_ennemy_tile(), team.captured(), x, y); 138 | } 139 | if self.debug_map { 140 | println!("{}", self.board); 141 | } 142 | } 143 | }; 144 | (x, y) 145 | } 146 | else { 147 | (0, 0) 148 | } 149 | } 150 | 151 | /// The `set_raw` function updates the turn and set the ai coordinate. 152 | 153 | fn set_raw (&mut self, (x, y): (usize, usize), team: &mut Team) -> (u32, u32) { 154 | self.board.set((x, y), team); 155 | self.turn += 1; 156 | if self.help && self.get_turn_is_ai() == false { 157 | self.help_decision = self.help_optimal_move(); 158 | } 159 | if self.info { 160 | println!("###{} ai - {}: {} captured, ({}, {}) played.", 161 | self.turn, team.get_ennemy_tile(), team.captured(), x, y); 162 | } 163 | if self.debug_map { 164 | println!("{}", self.board); 165 | } 166 | (x as u32, y as u32) 167 | } 168 | 169 | /// The `is_ai_versus` function returns a boolean if the player one 170 | /// and two are typed like ai. 171 | 172 | fn is_ai_versus (&self) -> bool { 173 | match (&self.player, &self.friend) { 174 | (&(_, Player::Ai), &(_, Player::Ai)) => true, 175 | _ => false, 176 | } 177 | } 178 | 179 | /// The `play` function sets the board with the new tail coordinate. 180 | 181 | fn play (&mut self, event: &Event) -> Option { 182 | let (x, y):(u32, u32) = match ( 183 | self.turn % 2 != 0, 184 | &mut self.player, 185 | &mut self.friend 186 | ) { 187 | (true, &mut (mut player_team, Player::Ai), &mut (friend_team, _)) => { 188 | let decision = Decision::get_optimal_move(&mut self.board, &(player_team, friend_team), friend_team, self.layer, heuristic); 189 | let result = self.set_raw(decision.get_result(), &mut player_team); 190 | 191 | self.player.0 = player_team; 192 | decision.print_result(); 193 | result 194 | }, 195 | (false, &mut (player_team, _), &mut (mut friend_team, Player::Ai)) => { 196 | let decision = Decision::get_optimal_move(&mut self.board, &(player_team, friend_team), player_team, self.layer, heuristic); 197 | let result = self.set_raw(decision.get_result(), &mut friend_team); 198 | 199 | self.friend.0 = friend_team; 200 | decision.print_result(); 201 | result 202 | }, 203 | (true, &mut (mut player_team, Player::Human), &mut (_, _)) => { 204 | let result = self.set(event, &mut player_team); 205 | 206 | self.player.0 = player_team; 207 | result 208 | }, 209 | (false, &mut (_, _), &mut (mut friend_team, Player::Human)) => { 210 | let result = self.set(event, &mut friend_team); 211 | 212 | self.friend.0 = friend_team; 213 | result 214 | }, 215 | }; 216 | self.board.is_win(x as usize, y as usize) 217 | } 218 | 219 | /// The `help_optimal_move` function returns the recommended coordinate to play. 220 | 221 | fn help_optimal_move (&mut self) -> (u32, u32) { 222 | let (x, y) = if self.turn % 2 != 0 { 223 | Decision::get_optimal_move ( 224 | &mut self.board, 225 | &(self.player.0, self.friend.0), 226 | self.player.0, 227 | self.layer, 228 | heuristic 229 | ).get_result() 230 | } 231 | else { 232 | Decision::get_optimal_move ( 233 | &mut self.board, 234 | &(self.player.0, self.friend.0), 235 | self.friend.0, 236 | self.layer, 237 | heuristic 238 | ).get_result() 239 | }; 240 | (x as u32, y as u32) 241 | } 242 | 243 | /// The `input` function listens all mouse event like resize and click. 244 | 245 | fn input ( 246 | &mut self, 247 | event: &Event, 248 | limit: u32 249 | ) { 250 | 251 | if let Some(resize) = event.resize(|w, h| (w as u32, h as u32)) { 252 | self.event.set_dimension(resize); 253 | } 254 | if self.win == false { 255 | 256 | if let Some(coordinate) = event.mouse_cursor(|x, y| { 257 | (x as u32, y as u32) 258 | }) { 259 | if let Some(coordinate) = self.event.check_inside_window ( 260 | coordinate, 261 | limit, 262 | ) { 263 | self.event.set_coordinate(coordinate); 264 | } 265 | 266 | } 267 | if let Some(team) = self.play(&event) { 268 | println!("{} win! Give him a cookie !", team); 269 | self.win = true; 270 | } 271 | } 272 | } 273 | 274 | /// The `draw` function refreshs the window with a new board. 275 | 276 | fn draw ( 277 | &mut self, 278 | gl: &mut GlGraphics, 279 | event: &RenderArgs, 280 | limit: u32 281 | ) { 282 | let dimension = self.get_size(); 283 | gl.draw(event.viewport(), |context, g| { 284 | graphics::clear(ORANGE, g); 285 | draw::draw_render(&self.board, dimension, limit, (&context, g)); 286 | 287 | if self.help 288 | && self.win == false 289 | && self.get_turn_is_ai() == false { 290 | draw::draw_help(&self.board, dimension, self.help_decision, ( 291 | &context, 292 | g 293 | )); 294 | } 295 | if self.event.get_over() { 296 | let (x, y) = self.event.get_coordinate(); 297 | 298 | if x < self.board.get_size() as u32 && y < self.board.get_size() as u32 { 299 | draw::draw_over ( 300 | &self.board, 301 | dimension, 302 | (x, y), 303 | (&context, g) 304 | ); 305 | } 306 | } 307 | }); 308 | } 309 | 310 | /// The `start` function loops the board. 311 | 312 | pub fn start ( 313 | &mut self, 314 | ) { 315 | let opengl = opengl_graphics::OpenGL::V3_2; 316 | let window: Window = piston::window::WindowSettings::new ( 317 | "Gomoku", 318 | self.event.get_dimension(), 319 | ).exit_on_esc(true).opengl(opengl).build().unwrap(); 320 | let ref mut gl = opengl_graphics::GlGraphics::new(opengl); 321 | let window = std::rc::Rc::new(std::cell::RefCell::new(window)); 322 | let limit: u32 = self.board.get_size() as u32; 323 | 324 | if self.is_ai_versus() { 325 | for event in window.clone().events() { 326 | if let Some(render) = event.render_args() { 327 | self.input(&event, limit); 328 | self.draw(gl, &render, limit); 329 | event.update(|_| {}); 330 | } 331 | } 332 | } else { 333 | for event in window.clone().events() { 334 | 335 | self.input(&event, limit); 336 | if let Some(render) = event.render_args() { 337 | self.draw(gl, &render, limit); 338 | event.update(|_| {}); 339 | } 340 | } 341 | } 342 | } 343 | } 344 | 345 | impl Default for Console { 346 | 347 | /// The `new` constructor function returns the interface console. 348 | 349 | fn default () -> Self { 350 | let board: GoBoard = Default::default(); 351 | let (team_player, team_friend) = Team::new_teams(); 352 | let size: u32 = board.get_size() as u32; 353 | 354 | Console { 355 | board: board, 356 | event: Mouse::new((CASE_WIDTH * size, CASE_WIDTH * size)), 357 | player: (team_player, Player::Human), 358 | friend: (team_friend, Player::Ai), 359 | layer: 3, 360 | turn: 0, 361 | win: false, 362 | help: false, 363 | help_decision: (size/2, size/2), 364 | info: true, 365 | debug_map: false, 366 | } 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "gomoku" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "chrono 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "clap 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "piston 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", 8 | "piston2d-graphics 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 9 | "piston2d-opengl_graphics 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", 10 | "pistoncore-glfw_window 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 11 | "pistoncore-glutin_window 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", 12 | "pistoncore-sdl2_window 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", 13 | ] 14 | 15 | [[package]] 16 | name = "advapi32-sys" 17 | version = "0.1.2" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | dependencies = [ 20 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 21 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 22 | ] 23 | 24 | [[package]] 25 | name = "android_glue" 26 | version = "0.1.1" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | 29 | [[package]] 30 | name = "ansi_term" 31 | version = "0.7.2" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | 34 | [[package]] 35 | name = "bitflags" 36 | version = "0.3.3" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | 39 | [[package]] 40 | name = "bitflags" 41 | version = "0.4.0" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | 44 | [[package]] 45 | name = "byteorder" 46 | version = "0.3.13" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | 49 | [[package]] 50 | name = "cgl" 51 | version = "0.1.1" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | dependencies = [ 54 | "gleam 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 56 | ] 57 | 58 | [[package]] 59 | name = "chrono" 60 | version = "0.2.17" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | dependencies = [ 63 | "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", 65 | ] 66 | 67 | [[package]] 68 | name = "clap" 69 | version = "2.1.2" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | dependencies = [ 72 | "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "strsim 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 75 | "vec_map 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "yaml-rust 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 77 | ] 78 | 79 | [[package]] 80 | name = "clock_ticks" 81 | version = "0.1.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | dependencies = [ 84 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 85 | ] 86 | 87 | [[package]] 88 | name = "cocoa" 89 | version = "0.2.1" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | dependencies = [ 92 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 93 | "core-graphics 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 94 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 95 | "objc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 96 | ] 97 | 98 | [[package]] 99 | name = "core-foundation" 100 | version = "0.2.0" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | dependencies = [ 103 | "core-foundation-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 105 | ] 106 | 107 | [[package]] 108 | name = "core-foundation-sys" 109 | version = "0.2.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | dependencies = [ 112 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 113 | ] 114 | 115 | [[package]] 116 | name = "core-graphics" 117 | version = "0.2.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | dependencies = [ 120 | "core-foundation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 121 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 122 | ] 123 | 124 | [[package]] 125 | name = "dlib" 126 | version = "0.1.1" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | dependencies = [ 129 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 130 | ] 131 | 132 | [[package]] 133 | name = "draw_state" 134 | version = "0.2.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | dependencies = [ 137 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 138 | ] 139 | 140 | [[package]] 141 | name = "dwmapi-sys" 142 | version = "0.1.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | dependencies = [ 145 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 146 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 147 | ] 148 | 149 | [[package]] 150 | name = "dylib" 151 | version = "0.0.2" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | dependencies = [ 154 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 155 | ] 156 | 157 | [[package]] 158 | name = "enum_primitive" 159 | version = "0.0.1" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | dependencies = [ 162 | "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 163 | ] 164 | 165 | [[package]] 166 | name = "enum_primitive" 167 | version = "0.1.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | dependencies = [ 170 | "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 171 | ] 172 | 173 | [[package]] 174 | name = "flate2" 175 | version = "0.2.13" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | dependencies = [ 178 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 179 | "miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 180 | ] 181 | 182 | [[package]] 183 | name = "freetype-rs" 184 | version = "0.3.1" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | dependencies = [ 187 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 188 | "freetype-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 189 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 190 | ] 191 | 192 | [[package]] 193 | name = "freetype-sys" 194 | version = "0.1.2" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | dependencies = [ 197 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 198 | "libz-sys 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "pkg-config 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 200 | ] 201 | 202 | [[package]] 203 | name = "gcc" 204 | version = "0.3.25" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | 207 | [[package]] 208 | name = "gdi32-sys" 209 | version = "0.1.1" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | dependencies = [ 212 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 213 | ] 214 | 215 | [[package]] 216 | name = "gl" 217 | version = "0.1.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | dependencies = [ 220 | "gl_common 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 221 | "gl_generator 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 222 | "khronos_api 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", 223 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 224 | ] 225 | 226 | [[package]] 227 | name = "gl_common" 228 | version = "0.1.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | dependencies = [ 231 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 232 | ] 233 | 234 | [[package]] 235 | name = "gl_generator" 236 | version = "0.1.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | dependencies = [ 239 | "khronos_api 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", 240 | "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 241 | "xml-rs 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", 242 | ] 243 | 244 | [[package]] 245 | name = "gl_generator" 246 | version = "0.2.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | dependencies = [ 249 | "khronos_api 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", 250 | "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 251 | "xml-rs 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", 252 | ] 253 | 254 | [[package]] 255 | name = "gleam" 256 | version = "0.1.19" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | dependencies = [ 259 | "gl_generator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 260 | "khronos_api 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", 261 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 262 | ] 263 | 264 | [[package]] 265 | name = "glfw" 266 | version = "0.1.2" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | dependencies = [ 269 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 270 | "enum_primitive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "glfw-sys 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 272 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 273 | "log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 274 | "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 275 | "semver 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 276 | ] 277 | 278 | [[package]] 279 | name = "glfw-sys" 280 | version = "3.1.3" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | 283 | [[package]] 284 | name = "glob" 285 | version = "0.2.10" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | 288 | [[package]] 289 | name = "glutin" 290 | version = "0.3.7" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | dependencies = [ 293 | "android_glue 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 294 | "cgl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 295 | "cocoa 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 296 | "core-foundation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 297 | "core-graphics 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 298 | "dwmapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 299 | "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 300 | "gl_common 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 301 | "gl_generator 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 302 | "kernel32-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 303 | "khronos_api 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 304 | "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", 305 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 306 | "objc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 307 | "osmesa-sys 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 308 | "shared_library 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 309 | "shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 310 | "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 311 | "wayland-client 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 312 | "wayland-kbd 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 313 | "wayland-window 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 314 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 315 | "x11-dl 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 316 | ] 317 | 318 | [[package]] 319 | name = "image" 320 | version = "0.3.15" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | dependencies = [ 323 | "byteorder 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", 324 | "enum_primitive 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 325 | "glob 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", 326 | "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 327 | "png 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 328 | ] 329 | 330 | [[package]] 331 | name = "interpolation" 332 | version = "0.1.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | 335 | [[package]] 336 | name = "kernel32-sys" 337 | version = "0.1.4" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | dependencies = [ 340 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 341 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 342 | ] 343 | 344 | [[package]] 345 | name = "kernel32-sys" 346 | version = "0.2.1" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | dependencies = [ 349 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 350 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 351 | ] 352 | 353 | [[package]] 354 | name = "khronos_api" 355 | version = "0.0.5" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | 358 | [[package]] 359 | name = "khronos_api" 360 | version = "0.0.7" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | 363 | [[package]] 364 | name = "khronos_api" 365 | version = "0.0.8" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | 368 | [[package]] 369 | name = "lazy_static" 370 | version = "0.1.15" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | 373 | [[package]] 374 | name = "libc" 375 | version = "0.1.12" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | 378 | [[package]] 379 | name = "libc" 380 | version = "0.2.2" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | 383 | [[package]] 384 | name = "libz-sys" 385 | version = "0.1.9" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | dependencies = [ 388 | "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", 389 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 390 | "pkg-config 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 391 | ] 392 | 393 | [[package]] 394 | name = "log" 395 | version = "0.3.3" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | dependencies = [ 398 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 399 | ] 400 | 401 | [[package]] 402 | name = "malloc_buf" 403 | version = "0.0.6" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | dependencies = [ 406 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 407 | ] 408 | 409 | [[package]] 410 | name = "miniz-sys" 411 | version = "0.1.7" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | dependencies = [ 414 | "gcc 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", 415 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 416 | ] 417 | 418 | [[package]] 419 | name = "mmap" 420 | version = "0.1.1" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | dependencies = [ 423 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 424 | "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 425 | ] 426 | 427 | [[package]] 428 | name = "num" 429 | version = "0.1.28" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | dependencies = [ 432 | "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", 433 | "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", 434 | ] 435 | 436 | [[package]] 437 | name = "objc" 438 | version = "0.1.8" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | dependencies = [ 441 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 442 | "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 443 | ] 444 | 445 | [[package]] 446 | name = "osmesa-sys" 447 | version = "0.0.5" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | dependencies = [ 450 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 451 | "shared_library 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 452 | ] 453 | 454 | [[package]] 455 | name = "piston" 456 | version = "0.13.1" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | dependencies = [ 459 | "pistoncore-event_loop 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", 460 | "pistoncore-input 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 461 | "pistoncore-window 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", 462 | ] 463 | 464 | [[package]] 465 | name = "piston-float" 466 | version = "0.2.0" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | 469 | [[package]] 470 | name = "piston-shaders_graphics2d" 471 | version = "0.1.0" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | 474 | [[package]] 475 | name = "piston-texture" 476 | version = "0.2.3" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | 479 | [[package]] 480 | name = "piston-viewport" 481 | version = "0.2.0" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | dependencies = [ 484 | "piston-float 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 485 | ] 486 | 487 | [[package]] 488 | name = "piston2d-graphics" 489 | version = "0.10.1" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | dependencies = [ 492 | "draw_state 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 493 | "interpolation 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 494 | "piston-texture 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 495 | "piston-viewport 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 496 | "read_color 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 497 | "vecmath 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 498 | ] 499 | 500 | [[package]] 501 | name = "piston2d-opengl_graphics" 502 | version = "0.14.1" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | dependencies = [ 505 | "freetype-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 506 | "gl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 507 | "image 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 508 | "khronos_api 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 509 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 510 | "piston-shaders_graphics2d 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 511 | "piston-texture 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 512 | "piston2d-graphics 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 513 | "shader_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 514 | ] 515 | 516 | [[package]] 517 | name = "pistoncore-event_loop" 518 | version = "0.13.1" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | dependencies = [ 521 | "clock_ticks 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 522 | "piston-viewport 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 523 | "pistoncore-input 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 524 | "pistoncore-window 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", 525 | ] 526 | 527 | [[package]] 528 | name = "pistoncore-glfw_window" 529 | version = "0.12.0" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | dependencies = [ 532 | "gl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 533 | "glfw 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 534 | "pistoncore-input 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 535 | "pistoncore-window 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", 536 | "shader_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 537 | ] 538 | 539 | [[package]] 540 | name = "pistoncore-glutin_window" 541 | version = "0.15.0" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | dependencies = [ 544 | "gl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 545 | "glutin 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 546 | "pistoncore-input 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 547 | "pistoncore-window 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", 548 | "shader_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 549 | ] 550 | 551 | [[package]] 552 | name = "pistoncore-input" 553 | version = "0.8.0" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | dependencies = [ 556 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 557 | "piston-viewport 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 558 | "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", 559 | ] 560 | 561 | [[package]] 562 | name = "pistoncore-sdl2_window" 563 | version = "0.16.0" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | dependencies = [ 566 | "gl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 567 | "pistoncore-input 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 568 | "pistoncore-window 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", 569 | "sdl2 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", 570 | "shader_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 571 | ] 572 | 573 | [[package]] 574 | name = "pistoncore-window" 575 | version = "0.12.1" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | dependencies = [ 578 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 579 | "pistoncore-input 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 580 | "shader_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 581 | ] 582 | 583 | [[package]] 584 | name = "pkg-config" 585 | version = "0.3.7" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | 588 | [[package]] 589 | name = "png" 590 | version = "0.3.1" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | dependencies = [ 593 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 594 | "flate2 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 595 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 596 | "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 597 | ] 598 | 599 | [[package]] 600 | name = "rand" 601 | version = "0.3.12" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | dependencies = [ 604 | "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 605 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 606 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 607 | ] 608 | 609 | [[package]] 610 | name = "read_color" 611 | version = "0.1.0" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | 614 | [[package]] 615 | name = "rustc-serialize" 616 | version = "0.3.16" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | 619 | [[package]] 620 | name = "sdl2" 621 | version = "0.9.1" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | dependencies = [ 624 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 625 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 626 | "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 627 | "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", 628 | "sdl2-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 629 | ] 630 | 631 | [[package]] 632 | name = "sdl2-sys" 633 | version = "0.6.2" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | dependencies = [ 636 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 637 | "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", 638 | ] 639 | 640 | [[package]] 641 | name = "semver" 642 | version = "0.2.0" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | 645 | [[package]] 646 | name = "shader_version" 647 | version = "0.2.1" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | 650 | [[package]] 651 | name = "shared_library" 652 | version = "0.1.2" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | dependencies = [ 655 | "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", 656 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 657 | ] 658 | 659 | [[package]] 660 | name = "shell32-sys" 661 | version = "0.1.1" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | dependencies = [ 664 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 665 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 666 | ] 667 | 668 | [[package]] 669 | name = "strsim" 670 | version = "0.4.0" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | 673 | [[package]] 674 | name = "tempdir" 675 | version = "0.3.4" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | dependencies = [ 678 | "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", 679 | ] 680 | 681 | [[package]] 682 | name = "tempfile" 683 | version = "1.1.3" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | dependencies = [ 686 | "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 687 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 688 | "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", 689 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 690 | ] 691 | 692 | [[package]] 693 | name = "time" 694 | version = "0.1.34" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | dependencies = [ 697 | "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 698 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 699 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 700 | ] 701 | 702 | [[package]] 703 | name = "user32-sys" 704 | version = "0.1.2" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | dependencies = [ 707 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 708 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 709 | ] 710 | 711 | [[package]] 712 | name = "vec_map" 713 | version = "0.4.0" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | 716 | [[package]] 717 | name = "vecmath" 718 | version = "0.2.0" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | dependencies = [ 721 | "piston-float 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 722 | ] 723 | 724 | [[package]] 725 | name = "wayland-client" 726 | version = "0.2.1" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | dependencies = [ 729 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 730 | "dlib 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 731 | "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", 732 | "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 733 | ] 734 | 735 | [[package]] 736 | name = "wayland-kbd" 737 | version = "0.2.1" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | dependencies = [ 740 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 741 | "dlib 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 742 | "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", 743 | "mmap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 744 | "wayland-client 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 745 | ] 746 | 747 | [[package]] 748 | name = "wayland-window" 749 | version = "0.1.1" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | dependencies = [ 752 | "byteorder 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", 753 | "tempfile 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 754 | "wayland-client 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 755 | ] 756 | 757 | [[package]] 758 | name = "winapi" 759 | version = "0.2.5" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | 762 | [[package]] 763 | name = "winapi-build" 764 | version = "0.1.1" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | 767 | [[package]] 768 | name = "x11-dl" 769 | version = "2.0.1" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | dependencies = [ 772 | "dylib 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 773 | "libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 774 | ] 775 | 776 | [[package]] 777 | name = "xml-rs" 778 | version = "0.1.26" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | dependencies = [ 781 | "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 782 | ] 783 | 784 | [[package]] 785 | name = "yaml-rust" 786 | version = "0.3.0" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | 789 | --------------------------------------------------------------------------------