1 //-----------------------------------------------------------------------------
2 /** @file pentobi_gtp/GtpEngine.cpp
3 @author Markus Enzenberger
4 @copyright GNU General Public License version 3 or later */
5 //-----------------------------------------------------------------------------
6
7 #include "GtpEngine.h"
8
9 #include <fstream>
10 #include "libboardgame_base/Writer.h"
11 #include "libpentobi_mcts/Util.h"
12
13 using libboardgame_base::Writer;
14 using libboardgame_gtp::Failure;
15 using libpentobi_base::Board;
16 using libpentobi_base::get_color_id;
17 using libpentobi_mcts::Float;
18
19 //-----------------------------------------------------------------------------
20
GtpEngine(Variant variant,unsigned level,bool use_book,const string & books_dir,unsigned nu_threads)21 GtpEngine::GtpEngine(
22 Variant variant, unsigned level, bool use_book,
23 const string& books_dir, unsigned nu_threads)
24 : libpentobi_gtp::GtpEngine(variant)
25 {
26 create_player(variant, level, books_dir, nu_threads);
27 get_mcts_player().set_use_book(use_book);
28 add("get_value", &GtpEngine::cmd_get_value);
29 add("name", &GtpEngine::cmd_name);
30 add("param", &GtpEngine::cmd_param);
31 add("move_values", &GtpEngine::cmd_move_values);
32 add("save_tree", &GtpEngine::cmd_save_tree);
33 add("selfplay", &GtpEngine::cmd_selfplay);
34 add("version", &GtpEngine::cmd_version);
35 }
36
37 GtpEngine::~GtpEngine() = default; // Non-inline to avoid GCC -Winline warning
38
cmd_get_value(Response & response)39 void GtpEngine::cmd_get_value(Response& response)
40 {
41 response << get_search().get_tree().get_root().get_value();
42 }
43
cmd_move_values(Response & response)44 void GtpEngine::cmd_move_values(Response& response)
45 {
46 auto children = get_search().get_tree().get_root_children();
47 if (children.empty())
48 return;
49 auto& bd = get_board();
50 vector<const Search::Node*> sorted_children;
51 sorted_children.reserve(children.size());
52 for (auto& i : children)
53 sorted_children.push_back(&i);
54 sort(sorted_children.begin(), sorted_children.end(), libpentobi_mcts::compare_node);
55 response << fixed;
56 for (auto node : sorted_children)
57 response << setprecision(0) << node->get_visit_count() << ' '
58 << setprecision(1) << node->get_value_count() << ' '
59 << setprecision(3) << node->get_value() << ' '
60 << bd.to_string(node->get_move(), true) << '\n';
61 }
62
cmd_name(Response & response)63 void GtpEngine::cmd_name(Response& response)
64 {
65 response.set("Pentobi");
66 }
67
cmd_save_tree(Arguments args)68 void GtpEngine::cmd_save_tree(Arguments args)
69 {
70 auto& search = get_search();
71 if (! search.get_last_history().is_valid())
72 throw Failure("no search tree");
73 ofstream out(args.get<string>());
74 libpentobi_mcts::dump_tree(out, search);
75 }
76
77 /** Let the engine play a number of games against itself.
78 This is more efficient than using twogtp if selfplay games are needed
79 because it has lower memory requirements (only one engine needed), process
80 switches between the engines are avoided and parts of the search tree can
81 be reused between moves of different players. */
cmd_selfplay(Arguments args)82 void GtpEngine::cmd_selfplay(Arguments args)
83 {
84 args.check_size(2);
85 auto nu_games = args.get<int>(0);
86 ofstream out(args.get<string>(1));
87 auto variant = get_board().get_variant();
88 auto variant_str = to_string(variant);
89 Board bd(variant);
90 auto& player = get_mcts_player();
91 ostringstream s;
92 for (int i = 0; i < nu_games; ++i)
93 {
94 s.str("");
95 Writer writer(s);
96 writer.set_indent(-1);
97 bd.init();
98 writer.begin_tree();
99 writer.begin_node();
100 writer.write_property("GM", variant_str);
101 writer.end_node();
102 while (! bd.is_game_over())
103 {
104 auto c = bd.get_effective_to_play();
105 auto mv = player.genmove(bd, c);
106 bd.play(c, mv);
107 writer.begin_node();
108 writer.write_property(get_color_id(variant, c),
109 bd.to_string(mv, false));
110 writer.end_node();
111 }
112 writer.end_tree();
113 out << s.str() << '\n';
114 }
115 }
116
cmd_param(Arguments args,Response & response)117 void GtpEngine::cmd_param(Arguments args, Response& response)
118 {
119 auto& p = get_mcts_player();
120 auto& s = get_search();
121 if (args.get_size() == 0)
122 response
123 << "avoid_symmetric_draw " << s.get_avoid_symmetric_draw() << '\n'
124 << "exploration_constant " << s.get_exploration_constant() << '\n'
125 << "fixed_simulations " << p.get_fixed_simulations() << '\n'
126 << "rave_child_max " << s.get_rave_child_max() << '\n'
127 << "rave_parent_max " << s.get_rave_parent_max() << '\n'
128 << "rave_weight " << s.get_rave_weight() << '\n'
129 << "reuse_subtree " << s.get_reuse_subtree() << '\n'
130 << "use_book " << p.get_use_book() << '\n';
131 else
132 {
133 args.check_size(2);
134 auto name = args.get(0);
135 if (name == "avoid_symmetric_draw")
136 s.set_avoid_symmetric_draw(args.get<bool>(1));
137 else if (name == "exploration_constant")
138 s.set_exploration_constant(args.get<Float>(1));
139 else if (name == "fixed_simulations")
140 p.set_fixed_simulations(args.get<Float>(1));
141 else if (name == "rave_child_max")
142 s.set_rave_child_max(args.get<Float>(1));
143 else if (name == "rave_parent_max")
144 s.set_rave_parent_max(args.get<Float>(1));
145 else if (name == "rave_weight")
146 s.set_rave_weight(args.get<Float>(1));
147 else if (name == "reuse_subtree")
148 s.set_reuse_subtree(args.get<bool>(1));
149 else if (name == "use_book")
150 p.set_use_book(args.get<bool>(1));
151 else
152 {
153 ostringstream msg;
154 msg << "unknown parameter '" << name << "'";
155 throw Failure(msg.str());
156 }
157 }
158 }
159
cmd_version(Response & response)160 void GtpEngine::cmd_version(Response& response)
161 {
162 string version;
163 #ifdef VERSION
164 version = VERSION;
165 #else
166 version = "unknown";
167 #endif
168 #ifdef LIBBOARDGAME_DEBUG
169 version.append(" (dbg)");
170 #endif
171 response.set(version);
172 }
173
create_player(Variant variant,unsigned level,const string & books_dir,unsigned nu_threads)174 void GtpEngine::create_player(Variant variant, unsigned level,
175 const string& books_dir, unsigned nu_threads)
176 {
177 auto max_level = level;
178 m_player = make_unique<Player>(variant, max_level, books_dir, nu_threads);
179 get_mcts_player().set_level(level);
180 set_player(*m_player);
181 }
182
get_mcts_player()183 Player& GtpEngine::get_mcts_player()
184 {
185 try
186 {
187 return dynamic_cast<Player&>(*m_player);
188 }
189 catch (const bad_cast&)
190 {
191 throw Failure("current player is not mcts player");
192 }
193 }
194
get_search()195 Search& GtpEngine::get_search()
196 {
197 return get_mcts_player().get_search();
198 }
199
use_cpu_time(bool enable)200 void GtpEngine::use_cpu_time(bool enable)
201 {
202 get_mcts_player().use_cpu_time(enable);
203 }
204
205 //-----------------------------------------------------------------------------
206