Skip to content

Commit

Permalink
Add support for time controls in loadsgf/printsgf.
Browse files Browse the repository at this point in the history
Added extra support for "TM" and "OT" and other sgf time control
properties on printsgf and loadsgf GTP commands.

* Added parsing and loading of "TM" and "OT" sgf properties on GTP command
  loadsgf. Only supports "OT" syntax matching output from a printsgf GTP
  command.
* Change SGFTree to have a shared_ptr for a time control.
* Added saving and loading of "BL", "WL", "OB" and "OW" sgf properties on
  GTP commands printsgf and loadsgf.
* Change to make TimeControl::make_from_text_sgf() a time control factory
  and other minor tidying.

Pull request leela-zero#2172.
  • Loading branch information
Hersmunch authored and gcp committed Apr 2, 2019
1 parent cc11c03 commit 148f979
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 18 deletions.
4 changes: 4 additions & 0 deletions src/GameState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ const TimeControl& GameState::get_timecontrol() const {
return m_timecontrol;
}

void GameState::set_timecontrol(const TimeControl& timecontrol) {
m_timecontrol = timecontrol;
}

void GameState::set_timecontrol(int maintime, int byotime,
int byostones, int byoperiods) {
TimeControl timecontrol(maintime, byotime,
Expand Down
1 change: 1 addition & 0 deletions src/GameState.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class GameState : public KoState {
void start_clock(int color);
void stop_clock(int color);
const TimeControl& get_timecontrol() const;
void set_timecontrol(const TimeControl& timecontrol);
void set_timecontrol(int maintime, int byotime, int byostones,
int byoperiods);
void adjust_time(int color, int time, int stones);
Expand Down
50 changes: 38 additions & 12 deletions src/SGFTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ GameState SGFTree::follow_mainline_state(unsigned int movenum) const {
// sets up the game history.
GameState result(get_state());

if (m_timecontrol_ptr) {
result.set_timecontrol(*m_timecontrol_ptr);
}

for (unsigned int i = 0; i <= movenum && link != nullptr; i++) {
// root position has no associated move
if (i != 0) {
Expand Down Expand Up @@ -147,7 +151,7 @@ void SGFTree::populate_states() {
// board size
it = m_properties.find("SZ");
if (it != end(m_properties)) {
std::string size = it->second;
const auto size = it->second;
std::istringstream strm(size);
int bsize;
strm >> bsize;
Expand All @@ -163,13 +167,13 @@ void SGFTree::populate_states() {
// komi
it = m_properties.find("KM");
if (it != end(m_properties)) {
std::string foo = it->second;
const auto foo = it->second;
std::istringstream strm(foo);
float komi;
strm >> komi;
int handicap = m_state.get_handicap();
const auto handicap = m_state.get_handicap();
// last ditch effort: if no GM or SZ, assume 19x19 Go here
int bsize = 19;
auto bsize = 19;
if (valid_size) {
bsize = m_state.board.get_boardsize();
}
Expand All @@ -181,10 +185,31 @@ void SGFTree::populate_states() {
}
}

// time
it = m_properties.find("TM");
if (it != end(m_properties)) {
const auto maintime = it->second;
it = m_properties.find("OT");
const auto byoyomi = (it != end(m_properties)) ? it->second : "";
it = m_properties.find("BL");
const auto black_time_left = (it != end(m_properties)) ? it->second : "";
it = m_properties.find("WL");
const auto white_time_left = (it != end(m_properties)) ? it->second : "";
it = m_properties.find("OB");
const auto black_moves_left = (it != end(m_properties)) ? it->second : "";
it = m_properties.find("OW");
const auto white_moves_left = (it != end(m_properties)) ? it->second : "";
m_timecontrol_ptr = TimeControl::make_from_text_sgf(maintime, byoyomi,
black_time_left,
white_time_left,
black_moves_left,
white_moves_left);
}

// handicap
it = m_properties.find("HA");
if (it != end(m_properties)) {
std::string size = it->second;
const auto size = it->second;
std::istringstream strm(size);
float handicap;
strm >> handicap;
Expand All @@ -195,7 +220,7 @@ void SGFTree::populate_states() {
// result
it = m_properties.find("RE");
if (it != end(m_properties)) {
std::string result = it->second;
const auto result = it->second;
if (boost::algorithm::find_first(result, "Time")) {
// std::cerr << "Skipping: " << result << std::endl;
m_winner = FastBoard::EMPTY;
Expand Down Expand Up @@ -226,22 +251,22 @@ void SGFTree::populate_states() {
}
// Loop through the stone list and apply
for (auto pit = prop_pair_ab.first; pit != prop_pair_ab.second; ++pit) {
auto move = pit->second;
int vtx = string_to_vertex(move);
const auto move = pit->second;
const auto vtx = string_to_vertex(move);
apply_move(FastBoard::BLACK, vtx);
}

// XXX: count handicap stones
const auto& prop_pair_aw = m_properties.equal_range("AW");
for (auto pit = prop_pair_aw.first; pit != prop_pair_aw.second; ++pit) {
auto move = pit->second;
int vtx = string_to_vertex(move);
const auto move = pit->second;
const auto vtx = string_to_vertex(move);
apply_move(FastBoard::WHITE, vtx);
}

it = m_properties.find("PL");
if (it != end(m_properties)) {
std::string who = it->second;
const auto who = it->second;
if (who == "W") {
m_state.set_to_move(FastBoard::WHITE);
} else if (who == "B") {
Expand All @@ -256,7 +281,7 @@ void SGFTree::populate_states() {

// XXX: maybe move this to the recursive call
// get move for side to move
auto colored_move = child_state.get_colored_move();
const auto colored_move = child_state.get_colored_move();
if (colored_move.first != FastBoard::INVAL) {
child_state.apply_move(colored_move.first, colored_move.second);
}
Expand All @@ -268,6 +293,7 @@ void SGFTree::populate_states() {
void SGFTree::copy_state(const SGFTree& tree) {
m_initialized = tree.m_initialized;
m_state = tree.m_state;
m_timecontrol_ptr = tree.m_timecontrol_ptr;
}

void SGFTree::apply_move(int color, int move) {
Expand Down
2 changes: 2 additions & 0 deletions src/SGFTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "FastBoard.h"
#include "GameState.h"
#include "KoState.h"
#include "TimeControl.h"

class SGFTree {
public:
Expand Down Expand Up @@ -77,6 +78,7 @@ class SGFTree {

bool m_initialized{false};
KoState m_state;
std::shared_ptr<TimeControl> m_timecontrol_ptr;
FastBoard::vertex_t m_winner{FastBoard::INVAL};
std::vector<SGFTree> m_children;
PropertyMap m_properties;
Expand Down
80 changes: 75 additions & 5 deletions src/TimeControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@

#include "TimeControl.h"

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <regex>
#include <sstream>

#include "GTP.h"
#include "Timing.h"
Expand All @@ -49,24 +52,91 @@ TimeControl::TimeControl(int maintime, int byotime,
reset_clocks();
}

std::string TimeControl::stones_left_to_text_sgf(const int color) const {
auto s = std::string{};
// We must be in byo-yomi before interpreting stones.
if (m_inbyo[color]) {
const auto c = color == FastBoard::BLACK ? "OB[" : "OW[";
if (m_byostones) {
s += c + std::to_string(m_stones_left[color]) + "]";
} else if (m_byoperiods) {
// KGS extension.
s += c + std::to_string(m_periods_left[color]) + "]";
}
}
return s;
}

std::string TimeControl::to_text_sgf() const {
if (m_byotime != 0 && m_byostones == 0 && m_byoperiods == 0) {
return ""; // infinite
return ""; // Infinite time.
}
auto s = "TM[" + std::to_string(m_maintime/100) + "]";
auto s = "TM[" + std::to_string(m_maintime / 100) + "]";
if (m_byotime) {
if (m_byostones) {
s += "OT[" + std::to_string(m_byostones) + "/";
s += std::to_string(m_byotime/100) + " Canadian]";
s += std::to_string(m_byotime / 100) + " Canadian]";
} else {
assert(m_byoperiods);
s += "OT[" + std::to_string(m_byoperiods) + "x";
s += std::to_string(m_byotime/100) + " byo-yomi]";
s += std::to_string(m_byotime / 100) + " byo-yomi]";
}
s += stones_left_to_text_sgf(FastBoard::BLACK);
s += stones_left_to_text_sgf(FastBoard::WHITE);
}
// Generously round up to avoid a remaining time of 0 triggering byo-yomi
// to be started when the sgf is loaded. This happens because byo-yomi
// stones have to be only written to the sgf when actually in byo-yomi
// and this is interpreted in adjust_time() as a special case
// that starts byo-yomi.
const auto black_time_left = (m_remaining_time[FastBoard::BLACK] + 99) / 100;
const auto white_time_left = (m_remaining_time[FastBoard::WHITE] + 99) / 100;
s += "BL[" + std::to_string(black_time_left) + "]";
s += "WL[" + std::to_string(white_time_left) + "]";
return s;
}

std::shared_ptr<TimeControl> TimeControl::make_from_text_sgf(
const std::string& maintime, const std::string& byoyomi,
const std::string& black_time_left, const std::string& white_time_left,
const std::string& black_moves_left, const std::string& white_moves_left) {
const auto maintime_centis = std::stoi(maintime) * 100;
auto byotime = 0;
auto byostones = 0;
auto byoperiods = 0;
if (!byoyomi.empty()) {
std::smatch m;
const auto re_canadian = std::regex{"(\\d+)/(\\d+) Canadian"};
const auto re_byoyomi = std::regex{"(\\d+)x(\\d+) byo-yomi"};
if (std::regex_match(byoyomi, m, re_canadian)) {
byostones = std::stoi(m[1]);
byotime = std::stoi(m[2]) * 100;
} else if (std::regex_match(byoyomi, m, re_byoyomi)) {
byoperiods = std::stoi(m[1]);
byotime = std::stoi(m[2]) * 100;
} else {
// Unrecognised byo-yomi syntax.
}
}
const auto timecontrol_ptr = std::make_shared<TimeControl>(maintime_centis,
byotime,
byostones,
byoperiods);
if (!black_time_left.empty()) {
const auto time = std::stoi(black_time_left) * 100;
const auto stones = black_moves_left.empty() ?
0 : std::stoi(black_moves_left);
timecontrol_ptr->adjust_time(FastBoard::BLACK, time, stones);
}
if (!white_time_left.empty()) {
const auto time = std::stoi(white_time_left) * 100;
const auto stones = white_moves_left.empty() ?
0 : std::stoi(white_moves_left);
timecontrol_ptr->adjust_time(FastBoard::WHITE, time, stones);
}
return timecontrol_ptr;
}

void TimeControl::reset_clocks() {
m_remaining_time = {m_maintime, m_maintime};
m_stones_left = {m_byostones, m_byostones};
Expand Down
7 changes: 6 additions & 1 deletion src/TimeControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#define TIMECONTROL_H_INCLUDED

#include <array>
#include <memory>

#include "config.h"
#include "Timing.h"
Expand All @@ -53,8 +54,12 @@ class TimeControl {
bool can_accumulate_time(int color) const;
size_t opening_moves(int boardsize) const;
std::string to_text_sgf() const;

static std::shared_ptr<TimeControl> make_from_text_sgf(
const std::string& maintime, const std::string& byoyomi,
const std::string& black_time_left, const std::string& white_time_left,
const std::string& black_moves_left, const std::string& white_moves_left);
private:
std::string stones_left_to_text_sgf(const int color) const;
void display_color_time(int color);
int get_moves_expected(int boardsize, size_t movenum) const;

Expand Down

0 comments on commit 148f979

Please sign in to comment.