Skip to content

Commit a71e4e3

Browse files
committed
Four types of playouts
1 parent be650ef commit a71e4e3

File tree

10 files changed

+181
-70
lines changed

10 files changed

+181
-70
lines changed

src/board/mod.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -264,27 +264,6 @@ impl Board {
264264
}
265265
}
266266

267-
pub fn playout_move(&self, rng: &mut XorShiftRng) -> Move {
268-
let color = self.next_player();
269-
let vacant = self.vacant();
270-
if vacant
271-
.iter()
272-
.map(|coord| Play(color, coord.col, coord.row))
273-
.any(|m| !self.is_eye(&m.coord(), *m.color()) && self.is_legal(m).is_ok() ) {
274-
//while loop testing all vacant spots
275-
loop {
276-
let random_vacant = vacant[rng.gen::<usize>() % vacant.len()];
277-
let random_play = Play(color, random_vacant.col, random_vacant.row);
278-
if !self.is_eye(&random_vacant, color) && self.is_legal(random_play).is_ok() {
279-
return random_play;
280-
}
281-
}
282-
} else {
283-
let color = self.next_player();
284-
Pass(color)
285-
}
286-
}
287-
288267
//#[inline(never)] //turn off for profiling
289268
pub fn legal_moves_without_eyes(&self) -> Vec<Move> {
290269
self.legal_moves_without_superko_check()

src/board/test/mod.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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 board = game.board();
503-
let mut rng = weak_rng();
504-
for _ in 0..10 {
505-
let m = board.playout_move(&mut rng);
506-
assert!(board.is_legal(m).is_ok());
507-
}
508-
}

src/engine/mc/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ fn spin_up_worker<'a, T: McEngine>(color: Color, recv_halt: Receiver<()>, moves:
125125
loop {
126126
for _ in 0..runs {
127127
let m = moves[rng.gen::<usize>() % moves.len()];
128-
let playout_result = config.playout.run(&board, &m, &mut rng);
128+
let playout_result = config.playout.run(&board, &m);
129129
let winner = playout_result.winner();
130130
T::record_playout(&mut stats, &playout_result, winner == color);
131131
}

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub fn main() {
7474
opts.optopt("e", "engine", "select an engine (defaults to amaf)", "amaf|mc|random");
7575
opts.optopt("r", "ruleset", "select the ruleset (defaults to chinese)", "cgos|chinese|tromp-taylor|minimal");
7676
opts.optopt("t", "threads", "number of threads to use (defaults to 1)", "NUM");
77-
opts.optopt("p", "playout", "type of playout to use (defaults to no-eyes)", "no-eyes|simple");
77+
opts.optopt("p", "playout", "type of playout to use (defaults to no-eyes)", "no-eyes|no-eyes-with-pass|simple|simple-with-pass");
7878
opts.optflag("h", "help", "print this help menu");
7979
opts.optflag("v", "version", "print the version number");
8080
opts.optflag("l", "log", "log to stderr (defaults to false)");

src/playout/mod.rs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@
2020
************************************************************************/
2121

2222
pub use self::no_eyes::NoEyesPlayout;
23+
pub use self::no_eyes::NoEyesWithPassPlayout;
2324
pub use self::simple::SimplePlayout;
25+
pub use self::simple::SimpleWithPassPlayout;
2426
use board::Board;
25-
use board::Move;
2627
use board::Color;
28+
use board::Move;
29+
use board::Pass;
30+
use board::Play;
2731

28-
use rand::XorShiftRng;
32+
use rand::random;
2933

3034
mod no_eyes;
3135
mod simple;
@@ -35,43 +39,76 @@ pub fn factory(opt: Option<String>) -> Box<Playout> {
3539
match opt {
3640
Some(s) => {
3741
match s.as_slice() {
42+
"no-eyes-with-pass" => Box::new(NoEyesWithPassPlayout::new()),
3843
"simple" => Box::new(SimplePlayout::new()),
44+
"simple-with-pass" => Box::new(SimpleWithPassPlayout::new()),
3945
_ => Box::new(NoEyesPlayout::new()),
4046
}
4147
},
4248
None => Box::new(NoEyesPlayout::new())
4349
}
4450
}
4551

46-
4752
pub trait Playout: Sync + Send {
4853

49-
fn run(&self, b: &Board, initial_move: &Move, rng: &mut XorShiftRng) -> PlayoutResult {
54+
fn run(&self, b: &Board, initial_move: &Move) -> PlayoutResult {
5055
let mut board = b.clone();
5156
let mut played_moves = Vec::new();
5257
board.play(*initial_move);
5358
played_moves.push(*initial_move);
5459
let max_moves = self.max_moves(board.size());
5560
let mut move_count = 0;
5661
while !board.is_game_over() && move_count < max_moves {
57-
let m = board.playout_move(rng);
62+
let m = self.select_move(&board);
5863
board.play(m);
5964
played_moves.push(m);
6065
move_count += 1;
6166
}
6267
PlayoutResult::new(played_moves, board.winner())
6368
}
6469

65-
fn moves(&self, b: &Board) -> Vec<Move>;
70+
fn is_playable(&self, board: &Board, m: &Move) -> bool;
71+
fn include_pass(&self) -> bool;
6672

6773
fn max_moves(&self, size: u8) -> usize {
6874
size as usize * size as usize * 3
6975
}
7076

77+
fn select_move(&self, board: &Board) -> Move {
78+
let color = board.next_player();
79+
let vacant = board.vacant();
80+
let playable_move_exists = vacant
81+
.iter()
82+
.map(|c| Play(color, c.col, c.row))
83+
.any(|m| board.is_legal(m).is_ok() && self.is_playable(board, &m));
84+
if playable_move_exists {
85+
loop {
86+
let add = if self.include_pass() {
87+
1
88+
} else {
89+
0
90+
};
91+
let r = random::<usize>() % (vacant.len() + add);
92+
if r == vacant.len() {
93+
return Pass(color);
94+
} else {
95+
let c = vacant[r];
96+
let m = Play(color, c.col, c.row);
97+
if board.is_legal(m).is_ok() && self.is_playable(board, &m) {
98+
return m;
99+
}
100+
}
101+
}
102+
} else {
103+
Pass(color)
104+
}
105+
}
106+
71107
fn playout_type(&self) -> String;
72108

73109
}
74110

111+
#[derive(Debug)]
75112
pub struct PlayoutResult {
76113
moves: Vec<Move>,
77114
winner: Color,

src/playout/no_eyes.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,39 @@ impl NoEyesPlayout {
3636

3737
impl Playout for NoEyesPlayout {
3838

39-
fn moves(&self, b: &Board) -> Vec<Move> {
40-
b.legal_moves_without_eyes()
39+
fn is_playable(&self, board: &Board, m: &Move) -> bool {
40+
!board.is_eye(&m.coord(), *m.color())
41+
}
42+
43+
fn include_pass(&self) -> bool {
44+
false
45+
}
46+
47+
fn playout_type(&self) -> String {
48+
format!("{:?}", self)
49+
}
50+
51+
}
52+
53+
#[derive(Debug)]
54+
pub struct NoEyesWithPassPlayout;
55+
56+
impl NoEyesWithPassPlayout {
57+
58+
pub fn new() -> NoEyesWithPassPlayout {
59+
NoEyesWithPassPlayout
60+
}
61+
62+
}
63+
64+
impl Playout for NoEyesWithPassPlayout {
65+
66+
fn is_playable(&self, board: &Board, m: &Move) -> bool {
67+
!board.is_eye(&m.coord(), *m.color())
68+
}
69+
70+
fn include_pass(&self) -> bool {
71+
true
4172
}
4273

4374
fn playout_type(&self) -> String {

src/playout/simple.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,38 @@ impl SimplePlayout {
3636

3737
impl Playout for SimplePlayout {
3838

39-
fn moves(&self, b: &Board) -> Vec<Move> {
40-
b.legal_moves_without_superko_check()
39+
fn is_playable(&self, _: &Board, _: &Move) -> bool {
40+
true
41+
}
42+
43+
fn include_pass(&self) -> bool {
44+
false
45+
}
46+
47+
fn playout_type(&self) -> String {
48+
format!("{:?}", self)
49+
}
50+
}
51+
52+
#[derive(Debug)]
53+
pub struct SimpleWithPassPlayout;
54+
55+
impl SimpleWithPassPlayout {
56+
57+
pub fn new() -> SimpleWithPassPlayout {
58+
SimpleWithPassPlayout
59+
}
60+
61+
}
62+
63+
impl Playout for SimpleWithPassPlayout {
64+
65+
fn is_playable(&self, _: &Board, _: &Move) -> bool {
66+
true
67+
}
68+
69+
fn include_pass(&self) -> bool {
70+
true
4171
}
4272

4373
fn playout_type(&self) -> String {

src/playout/test/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,25 @@ fn factory_returns_no_eyes_by_default() {
3131
}
3232

3333
#[test]
34-
fn factory_returns_simple_when_given_simple() {
34+
fn factory_returns_simple() {
3535
let playout = super::factory(Some(String::from_str("simple")));
3636
assert_eq!("SimplePlayout", playout.playout_type());
3737
}
3838

39+
#[test]
40+
fn factory_returns_simple_with_pass() {
41+
let playout = super::factory(Some(String::from_str("simple-with-pass")));
42+
assert_eq!("SimpleWithPassPlayout", playout.playout_type());
43+
}
44+
3945
#[test]
4046
fn factroy_returns_no_eyes_when_given_any_string() {
4147
let playout = super::factory(Some(String::from_str("foo")));
4248
assert_eq!("NoEyesPlayout", playout.playout_type());
4349
}
50+
51+
#[test]
52+
fn factory_returns_no_eyes_with_pass() {
53+
let playout = super::factory(Some(String::from_str("no-eyes-with-pass")));
54+
assert_eq!("NoEyesWithPassPlayout", playout.playout_type());
55+
}

src/playout/test/no_eyes.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,17 @@ use board::Black;
2525
use board::Board;
2626
use board::Play;
2727
use playout::NoEyesPlayout;
28+
use playout::NoEyesWithPassPlayout;
2829
use playout::Playout;
2930
use ruleset::KgsChinese;
3031

31-
use rand::weak_rng;
3232
use test::Bencher;
3333

3434
#[test]
3535
fn should_add_the_passed_moves_as_the_first_move() {
3636
let board = Board::new(9, 6.5, KgsChinese);
37-
let mut rng = weak_rng();
3837
let playout = NoEyesPlayout::new();
39-
let result = playout.run(&board, &Play(Black, 1, 1), &mut rng);
38+
let result = playout.run(&board, &Play(Black, 1, 1));
4039
assert_eq!(Play(Black, 1, 1), result.moves()[0]);
4140
}
4241

@@ -47,25 +46,43 @@ fn max_moves() {
4746
}
4847

4948
#[bench]
50-
fn bench_9x9_playout_speed(b: &mut Bencher) {
49+
fn no_eyes_09x09(b: &mut Bencher) {
5150
let board = Board::new(9, 6.5, KgsChinese);
52-
let mut rng = weak_rng();
5351
let playout = NoEyesPlayout::new();
54-
b.iter(|| playout.run(&board, &Play(Black, 1, 1), &mut rng))
52+
b.iter(|| playout.run(&board, &Play(Black, 1, 1)))
5553
}
5654

5755
#[bench]
58-
fn bench_13x13_playout_speed(b: &mut Bencher) {
56+
fn no_eyes_13x13(b: &mut Bencher) {
5957
let board = Board::new(13, 6.5, KgsChinese);
60-
let mut rng = weak_rng();
6158
let playout = NoEyesPlayout::new();
62-
b.iter(|| playout.run(&board, &Play(Black, 1, 1), &mut rng))
59+
b.iter(|| playout.run(&board, &Play(Black, 1, 1)))
6360
}
6461

6562
#[bench]
66-
fn bench_19x19_playout_speed(b: &mut Bencher) {
63+
fn no_eyes_19x19(b: &mut Bencher) {
6764
let board = Board::new(19, 6.5, KgsChinese);
68-
let mut rng = weak_rng();
6965
let playout = NoEyesPlayout::new();
70-
b.iter(|| playout.run(&board, &Play(Black, 1, 1), &mut rng))
66+
b.iter(|| playout.run(&board, &Play(Black, 1, 1)))
67+
}
68+
69+
#[bench]
70+
fn with_pass_09x09(b: &mut Bencher) {
71+
let board = Board::new(9, 6.5, KgsChinese);
72+
let playout = NoEyesWithPassPlayout::new();
73+
b.iter(|| playout.run(&board, &Play(Black, 1, 1)))
74+
}
75+
76+
#[bench]
77+
fn with_pass_13x13(b: &mut Bencher) {
78+
let board = Board::new(13, 6.5, KgsChinese);
79+
let playout = NoEyesWithPassPlayout::new();
80+
b.iter(|| playout.run(&board, &Play(Black, 1, 1)))
81+
}
82+
83+
#[bench]
84+
fn with_pass_19x19(b: &mut Bencher) {
85+
let board = Board::new(19, 6.5, KgsChinese);
86+
let playout = NoEyesWithPassPlayout::new();
87+
b.iter(|| playout.run(&board, &Play(Black, 1, 1)))
7188
}

0 commit comments

Comments
 (0)