Skip to content

Commit 1c96f06

Browse files
committed
Merge pull request #120 from ujh/two-kinds-of-playouts
Four kinds of playouts
2 parents 5f51343 + a71e4e3 commit 1c96f06

File tree

21 files changed

+601
-206
lines changed

21 files changed

+601
-206
lines changed

src/board/mod.rs

Lines changed: 6 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* along with Iomrascálaí. If not, see <http://www.gnu.org/licenses/>. *
2020
* *
2121
************************************************************************/
22+
2223
pub use self::Color::Black;
2324
pub use self::Color::Empty;
2425
pub use self::Color::White;
@@ -28,22 +29,22 @@ pub use self::movement::Move;
2829
pub use self::movement::Pass;
2930
pub use self::movement::Play;
3031
pub use self::movement::Resign;
31-
use rand::{Rng, XorShiftRng};
3232
use ruleset::Ruleset;
3333
use score::Score;
3434
use self::point::Point;
3535

36-
use std::sync::Arc;
36+
use rand::Rng;
37+
use rand::XorShiftRng;
3738
use std::collections::HashMap;
3839
use std::collections::HashSet;
3940
use std::fmt;
40-
use std::vec::Vec;
41+
use std::sync::Arc;
4142

42-
mod test;
4343
mod chain;
4444
mod coord;
4545
mod movement;
4646
mod point;
47+
mod test;
4748

4849
#[derive(Debug, Eq, PartialEq)]
4950
pub enum IllegalMove {
@@ -262,63 +263,7 @@ impl Board {
262263
.collect()
263264
}
264265
}
265-
266-
pub fn playout_move(&self, rng: &mut XorShiftRng) -> Move {
267-
let color = self.next_player();
268-
let vacant = self.vacant();
269-
if vacant
270-
.iter()
271-
.map(|coord| Play(color, coord.col, coord.row))
272-
.any(|m| !self.is_eye(&m.coord(), *m.color()) && self.playout_legal_move(m) ) {
273-
//while loop testing all vacant spots
274-
loop {
275-
let random_vacant = vacant[rng.gen::<usize>() % vacant.len()];
276-
let random_play = Play(color, random_vacant.col, random_vacant.row);
277-
if !self.is_eye(&random_vacant, color) && self.playout_legal_move(random_play) {
278-
return random_play;
279-
}
280-
}
281-
} else {
282-
let color = self.next_player();
283-
Pass(color)
284-
}
285-
}
286-
287-
fn playout_legal_move(&self, m: Move) -> bool {
288-
// Can't play on a Ko point
289-
if self.ko.is_some() && m.coord() == self.ko.unwrap() {
290-
if self.neighbours(m.coord()) //neighbours of the coordinate of the ko point
291-
.iter()
292-
.filter(|&c| self.color(c) == m.color().opposite()) //accept coordinates of opposite stones
293-
.map(|&c| self.get_chain(c).unwrap()) //get the chain of those opposite stones
294-
.any(|chain| chain.liberties().len() == 1 && chain.coords().len() == 1) { //if any of them has one liberty and one stone
295-
return false;
296-
}
297-
}
298-
// Can't play suicide move
299-
if !self.ruleset.suicide_allowed() {
300-
// All neighbours must be occupied
301-
if self.neighbours(m.coord()).iter().all(|c| self.color(c) != Empty) {
302-
// A move is a suicide move if all of the opposing,
303-
// neighbouring chain has more than one liberty and all of
304-
// our own chains have only one liberty.
305-
let enemy_chains_with_other_libs = self.neighbours(m.coord())
306-
.iter()
307-
.filter(|&c| self.color(c) == m.color().opposite())
308-
.all(|&c| self.get_chain(c).unwrap().liberties().len() > 1);
309-
let own_chains_without_other_libs = self.neighbours(m.coord())
310-
.iter()
311-
.filter(|&c| self.color(c) == *m.color())
312-
.all(|&c| self.get_chain(c).unwrap().liberties().len() <= 1);
313-
if enemy_chains_with_other_libs && own_chains_without_other_libs {
314-
return false;
315-
}
316-
}
317-
}
318-
319-
true
320-
}
321-
266+
322267
//#[inline(never)] //turn off for profiling
323268
pub fn legal_moves_without_eyes(&self) -> Vec<Move> {
324269
self.legal_moves_without_superko_check()

src/board/test/mod.rs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
#![cfg(test)]
2424
#![allow(unused_must_use)]
25-
use std::path::Path;
2625
use board::Black;
2726
use board::Board;
2827
use board::Coord;
@@ -36,8 +35,9 @@ use ruleset::AnySizeTrompTaylor;
3635
use ruleset::KgsChinese;
3736
use ruleset::Minimal;
3837
use sgf::Parser;
39-
use rand::{Rng, weak_rng};
40-
use test::Bencher;
38+
39+
use rand::weak_rng;
40+
use std::path::Path;
4141

4242
mod diagonals;
4343
mod eye;
@@ -494,15 +494,3 @@ fn adv_stones_removed_only_contains_each_coord_once() {
494494
removed_coords.dedup();
495495
assert_eq!(removed_coords.len(), board.adv_stones_removed().len());
496496
}
497-
498-
#[test]
499-
fn random_playout_moves_should_be_legal() {
500-
let parser = Parser::from_path(Path::new("fixtures/sgf/recapture-but-not-ko.sgf"));
501-
let game = parser.game().unwrap();
502-
let mut board = game.board();
503-
let mut rng = weak_rng();
504-
for i in 0..10 {
505-
let m = board.playout_move(&mut rng);
506-
assert!(board.is_legal(m).is_ok());
507-
}
508-
}

src/config.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
* *
2020
************************************************************************/
2121

22+
use playout::Playout;
23+
use playout::SimplePlayout;
2224
use ruleset::Minimal;
2325
use ruleset::Ruleset;
2426

25-
#[derive(Copy)]
2627
pub struct Config {
2728
pub log: bool,
29+
pub playout: Box<Playout>,
2830
pub ruleset: Ruleset,
2931
pub threads: usize,
3032
}
@@ -34,6 +36,7 @@ impl Config {
3436
pub fn default() -> Config {
3537
Config {
3638
log: false,
39+
playout: Box::new(SimplePlayout::new()),
3740
ruleset: Minimal,
3841
threads: 1,
3942
}

src/engine/controller/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,21 @@ use timer::Timer;
2828
use std::old_io::Writer;
2929

3030
use std::old_io::timer::sleep;
31+
use std::sync::Arc;
3132
use std::sync::mpsc::channel;
3233
use std::thread;
3334
use std::time::duration::Duration;
3435

3536
mod test;
3637

3738
pub struct EngineController<'a> {
38-
config: Config,
39+
config: Arc<Config>,
3940
engine: Box<Engine + 'a>,
4041
}
4142

4243
impl<'a> EngineController<'a> {
4344

44-
pub fn new<'b>(config: Config, engine: Box<Engine + 'b>) -> EngineController<'b> {
45+
pub fn new<'b>(config: Arc<Config>, engine: Box<Engine + 'b>) -> EngineController<'b> {
4546
EngineController {
4647
config: config,
4748
engine: engine,

src/engine/controller/test.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use ruleset::Minimal;
3232
use super::EngineController;
3333
use timer::Timer;
3434

35+
use std::sync::Arc;
3536
use std::sync::mpsc::Receiver;
3637
use std::sync::mpsc::Sender;
3738
use time::PreciseTime;
@@ -61,7 +62,7 @@ fn the_engine_can_use_less_time_than_allocated() {
6162
let mut timer = Timer::new();
6263
let budget = timer.budget(&game);
6364
let engine = Box::new(EarlyReturnEngine::new());
64-
let mut controller = EngineController::new(Config::default(), engine);
65+
let mut controller = EngineController::new(Arc::new(Config::default()), engine);
6566
let start_time = PreciseTime::now();
6667
let m = controller.run_and_return_move(color, &game, &mut timer);
6768
let elapsed_time = start_time.to(PreciseTime::now()).num_milliseconds();
@@ -97,7 +98,7 @@ fn the_controller_asks_the_engine_for_a_move_when_the_time_is_up() {
9798
timer.setup(1, 0, 0);
9899
let budget = timer.budget(&game);
99100
let engine = Box::new(WaitingEngine::new());
100-
let mut controller = EngineController::new(Config::default(), engine);
101+
let mut controller = EngineController::new(Arc::new(Config::default()), engine);
101102
let start_time = PreciseTime::now();
102103
let m = controller.run_and_return_move(color, &game, &mut timer);
103104
let elapsed_time = start_time.to(PreciseTime::now()).num_milliseconds();

src/engine/mc/amaf.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,22 @@ use board::Color;
2424
use board::Move;
2525
use config::Config;
2626
use game::Game;
27-
use playout::Playout;
27+
use playout::PlayoutResult;
2828
use super::Engine;
2929
use super::McEngine;
3030
use super::MoveStats;
3131

32+
use std::sync::Arc;
3233
use std::sync::mpsc::Receiver;
3334
use std::sync::mpsc::Sender;
3435

3536
pub struct AmafMcEngine {
36-
config: Config
37+
config: Arc<Config>
3738
}
3839

3940
impl AmafMcEngine {
4041

41-
pub fn new(config: Config) -> AmafMcEngine {
42+
pub fn new(config: Arc<Config>) -> AmafMcEngine {
4243
AmafMcEngine { config: config }
4344
}
4445

@@ -47,14 +48,18 @@ impl AmafMcEngine {
4748
impl Engine for AmafMcEngine {
4849

4950
fn gen_move(&self, color: Color, game: &Game, sender: Sender<Move>, receiver: Receiver<()>) {
50-
super::gen_move::<AmafMcEngine>(self.config, color, game, sender, receiver);
51+
super::gen_move::<AmafMcEngine>(self.config.clone(), color, game, sender, receiver);
52+
}
53+
54+
fn engine_type(&self) -> &'static str {
55+
"amaf"
5156
}
5257

5358
}
5459

5560
impl McEngine for AmafMcEngine {
5661

57-
fn record_playout(stats: &mut MoveStats, playout: &Playout, won: bool) {
62+
fn record_playout(stats: &mut MoveStats, playout: &PlayoutResult, won: bool) {
5863
for m in playout.moves().iter() {
5964
if won {
6065
stats.record_win(&m);

src/engine/mc/mod.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,13 @@ use board::Resign;
3232
use config::Config;
3333
use game::Game;
3434
use playout::Playout;
35-
use std::old_io::Writer;
35+
use playout::PlayoutResult;
3636

37-
use rand::{Rng, weak_rng};
37+
use rand::Rng;
38+
use rand::weak_rng;
3839
use std::marker::MarkerTrait;
40+
use std::old_io::Writer;
41+
use std::sync::Arc;
3942
use std::sync::mpsc::Receiver;
4043
use std::sync::mpsc::Sender;
4144
use std::sync::mpsc::channel;
@@ -46,11 +49,11 @@ mod simple;
4649

4750
pub trait McEngine: MarkerTrait {
4851

49-
fn record_playout(&mut MoveStats, &Playout, bool);
52+
fn record_playout(&mut MoveStats, &PlayoutResult, bool);
5053

5154
}
5255

53-
fn gen_move<T: McEngine>(config: Config, color: Color, game: &Game, sender: Sender<Move>, receiver: Receiver<()>) {
56+
fn gen_move<T: McEngine>(config: Arc<Config>, color: Color, game: &Game, sender: Sender<Move>, receiver: Receiver<()>) {
5457
let moves = game.legal_moves_without_eyes();
5558
if moves.is_empty() {
5659
if config.log {
@@ -62,7 +65,7 @@ fn gen_move<T: McEngine>(config: Config, color: Color, game: &Game, sender: Send
6265
let mut stats = MoveStats::new(&moves, color);
6366
let mut counter = 0;
6467
let (send_result, receive_result) = channel::<(MoveStats, usize)>();
65-
let (guards, halt_senders) = spin_up::<T>(color, config.threads, &moves, game, send_result);
68+
let (guards, halt_senders) = spin_up::<T>(color, config.clone(), &moves, game, send_result);
6669
loop {
6770
select!(
6871
result = receive_result.recv() => {
@@ -100,30 +103,31 @@ fn finish(color: Color, game: &Game, stats: MoveStats, sender: Sender<Move>, hal
100103
}
101104
}
102105

103-
fn spin_up<'a, T: McEngine>(color: Color, threads: usize, moves: &'a Vec<Move>, game: &Game, send_result: Sender<(MoveStats<'a>, usize)>) -> (Vec<thread::JoinGuard<'a, ()>>, Vec<Sender<()>>) {
106+
fn spin_up<'a, T: McEngine>(color: Color, config: Arc<Config>, moves: &'a Vec<Move>, game: &Game, send_result: Sender<(MoveStats<'a>, usize)>) -> (Vec<thread::JoinGuard<'a, ()>>, Vec<Sender<()>>) {
104107
let mut guards = Vec::new();
105108
let mut halt_senders = Vec::new();
106-
for _ in 0..threads {
109+
for _ in 0..config.threads {
107110
let (send_halt, receive_halt) = channel::<()>();
108111
halt_senders.push(send_halt);
109112
let send_result = send_result.clone();
110-
let guard = spin_up_worker::<T>(color, receive_halt, moves, game.board(), send_result);
113+
let config = config.clone();
114+
let guard = spin_up_worker::<T>(color, receive_halt, moves, game.board(), config, send_result);
111115
guards.push(guard);
112116
}
113117
(guards, halt_senders)
114118
}
115119

116-
fn spin_up_worker<'a, T: McEngine>(color: Color, recv_halt: Receiver<()>, moves: &'a Vec<Move>, board: Board, send_result: Sender<(MoveStats<'a>, usize)>) -> thread::JoinGuard<'a, ()> {
120+
fn spin_up_worker<'a, T: McEngine>(color: Color, recv_halt: Receiver<()>, moves: &'a Vec<Move>, board: Board, config: Arc<Config>, send_result: Sender<(MoveStats<'a>, usize)>) -> thread::JoinGuard<'a, ()> {
117121
thread::scoped(move || {
118122
let runs = 100;
119123
let mut rng = weak_rng();
120124
let mut stats = MoveStats::new(moves, color);
121125
loop {
122126
for _ in 0..runs {
123127
let m = moves[rng.gen::<usize>() % moves.len()];
124-
let playout = Playout::run(&board, &m, &mut rng);
125-
let winner = playout.winner();
126-
T::record_playout(&mut stats, &playout, winner == color);
128+
let playout_result = config.playout.run(&board, &m);
129+
let winner = playout_result.winner();
130+
T::record_playout(&mut stats, &playout_result, winner == color);
127131
}
128132
if recv_halt.try_recv().is_ok() {
129133
break;

src/engine/mc/simple.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,35 +24,42 @@ use board::Color;
2424
use board::Move;
2525
use config::Config;
2626
use game::Game;
27-
use playout::Playout;
27+
use playout::PlayoutResult;
2828
use super::Engine;
2929
use super::McEngine;
3030
use super::MoveStats;
3131

32+
use std::sync::Arc;
3233
use std::sync::mpsc::Receiver;
3334
use std::sync::mpsc::Sender;
3435

3536
pub struct SimpleMcEngine {
36-
config: Config
37+
config: Arc<Config>
3738
}
3839

3940
impl SimpleMcEngine {
4041

41-
pub fn new(config: Config) -> SimpleMcEngine {
42+
pub fn new(config: Arc<Config>) -> SimpleMcEngine {
4243
SimpleMcEngine { config: config }
4344
}
4445

4546
}
4647

4748
impl Engine for SimpleMcEngine {
49+
4850
fn gen_move(&self, color: Color, game: &Game, sender: Sender<Move>, receiver: Receiver<()>) {
49-
super::gen_move::<SimpleMcEngine>(self.config, color, game, sender, receiver);
51+
super::gen_move::<SimpleMcEngine>(self.config.clone(), color, game, sender, receiver);
52+
}
53+
54+
fn engine_type(&self) -> &'static str {
55+
"simple-mc"
5056
}
57+
5158
}
5259

5360
impl McEngine for SimpleMcEngine {
5461

55-
fn record_playout(stats: &mut MoveStats, playout: &Playout, won: bool) {
62+
fn record_playout(stats: &mut MoveStats, playout: &PlayoutResult, won: bool) {
5663
let m = playout.moves()[0];
5764
if won {
5865
stats.record_win(&m);

0 commit comments

Comments
 (0)