Skip to content

Commit

Permalink
Merge 13e876c into 84c17b4
Browse files Browse the repository at this point in the history
  • Loading branch information
ujh committed Nov 18, 2016
2 parents 84c17b4 + 13e876c commit 460b2dc
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 102 deletions.
13 changes: 0 additions & 13 deletions misc/clop/large-patterns.clop

This file was deleted.

File renamed without changes.
14 changes: 14 additions & 0 deletions misc/clop/time-control.clop
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Use for the naming of the .log and .dat files
Name time-control
Processor main
Script ./time-control-runner
Replications 1

# [tree]
# IntegerParameter tree.expand_after 0 20
# LinearParameter tree.rave_equiv 0.0 100.0


# [time_control]
LinearParameter time_control.f_earlystop 1.0 3.0
LinearParameter time_control.p_earlystop 0.3 1.0
8 changes: 4 additions & 4 deletions src/config/defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ small_patterns = 14

[time_control]

c = 0.928949
fastplay_threshold = 0.811985
fastplay_budget = 0.305821
min_stones = 24
f_earlystop = 2.5
min_stones = 30
p_earlystop = 0.4
vacant_point_scaling_factor = 0.75

[tree]

Expand Down
39 changes: 20 additions & 19 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,23 +190,24 @@ impl FromToml for PriorsConfig {
/// Holds all settings related to time control.
#[derive(Debug, PartialEq)]
pub struct TimeControlConfig {
/// Scaling factor for allocating the time for the next move. We
/// devide the remaining time by `c * <EMPTY INTERSECTION COUNT>`.
/// To make sure we never run out of time we set the empty
/// intersection count to 30 if there are less than 30 empty
/// intersections on the board.
pub c: f32,
/// The percentage of the allocated time for the current move
/// after which to check for early termination of the search.
pub fastplay_budget: f32,
/// Once `fastplay_budget` percent of the allocated time for a
/// move have passed check if the best move has a win rate that is
/// higher than this value. If so then stop the search and return
/// this move.
pub fastplay_threshold: f32,
/// Factor by which to scale the static calulation of the time
/// for the next move. This is used to "use up" the time that's
/// save from early stopping when the best move can't change
/// anymore. Normally this value is greater than 1.
pub f_earlystop: f32,
/// Minimum number of stones to use when calculating the budget
/// for the next move.
pub min_stones: usize,
pub min_stones: f32,
/// This factor approximates the number of times the second
/// best move is selected for playouts. This improves the calculation
/// of when the second best move can still overtake the best move.
pub p_earlystop: f32,
/// Scaling factor by which to multiply the empty intersections
/// on the board. It is used to improve the estimate of remaining
/// moves by giving a rough estimate on how many empty intersections
/// will remain on the finished board. That's why it's usually
/// smaller than 1.0.
pub vacant_point_scaling_factor: f32,
}

impl TimeControlConfig {
Expand All @@ -218,10 +219,10 @@ impl TimeControlConfig {
table.extend(default_table);
table.extend(opts);
TimeControlConfig {
c: Self::as_float(&table, "c"),
fastplay_budget: Self::as_float(&table, "fastplay_budget"),
fastplay_threshold: Self::as_float(&table, "fastplay_threshold"),
min_stones: Self::as_integer(&table, "min_stones"),
f_earlystop: Self::as_float(&table, "f_earlystop"),
min_stones: Self::as_float(&table, "min_stones"),
p_earlystop: Self::as_float(&table, "p_earlystop"),
vacant_point_scaling_factor: Self::as_float(&table, "vacant_point_scaling_factor"),
}
}
}
Expand Down
21 changes: 10 additions & 11 deletions src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ impl Engine {
self.config.log(format!("No moves to simulate!"));
return (Pass(color), self.root.playouts());
}
let stop = |win_ratio, _| { timer.ran_out_of_time(win_ratio) };
let stop = |playouts, plays_best, plays_second_best| {
timer.ran_out_of_time(playouts, plays_best, plays_second_best)
};
self.search(game, stop);
self.genmove_log();
let playouts = self.root.playouts();
Expand All @@ -148,15 +150,15 @@ impl Engine {
);
}

fn search<F>(&mut self, game: &Game, stop: F) where F: Fn(f32, usize) -> bool {
fn search<F>(&mut self, game: &Game, stop: F) where F: Fn(usize, usize, usize) -> bool {
self.send_new_state_to_workers(game);
let initial_playouts = self.root.playouts();
loop {
let win_ratio = {
let (best, _) = self.root.best();
best.win_ratio()
};
let (playouts_best, playouts_second_best) = self.root.best2_playouts();
let done = {
stop(win_ratio, self.root.playouts())
// So as to not pass in 0 as it will mess up the calculation
let playouts_run = self.root.playouts() - initial_playouts + 1;
stop(playouts_run, playouts_best, playouts_second_best)
};
if done { return; }
let r = self.receive_from_threads.recv();
Expand All @@ -175,10 +177,7 @@ impl Engine {
};
self.root = Node::root(game, color, self.config.clone());
}
let initial_playouts = self.root.playouts();
let stop = |_, current_playouts: usize| {
(current_playouts - initial_playouts) > playouts
};
let stop = |current_playouts, _, _| { current_playouts > playouts };
self.search(game, stop);
}

Expand Down
16 changes: 15 additions & 1 deletion src/engine/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,27 @@ impl Node {
for n in self.children.iter() {
if n.m().is_pass() {
pass = n;
} else if n.plays_with_prior_factor() > best.plays_with_prior_factor() {
} else if n.playouts > best.playouts {
best = n;
}
}
(best, pass)
}

pub fn best2_playouts(&self) -> (usize, usize) {
let mut most_playouts = 0;
let mut second_most_playouts = 0;
for n in &self.children {
if n.playouts > most_playouts {
second_most_playouts = most_playouts;
most_playouts = n.playouts;
} else if n.playouts > second_most_playouts {
second_most_playouts = n.playouts;
}
}
(most_playouts, second_most_playouts)
}

fn weighted_win(&self, score: &Score) -> f32 {
let weight = self.config.tree.score_weight;
(weight * score.adjusted()) + (1.0 - weight)
Expand Down
16 changes: 16 additions & 0 deletions src/engine/node/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,19 @@ fn remove_illegal_children_removes_superko_violations() {
node.remove_illegal_children(&game);
assert!(node.children.iter().all(|n| n.m() != Play(White, 2, 9)));
}

describe! best2_playouts {

it "returns the two highest play counts" {
let mut root = Node::new(Pass(Black), config().clone());
let child_template = Node::new(Play(White, 1, 1), config().clone());
let mut child1 = child_template.clone();
child1.playouts = 100;
let mut child2 = child_template.clone();
child2.playouts = 200;
let mut child3 = child_template.clone();
child3.playouts = 50;
root.children = vec!(child1, child2, child3);
assert_eq!((200,100), root.best2_playouts());
}
}
27 changes: 11 additions & 16 deletions src/timer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
use config::Config;
use game::Info;

use std::cmp::max;
use std::sync::Arc;
use time::Duration;
use time::PreciseTime;
Expand Down Expand Up @@ -86,16 +85,16 @@ impl Timer {
self.config.log(msg);
}

pub fn ran_out_of_time(&self, win_ratio: f32) -> bool {
let fastplay_budget = (1.0 / self.config.time_control.fastplay_budget).floor() as i32;
let budget5 = self.current_budget / fastplay_budget;
pub fn ran_out_of_time(&self, playouts: usize, playouts_best: usize, playouts_second_best: usize) -> bool {
let elapsed = self.elapsed();
if elapsed > budget5 && win_ratio > self.config.time_control.fastplay_threshold {
self.config.log(format!("Search stopped early. Fastplay rule triggered."));
true
} else {
elapsed > self.current_budget
if elapsed > self.current_budget {
return true;
}
let remaining = self.current_budget - elapsed;
let remaining_playouts = (playouts as f32 * remaining.num_milliseconds() as f32) / elapsed.num_milliseconds() as f32;
let scaled_remaining_playouts = remaining_playouts * self.config.time_control.p_earlystop;
let playout_difference = playouts_best - playouts_second_best;
scaled_remaining_playouts < playout_difference as f32
}

pub fn stop(&mut self) {
Expand Down Expand Up @@ -163,16 +162,12 @@ impl Timer {
}
}

fn c(&self) -> f32 {
self.config.time_control.c
}

fn budget<T: Info>(&self, game: &T) -> Duration {
// If there's still main time left
let ms = if self.main_time_left > 0 {
let min_stones = self.config.time_control.min_stones as u16;
let vacant = max(game.vacant_point_count(), min_stones) as f32;
(self.main_time_left as f32 / (self.c() * vacant)).floor() as i64
let min_stones = self.config.time_control.min_stones;
let vacant = game.vacant_point_count() as f32 * self.config.time_control.vacant_point_scaling_factor;
(self.main_time_left as f32 / vacant.max(min_stones)).floor() as i64
} else if self.byo_time_left() == 0 {
0
} else {
Expand Down
42 changes: 4 additions & 38 deletions src/timer/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,8 @@ describe! timer {

before_each {
let mut c = Config::test_config();
c.time_control.c = 0.5;
c.time_control.fastplay_budget = 0.05;
c.time_control.min_stones = 30;
c.time_control.min_stones = 30.0;
c.time_control.vacant_point_scaling_factor = 1.0;
let config = Arc::new(c);
let mut timer = Timer::new(config.clone());
}
Expand Down Expand Up @@ -186,13 +185,13 @@ describe! timer {
it "uses the vacant points to calculate during main time" {
timer.setup(100, 0, 0);
let game_info = &TestGameInfo::new(100);
assert_that!(timer.budget(game_info).num_milliseconds(), is(equal_to(2_000)));
assert_that!(timer.budget(game_info).num_milliseconds(), is(equal_to(1_000)));
}

it "uses a minimum of 30 points to calculate during main time" {
timer.setup(300, 0, 0);
let game_info = &TestGameInfo::new(10);
assert_that!(timer.budget(game_info).num_milliseconds(), is(equal_to(20_000)));
assert_that!(timer.budget(game_info).num_milliseconds(), is(equal_to(10_000)));
}
}

Expand Down Expand Up @@ -291,37 +290,4 @@ describe! timer {

}

describe! ran_out_of_time {

it "returns false if there's still time left" {
timer.current_budget = Duration::seconds(1);
assert!(!timer.ran_out_of_time(0.5));
}

it "returns true if the time is up" {
timer.current_budget = Duration::milliseconds(1);
sleep_ms(10);
assert!(timer.ran_out_of_time(0.5));
}

describe! over_5_percent_threshold {

before_each {
let win_ratio = config.time_control.fastplay_threshold + 0.01;
}

it "returns false if less than 5% of the time is up" {
timer.current_budget = Duration::seconds(1);
assert!(!timer.ran_out_of_time(win_ratio));
}

it "returns true if more than 5% of the time is up" {
timer.current_budget = Duration::milliseconds(100);
sleep_ms(10);
assert!(timer.ran_out_of_time(win_ratio));
}

}

}
}

0 comments on commit 460b2dc

Please sign in to comment.