This repository has been archived on 2023-04-18. You can view files and clone it, but cannot push or open issues or pull requests.
iaj/TP2/run_many_games.pike
2023-02-16 20:35:32 +01:00

463 lines
14 KiB
Text

#!/usr/bin/env pike
// pike run_many_games.pike -f ./rand_player -s ./rand_player -v 1 -p 1 -l 6 -c 4
// "Options:\n"
// " -n, --number=NB_GAMES the number of games to play\n"
// " -l, --nbl=NB_LINES the number of lines on the board\n"
// " -c, --nbc=NB_COLS the number of cols on the board\n"
// " -f, --first=COMMAND_LINE\n"
// " -s, --second=COMMAND_LINE command lines to run the two engines with.\n\n"
// " -o, --outputdir=OUTPUT_DIRECTORY (default ouput is data)\n"
// " --help display this help and exit.\n"
// " -v, --verbose=LEVEL 1 - print moves, 2 and higher - draw boards.\n"
// " -p, --pause=SECONDS 1 - sleep(1), 2 and more - sleep(2 and more).\n";
#define DUMP_GTP_PIPES 0
class btp_server { // breakthrough text protocol server
int server_is_up;
private Stdio.File file_out; // reading without buffer (stream mode)
private Stdio.FILE file_in; // reading with buffer (line per line mode)
string command_line;
string|int engine_name; // just for fun
void create(string _command_line) {
file_out = Stdio.File();
file_in = Stdio.FILE();
command_line = _command_line;
array error = catch {
Process.create_process(command_line / " ",
([ "stdin" : file_out->pipe(),
"stdout" : file_in->pipe() ])); };
if (error) {
werror(error[0]); werror("Command line was `%s'.\n", command_line);
destruct(this_object());
} else {
array error = catch {
engine_name = get_name();
server_is_up = 1;
};
if (error) {
werror("Engine `%s' crashed at startup.\nPerhaps command line is wrong.\n", command_line);
destruct(this_object());
}
}
}
array send_command(string command) {
#if DUMP_GTP_PIPES
werror("[%s%s] %s\n", full_engine_name ? full_engine_name + ", " : "", command);
#endif
command = String.trim_all_whites(command);
sscanf(command, "%[0-9]", string id);
if (command[0] == '#' || command == id) return ({ 0, "" });
file_out->write("%s\n", command);
string response = file_in->gets();
if (!response) {
server_is_up = 0;
error("Engine `%s' playing crashed!", command_line);
}
#if DUMP_GTP_PIPES
werror("%s\n", response);
#endif
array result;
int id_length = strlen(id);
if (response && response[..id_length] == "=" + id)
result = ({ 0, response[id_length + 1 ..] });
else if (response && response[..id_length] == "?" + id)
result = ({ 1, response[id_length + 1 ..] });
else
result = ({ -1, response });
result[1] = String.trim_all_whites(result[1]);
while (1) {
response = file_in->gets();
#if DUMP_GTP_PIPES
werror("%s\n", response);
#endif
if (response == "") {
if (result[0] < 0) {
werror("Warning, unrecognized response to command `%s':\n", command);
werror("%s\n", result[1]);
}
return result;
}
result[1] += "\n" + response;
}
}
string get_name() {
return send_command("name")[1];
}
string generate_move() {
return send_command("genmove")[1];
}
void new_game(int _nbl, int _nbc) {
send_command("newgame "+_nbl+" "+_nbc);
}
void move(string _movestr) {
send_command("play " +_movestr);
}
void quit() {
send_command("quit");
}
};
#define ENDGAME_WHITE_WIN 0
#define ENDGAME_BLACK_WIN 0
#define ENDGAME_WHITE_WIN 0
class btp_game {
private btp_server p0;
private btp_server p1;
private int verbose;
private int in_game_pause;
public int nb_games;
public string p0_name;
public int p0_score;
public int p0_new_win;
public int p0_wins;
public string p1_name;
public int p1_score;
public int p1_new_win;
public int p1_wins;
public int nb_turn;
public int board_nbl = 0;
public int board_nbc = 0;
bool board_alloc = false;
public string board; // board length = board_nbl*board_nbc
float p0_remaining_time;
float p1_remaining_time;
public string output_dir = "data"; // default dir
void create(string command_line_player0, string command_line_player1,
int game_nbl, int game_nbc,
string new_output_dir, int _in_game_pause, int _verbose) {
in_game_pause = _in_game_pause;
verbose = _verbose;
p0 = btp_server(command_line_player0);
if (p0) p1 = btp_server(command_line_player1);
if (!p0 || !p1) {
werror("!p0 || !p1"); finalize(); exit(0);
}
board_nbl = game_nbl;
board_nbc = game_nbc;
nb_games = 0;
p0_name = command_line_player0; p0_new_win = 0; p0_wins = 0;
p1_name = command_line_player1; p1_new_win = 0; p1_wins = 0;
if(new_output_dir != "") {
output_dir = new_output_dir;
}
}
void show_endgame() {
print_board();
werror("(%s %d %.2f) (%s %d %.2f) ",
p0_name, p0_score, p0_remaining_time,
p1_name, p1_score, p1_remaining_time);
if(p0_new_win == 1) {
werror("=> "+p0_name+" win\n");
} else if(p1_new_win == 1) {
werror("=> "+p1_name+" win\n");
} else {
werror("=> draw game\n");
}
}
void print_score(string file_name) {
Stdio.File o = Stdio.File();
if(!o->open(file_name,"wac")) {
write("Failed to open file.\n");
return;
}
o->write(" (%s %d %.2f) (%s %d %.2f) ",
p0_name, p0_score, p0_remaining_time,
p1_name, p1_score, p1_remaining_time);
if(p0_new_win == 1) {
o->write("=> "+p0_name+" win\n");
} else if(p1_new_win == 1) {
o->write("=> "+p1_name+" win\n");
} else {
o->write("=> draw game\n");
}
o->close();
}
// @ is black player and o is white player
void init_board() {
nb_turn = 0;
p0_remaining_time = 24.0;
p1_remaining_time = 24.0;
if(board_alloc == false) {
for(int i = 0; i < board_nbl*board_nbc; i++)
board = board+".";
board_alloc = true;
} else {
for(int i = 0; i < board_nbl*board_nbc; i++)
board[i] = '.';
}
for(int i = 0; i < 2*board_nbc; i++)
board[i] = '@';
for(int i = (board_nbl-2)*board_nbc; i < board_nbl*board_nbc; i++)
board[i] = 'o';
}
void print_board() {
bool color_print = true;
if(color_print) {
werror("nb_turn: %d timers : \x1b[31m%.2f\x1b[0m : %.2f\n",
nb_turn, p0_remaining_time, p1_remaining_time);
} else {
werror("nb_turn: %d timers : %.2f : %.2f\n",
nb_turn, p0_remaining_time, p1_remaining_time);
}
for(int i = 0; i < board_nbl; i++) {
werror(""+(board_nbl-i)+" ");
for(int j = 0; j < board_nbc; j++) {
if(color_print) {
if(board[i*board_nbc+j] == '@') {
werror("\x1b[31m%c\x1b[0m ", board[i*board_nbc+j]);
} else {
werror("%c ",board[i*board_nbc+j]);
}
} else {
werror("%c ",board[i*board_nbc+j]);
}
}
werror("\n");
}
werror(" ");
for(int j = 0; j < board_nbc; j++)
werror("%c ", 'a'+j);
werror("\n");
}
bool play_move(string move) {
if(verbose >= 1) werror("==== play_move "+move+"\n");
if(move == "PASS") { nb_turn ++; return true; }
int strpos = 0;
int line_i = board_nbl-(move[0]-'0');
int col_i = move[1]-'a';
int line_f = board_nbl-(move[2]-'0');
int col_f = move[3]-'a';
if(verbose >= 2) werror("==== play at "+line_i+" "+col_i+" "+line_f+" "+col_f+"\n");
if(line_i < 0 || line_f < 0) return false;
if(line_i >= board_nbl || line_f >= board_nbl) return false;
if(col_i < 0 || col_f < 0) return false;
if(col_i >= board_nbc || col_f >= board_nbc) return false;
if(abs(line_f-line_i) > 1) return false;
if(abs(col_f-col_i) > 1) return false;
if(nb_turn%2==0) {
board[line_i*board_nbc+col_i] = '.';
board[line_f*board_nbc+col_f] = 'o';
} else {
board[line_i*board_nbc+col_i] = '.';
board[line_f*board_nbc+col_f] = '@';
}
nb_turn ++;
return true;
}
bool endgame() {
for(int i = 0; i < board_nbc; i++)
if(board[i] == 'o') return true;
for(int i = (board_nbl-1)*board_nbc; i < board_nbl*board_nbc; i++)
if(board[i] == '@') return true;
return false;
}
int count_pawn_on_board() {
int ret = 0;
for(int i = 0; i < board_nbl*board_nbc; i++) {
if(board[i] != '.') ret += 1;
}
return ret;
}
void play() {
if (verbose >= 2) werror("\nBeginning a new game.\n");
p0_new_win = 0;
p1_new_win = 0;
p0_score = 0;
p1_score = 0;
p0->new_game(board_nbl, board_nbc);
p1->new_game(board_nbl, board_nbc);
init_board();
// perform a match
string p0_move = "";
string p1_move = "";
while(true) {
if(verbose >= 1) print_board();
array(int) Ti = System.gettimeofday();
p0_move = p0->generate_move();
if(verbose >= 2) werror("P0_move received : "+p0_move+"\n");
array(int) Tf = System.gettimeofday();
float ms = (float)((Tf[0] - Ti[0]))+(float)(Tf[1] - Ti[1])/1000000;
p0_remaining_time -= ms;
if(p0_remaining_time < 0.0) {
p0_new_win = 0; p1_new_win = 1;
werror(" ===> "+p0_name+" time exceeded\n");
print_board();
werror(" ===> "+p1_name+" WIN\n");
break;
}
if(play_move(p0_move) == false) {
p0_new_win = 0; p1_new_win = 1;
werror(" ===> "+p0_name+" try to play "+p0_move+"\n");
print_board();
werror(" ===> "+p1_name+" WIN\n");
break;
} else {
if(verbose >= 2) {
werror("==== ok\n");
}
}
p1->move(p0_move);
if(endgame()) {
if(verbose >= 2) {
werror("=== endgame DETECTED\n");
print_board();
}
p0_new_win = 1;
p0_score = count_pawn_on_board();
p1_new_win = 0;
p1_score = -count_pawn_on_board();
break;
}
if(p0_move == "PASS" && p1_move == "PASS") {
if(verbose >= 2) werror("=== all players PASS DETECTED\n");
break;
}
if(verbose >= 2) print_board();
Ti = System.gettimeofday();
p1_move = p1->generate_move();
if(verbose >= 2) werror("P1_move received : "+p1_move+"\n");
Tf = System.gettimeofday();
ms = (float)((Tf[0] - Ti[0]))+(float)(Tf[1] - Ti[1])/1000000;
p1_remaining_time -= ms;
if(p1_remaining_time < 0.0) {
p1_new_win = 0; p0_new_win = 1;
werror(" ===> "+p1_name+" time exceeded\n");
print_board();
werror(" ===> "+p0_name+" WIN\n");
break;
}
if(play_move(p1_move) == false) {
p1_new_win = 0; p0_new_win = 1;
werror(" ===> "+p1_name+" try to play "+p1_move+"\n");
print_board();
werror(" ===> "+p0_name+" WIN\n");
break;
} else {
if(verbose >= 2) {
werror("==== ok\n");
}
}
p0->move(p1_move);
if(in_game_pause > 0)
sleep(in_game_pause);
if(endgame()) {
if(verbose >= 2) {
werror("=== endgame DETECTED\n");
print_board();
}
p1_new_win = 1;
p1_score = count_pawn_on_board();
p0_new_win = 0;
p0_score = -count_pawn_on_board();
break;
}
if(p0_move == "PASS" && p1_move == "PASS") {
if(verbose >= 2) werror("=== all players PASS DETECTED\n");
break;
}
}
}
void finalize() {
p0->quit(); p1->quit();
}
}
void run_many_games(btp_game game, int _nb_games_to_play, int verbose) {
game->nb_games = 0;
for (int k = 0; k < _nb_games_to_play; k++) {
game->play();
if(game->p0_new_win == 1) {
game->show_endgame();
werror("================= player1 WIN\n");
game->p0_wins ++;
}
if(game->p1_new_win == 1) {
game->show_endgame();
werror("================= player2 WIN\n");
game->p1_wins ++;
}
if(game->p0_new_win == 0 && game->p1_new_win == 0) {
werror("================= noone WIN\n");
}
game->nb_games ++;
game->print_score(game->output_dir+"/scores.txt");
}
game->finalize();
}
string help_message =
"Usage: %s [OPTION]... [FILE]...\n\n"
"Runs one or many matches between two programs text protocol engines.\n"
"`--white' and `--black' options are mandatory.\n\n"
"Options:\n"
" -n, --number=NB_GAMES the number of games to play\n"
" -l, --nbl=NB_LINES the number of lines on the board\n"
" -c, --nbc=NB_COLS the number of cols on the board\n"
" -f, --first=COMMAND_LINE\n"
" -s, --second=COMMAND_LINE command lines to run the two engines with.\n\n"
" -o, --outputdir=OUTPUT_DIRECTORY (default ouput is data)\n"
" --help display this help and exit.\n"
" -v, --verbose=LEVEL 1 - print moves, 2 and higher - draw boards.\n"
" -p, --pause=SECONDS 1 - sleep(1), 2 and more - sleep(2 and more).\n";
int main(int argc, array(string) argv) {
string hint = sprintf("Try `%s --help' for more information.\n",
basename(argv[0]));
if (Getopt.find_option(argv, UNDEFINED, "help")) {
write(help_message, basename(argv[0]));
return 0;
}
string str_p0 = Getopt.find_option(argv, "f", "first", UNDEFINED, "");
if (str_p0 == "") {
werror("First player is not specified.\n" + hint);
return 1;
}
string str_p1 = Getopt.find_option(argv, "s", "second", UNDEFINED, "");
if (str_p1 == "") {
werror("Second player is not specified.\n" + hint);
return 1;
}
string str_nb_games = Getopt.find_option(argv, "n", "games", UNDEFINED, "");
string str_nbl = Getopt.find_option(argv, "l", "nbl", UNDEFINED, "");
string str_nbc = Getopt.find_option(argv, "c", "nbc", UNDEFINED, "");
string str_output_dir = Getopt.find_option(argv, "o", "outputdir", UNDEFINED, "");
int verbose = (int) Getopt.find_option(argv, "v", "verbose", UNDEFINED, "0");
int in_game_pause = (int) Getopt.find_option(argv, "p", "pause", UNDEFINED, "0");
int game_nbl = 8; // default
int game_nbc = 8; // default
int nb_games = 1; // default
if (str_nbl != "") {
sscanf(str_nbl, "%d", game_nbl);
if(game_nbl <= 0) game_nbl = 8;
}
if (str_nbc != "") {
sscanf(str_nbc, "%d", game_nbc);
if(game_nbc <= 0) game_nbc = 8;
}
if (str_nb_games != "") {
sscanf(str_nb_games, "%d", nb_games);
if(nb_games <= 0) nb_games = 1;
}
btp_game game = btp_game(str_p0, str_p1, game_nbl, game_nbc, str_output_dir, in_game_pause, verbose);
if (game) {
run_many_games(game, nb_games, verbose);
}
return 0;
}