1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use time::precise_time_s;

use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};

#[derive(Clone)]
pub struct Clock {
    pub polling_nodes_count: u64,
    pub started_at: u64,
    moves_level: u16,
    moves_remaining: u16,
    time_remaining: u64,
    last_nodes_count: u64,
    is_finished: Arc<AtomicBool>,
    is_level: bool // TODO: find a better name
}

impl Clock {
    pub fn new(moves: u16, time: u64) -> Clock {
        Clock {
            polling_nodes_count: 100,
            started_at: 0,
            moves_level: moves,
            moves_remaining: moves,
            time_remaining: time,
            last_nodes_count: 0,
            is_finished: Arc::new(AtomicBool::new(false)),
            is_level: true
        }
    }

    pub fn start(&mut self, ply: usize) {
        self.is_finished.store(false, Ordering::Relaxed);
        self.last_nodes_count = 0;
        self.started_at = (precise_time_s() * 1000.0) as u64;

        // The UCI protocol gives the number of remaining moves before each
        // search but XBoard doesn't so we need to calculate it based on moves
        // history and the level command.
        if self.is_level {
            assert!(ply > 0);
            let moves_done = (((ply - 1) / 2) as u16) % self.moves_level;
            self.moves_remaining = self.moves_level - moves_done;
        }
    }

    pub fn stop(&mut self) {
        self.is_finished.store(true, Ordering::Relaxed);
    }

    pub fn disable_level(&mut self) {
        self.is_level = false;
    }

    pub fn set_time(&mut self, time: u64) {
        self.time_remaining = time;
    }

    pub fn allocated_time(&self) -> u64 {
        self.time_remaining / self.moves_remaining as u64
    }

    pub fn elapsed_time(&self) -> u64 {
        let now = (precise_time_s() * 1000.0) as u64;

        now - self.started_at
    }

    pub fn poll(&mut self, nodes_count: u64) -> bool {
        // We do the real computation only every `polling_nodes_count` nodes
        // TODO: do we need this?
        if nodes_count - self.last_nodes_count > self.polling_nodes_count {
            self.last_nodes_count = nodes_count;

            // A certain amount of time pass between two polls,
            // and after the end of the search.
            let time_between_polls = self.polling_nodes_count / 4;
            let time_to_play = 25;
            let delta = time_between_polls + time_to_play;

            if delta + self.elapsed_time() > self.allocated_time() {
                self.is_finished.store(true, Ordering::Relaxed);
            }
        }

        self.is_finished.load(Ordering::Relaxed)
    }
}