-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.cpp
More file actions
320 lines (294 loc) · 14.8 KB
/
main.cpp
File metadata and controls
320 lines (294 loc) · 14.8 KB
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/*
doko is a C++ doppelkopf program with an integrated UCT player.
Copyright (c) 2011-2016 Silvan Sievers
For questions, please write to: silvan.sievers@unibas.ch
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "options.h"
#include "session.h"
#include <csignal>
#include <cstdlib>
#include <fstream>
#include <limits>
#include <sstream>
#include <unistd.h>
using namespace std;
int get_peak_memory_in_kb() {
// On error, produces a warning on cerr and returns -1.
int memory_in_kb = -1;
ostringstream filename_stream;
filename_stream << "/proc/" << getpid() << "/status";
const char *filename = filename_stream.str().c_str();
ifstream procfile(filename);
string word;
while (procfile.good()) {
procfile >> word;
if (word == "VmPeak:") {
procfile >> memory_in_kb;
break;
}
// Skip to end of line.
procfile.ignore(numeric_limits<streamsize>::max(), '\n');
}
if (procfile.fail())
memory_in_kb = -1;
if (memory_in_kb == -1)
cerr << "warning: could not determine peak memory" << endl;
return memory_in_kb;
}
void print_peak_memory() {
cout << "peak memory: " << get_peak_memory_in_kb() << " KB" << endl;
}
void signal_handler(int signal_number) {
// See glibc manual: "Handlers That Terminate the Process"
static volatile sig_atomic_t handler_in_progress = 0;
if (handler_in_progress)
raise(signal_number);
handler_in_progress = 1;
print_peak_memory();
cout << "caught signal " << signal_number << " -- exiting" << endl;
signal(signal_number, SIG_DFL);
raise(signal_number);
}
void exit_handler(int, void *) {
print_peak_memory();
}
void register_event_handlers() {
// On exit or when receiving certain signals such as SIGINT (Ctrl-C),
// print the peak memory usage.
on_exit(exit_handler, 0);
signal(SIGABRT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGSEGV, signal_handler);
signal(SIGINT, signal_handler);
}
void print_help() {
cout << "Usage:";
cout << "--help,-h: print this help message" << endl;
cout << "--number,-n: number of games for the session, preferably a number divisible by 4 (if smaller than 4, compulsory solos are disabled)" << endl;
cout << "--no-solo: disable solo playing. Players with two queens of clubs automatically play a marriage. (default: false)" << endl;
cout << "--compulsory-solo: play with compulsory solo (default: false)" << endl;
cout << "--random,--r: use random cards for the whole session (default: false). if option is not set, you will be asked to input a card distribution manually (or to deal random cards) after each game";
cout << "--seed,--s: random seed for random cards dealing" << endl;
cout << "--announcing-version: 0 for the version in which a player asked for an announcement can choose between all the legal announcements and 1 for the version in which a player asked for an announcement only can opt to not announce or to announce the 'next' announcement for his team and then gets asked again immediately if he wants to make another announcement or not" << endl;
cout << "--players,--p: specify four player types from { uct, human, random } (default: uct random random random)" << endl;
cout << "--print-player-options: prints options that can be specified by using the --p*-options arguments of the program, then terminating" << endl;
cout << "--p0-options: specify options for player 0 (only if UCT player; see --print-player-options for available options)" << endl;
cout << "--p1-options: specify options for player 1 (only if UCT player; see --print-player-options for available options)" << endl;
cout << "--p2-options: specify options for player 2 (only if UCT player; see --print-player-options for available options)" << endl;
cout << "--p3-options: specify options for player 3 (only if UCT player; see --print-player-options for available options)" << endl;
cout << "--create-graph: create a .dot-file for each constructed UCT tree (default: false)" << endl;
cout << "--verbose,--v: display detailed output during play (default: false). recommended to enable if playing with human players" << endl;
cout << "--uct-verbose: display detailed output from UCT players and UCT algorithm (default: false). only relevant if there is at least one UCT player" << endl;
cout << "--debug,--d: display debug output in BeliefGameState (default: false)" << endl;
cout << "--uct-debug: display debug output in Uct (default: false)" << endl;
}
void print_player_options() {
string player_options = "(currently only a UCT player accepts options)\n\nversion:\n0 for an UCT algorithm with a number of simulations, each with a fixed card assignment and a number of rollouts per simulation, 1 for an UCT algorithm with a number of rollouts, each using a different card assignment\n\nscore points factor:\ninteger which score points get multiplyed by in order to obtain UCT rewards\n\nplayer's or team's points:\n0 for using player's point as an additional bias to the score points, 1 for using the player's team points\n\nplaying points divisor:\ninterger which the player's or the team points of the player get divided by before being added to the (modified) score points\n\nexploration:\ninteger used as exploration constant in the UCT formula\n\nrollouts:\ninteger setting the number of rollouts performed in a UCT search (either in total, or per simulation)\n\nsimulations:\ninteger setting the number of simulations performed in a UCT search, specify anything if using version 1 (do not leave empty though!)\n\nannouncements:\n0 to forbid the UCT player to do announcements, 1 to allow, 2 to allow but to forbid if all possible moves yield a negative reward\n\nWrong UCT formula:\n0 to use the correct UCT formula and 1 to use the total number of visits in the tree (i.e. the current number of rollout) rather than the number of total visits of the specific node for which the formula is calculated\n\nMC simulation:\n0 if no MC simulation should be carried on but all states encountered during a rollout should be added to the tree, i.e. more than one per rollout. 1 if a MC simulation should be carried on as soon as a leaf node was added to the tree, i.e. only one node is added to the tree per rollout\n\nAction selection:\n0 to choose the first successor when expanding the first node and use random action selection after a new node was inserted, 1 to also use random action selection when expanding the first node (rest same as 0), 2 to choose the first successor when expanding the first node and use heurstic guided action selection after a new node was inserted, 3 to use random action selection when expanding the first node and heristic guided action selection after a new node was inserted, 4 to use heuristic guided action whenever a successor needs to be chosen\n\n(defaults: 1 500 1 1 20000 1000 10 2 0 0 0)";
cout << player_options << endl;
}
int get_int_option(int argc, char *argv[], int &index) {
if (index + 1 >= argc) {
cerr << "Missing (integer) argument after " << argv[index] << endl;
exit(2);
}
int result = atoi(argv[index + 1]);
++index;
return result;
}
void parse_players_options(int argc, char *argv[], int &index, vector<int> &players_options) {
if (index + 11 >= argc) {
cerr << "Missing eleven (integer) arguments after " << argv[index] << endl;
exit(2);
}
players_options.reserve(11);
for (int j = 0; j < 11; ++j) {
int option = atoi(argv[index + 1 + j]);
players_options.push_back(option);
}
index += 11;
}
void check_uct_player_options(const vector<int> &player_options) {
if (player_options.size() != 11) {
cerr << "must specify 11 player options" << endl;
exit(2);
}
if (player_options[0] != 0 && player_options[0] != 1) {
cerr << "version must be set to 0 or 1" << endl;
exit(2);
}
if (player_options[1] < 1) {
cerr << "score points factor must be greater 0" << endl;
exit(2);
}
if (player_options[2] != 0 && player_options[2] != 1) {
cerr << "using player's or team's points must be set to 0 or 1" << endl;
exit(2);
}
if (player_options[3] < 1) {
cerr << "playing points divisor must be greater 0" << endl;
exit(2);
}
if (player_options[4] < 1) {
cerr << "exploration constant must be greater 0" << endl;
exit(2);
}
if (player_options[5] < 1) {
cerr << "number of rollouts must be greater 0" << endl;
exit(2);
}
if (player_options[6] < 1) {
cerr << "number of simulations must be greater 0" << endl;
exit(2);
}
if (player_options[7] != 0 && player_options[7] != 1 && player_options[7] != 2) {
cerr << "announcement option must be set to 0, 1 or 2" << endl;
exit(2);
}
if (player_options[8] != 0 && player_options[8] != 1) {
cerr << "using wrong UCT formula must be set to 0 or 1" << endl;
exit(2);
}
if (player_options[9] != 0 && player_options[9] != 1) {
cerr << "using MC simulation must be set to 0 or 1" << endl;
exit(2);
}
if (player_options[10] < 0 || player_options[10] > 4) {
cerr << "action selection must be in the interval [0,4]" << endl;
exit(2);
}
}
int main(int argc, char *argv[]) {
register_event_handlers();
int number = 1000;
bool no_solo = false;
bool compulsory_solo = false;
bool random = false;
int seed = 2012;
int announcing_version = 1;
vector<player_t> players_types;
vector<vector<int> > players_options(4);
bool create_graph = false;
bool verbose = false;
bool uct_verbose = false;
bool debug = false;
bool uct_debug = false;
// TODO: test if important command line arguments trigger errors as intended
// TODO: move parsing to Options? Or have its own class
// TODO: even catch more invalid command line options
for (int i = 1; i < argc; ++i) {
string arg = argv[i];
if (arg == "--help" || arg == "-h") {
print_help();
exit(0);
} else if (arg == "--print-player-options") {
print_player_options();
exit(0);
}
else if (arg == "--number" || arg == "-n") {
number = get_int_option(argc, argv, i);
if (number % 4 != 0) {
cerr << "number of games must be a multiple of 4" << endl;
exit(2);
}
} else if (arg == "--no-solo") {
no_solo = true;
} else if (arg == "--compulsory-solo") {
compulsory_solo = true;
} else if (arg == "--random" || arg == "-r") {
random = true;
} else if (arg == "--seed" || arg == "-s") {
seed = get_int_option(argc, argv, i);
} else if (arg == "--announcing-version") {
announcing_version = get_int_option(argc, argv, i);
} else if (arg == "--players" || arg == "-p") {
if (i + 4 >= argc) {
cerr << "Missing four arguments after " << argv[i] << endl;
exit(2);
}
players_types.reserve(4);
for (int j = 0; j < 4; ++j) {
string player_type(argv[i + 1 + j]);
if (player_type == "human") {
players_types.push_back(HUMAN);
} else if (player_type == "random") {
players_types.push_back(RANDOM);
} else if (player_type == "uct") {
players_types.push_back(UCT);
} else {
cerr << "Players types can be human, random or uct" << endl;
exit(2);
}
}
i += 4;
} else if (arg == "--p0-options") {
parse_players_options(argc, argv, i, players_options[0]);
} else if (arg == "--p1-options") {
parse_players_options(argc, argv, i, players_options[1]);
} else if (arg == "--p2-options") {
parse_players_options(argc, argv, i, players_options[2]);
} else if (arg == "--p3-options") {
parse_players_options(argc, argv, i, players_options[3]);
} else if (arg == "--create-graph") {
create_graph = true;
} else if (arg == "--verbose" || arg == "v") {
verbose = true;
} else if (arg == "--uct-verbose") {
uct_verbose = true;
} else if (arg == "--debug" || arg == "d") {
debug = true;
} else if (arg == "--uct-debug") {
uct_debug = true;
} else {
cerr << "Unrecognized option " << arg << endl;
exit(2);
}
}
// TODO: use better player options (ideally named)
if (players_types.empty()) {
// use default values
players_types.push_back(UCT);
players_types.push_back(RANDOM);
players_types.push_back(RANDOM);
players_types.push_back(RANDOM);
}
for (size_t i = 0; i < players_options.size(); ++i) {
if (players_types[i] != UCT && !players_options[i].empty()) {
cerr << "Specifying player options for player " << i
<< " only possible if it is a UCT player" << endl;
exit(2);
}
if (players_types[i] == UCT) {
if (players_options[i].empty()) {
// use default values
players_options[i].push_back(1);
players_options[i].push_back(500);
players_options[i].push_back(1);
players_options[i].push_back(1);
players_options[i].push_back(20000);
players_options[i].push_back(1000);
players_options[i].push_back(10);
players_options[i].push_back(2);
players_options[i].push_back(0);
players_options[i].push_back(0);
players_options[i].push_back(0);
} else {
check_uct_player_options(players_options[i]);
}
}
}
Options options(number, no_solo, compulsory_solo, players_types, random,
seed, verbose, uct_verbose, debug, uct_debug,
players_options, create_graph, announcing_version);
Session session(options);
return 0;
}