#! /bin/env pike /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * This program is distributed with GNU Go, a Go program. * * * * Contact address@hidden, or see http://www.gnu.org/software/gnugo/ * * for more information. * * * * Copyright 2003 by the Free Software Foundation. * * * * 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 - version 2 * * * * 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 in file COPYING for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program; if not, write to the Free * * Software Foundation, Inc., 59 Temple Place - Suite 330, * * Boston, MA 02111, USA. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* This script uses the same algorithm for optimizing move ordering parameters * as dfa_patterns_optimize_variations() in patterns/dfa.c uses for optimizing * DFAs. The significant drawback of the iterative algorithm for move ordering * tuning is slowness. However, there are no obvious workarounds for it. */ class GtpServer { private Stdio.File file_out; private Stdio.File pipe_out; private Stdio.FILE file_in; private Stdio.FILE pipe_in; void create(string command_line) { file_out = Stdio.File(); pipe_out = file_out->pipe(); file_in = Stdio.FILE(); pipe_in = file_in->pipe(); array state = catch { Process.create_process(command_line / " ", (["stdin": pipe_out, "stdout": pipe_in])); }; if (state) { werror(state[0]); werror("Command line was `%s'.\n", command_line); destruct(this); } } array(string) send_command(string command) { command = String.trim_all_whites(command); if (command == "" || command[0] == '#') return ({" ", ""}); string id = ""; sscanf(command, "%[0-9]", id); id += " "; int id_length = strlen(id); file_out->write(command + "\n"); string response = file_in->gets(); array(string) result; if (response[0 .. id_length] == "=" + id) result = ({"=", response[id_length + 1 ..]}); else if (response[0 .. id_length] == "?" + id) result = ({"?", response[id_length + 1 ..]}); else { werror("Warning: unexpected response `%s' to command `%s'\n", response, command); return ({" ", ""}); } while (1) { response = file_in->gets(); if (response == "") return result; result[1] += "\n" + response; } } int known_command(string command) { return `+(@send_command("known_command " + command)) == "=true"; } void reset_reading_node_counter() { send_command("reset_reading_node_counter"); } int get_reading_node_counter() { array(string) response = send_command("get_reading_node_counter"); if (response[0] != "=") return -1; return (int) response[1]; } array(int) get_move_ordering_parameters() { array(string) response = send_command("get_move_ordering_parameters"); if (response[0] != "=") return ({}); return (array(int)) (response[1] / " "); } void tune_move_ordering(array(int) parameters) { send_command("tune_move_ordering " + ((array(string)) parameters) * " "); } } static array(int) alter_move_ordering_parameters(array(int) initial_parameters) { int num_parameters = sizeof(initial_parameters); array(int) altered_parameters = copy_value(initial_parameters); for (int k = 0; k < num_parameters; k++) { if (random(num_parameters) < 6) altered_parameters[k] += random(5) - 2; } return altered_parameters; } static int optimize_move_ordering_parameters(GtpServer server, string gtp_file, string results_file, int iterations) { string sample_file_contents = Stdio.read_file(gtp_file); if (!sample_file_contents) { werror("Error: unable to read file `%s'\n", gtp_file); return 1; } array(int) current_parameters = server->get_move_ordering_parameters(); if (sizeof(current_parameters) == 0) { werror("Error: the server refuses to give its ordering parameters"); return 1; } string intermediate_results = Stdio.read_file(results_file); if (intermediate_results) { array(int) intermediate_parameters = map(array_sscanf(intermediate_results, "%{%d%}")[0], `[], 0); if (sizeof(intermediate_parameters) != sizeof(current_parameters)) { werror("Warning: contents of `%s' are out of date.\n" "Falling back on server's default parameters", results_file); } else { current_parameters = intermediate_parameters; server->tune_move_ordering(current_parameters); } } server->reset_reading_node_counter(); array(string) sample_commands = sample_file_contents / "\n"; foreach (sample_commands, string command) server->send_command(command); int initial_nodes = server->get_reading_node_counter(); int current_nodes = initial_nodes; write("Initial reading nodes: %d\n", initial_nodes); random_seed(time()); while (iterations--) { array(int) altered_parameters = alter_move_ordering_parameters(current_parameters); server->tune_move_ordering(altered_parameters); server->send_command("clear_cache"); server->reset_reading_node_counter(); foreach (sample_commands, string command) server->send_command(command); write("."); int this_iteration_nodes = server->get_reading_node_counter(); if (this_iteration_nodes < current_nodes) { write("\nOptimized: %d => %d nodes (%d iterations left)\n", current_nodes, this_iteration_nodes, iterations); current_nodes = this_iteration_nodes; current_parameters = altered_parameters; } } if (current_nodes < initial_nodes) { write("\nTotal optimization: %d => %d nodes (-%.2f%%)\n", initial_nodes, current_nodes, ((100.0 * (initial_nodes - current_nodes)) / initial_nodes)); string results = ((array(string)) current_parameters) * "\n" + "\n"; array state = catch { Stdio.write_file(results_file, results); }; if (state) { werror(state[0]); werror("Warning: writing results to stdout instead:\n"); write(results); } } return 0; } static string help_message = "Usage: moveordering [OPTION]... [ITERATIONS]\n\n" "Options:\n" " -f, --file=GTP_FILE GTP script file to use\n" " -h, --help display this help and exit\n" " -p, --program=PROGRAM full command line to start GTP server with\n" " -r, --results=FILE text file to read and write optimization results to\n\n" "Options have the following default values:\n" " --file=reading.tst\n" " --program=\"../interface/gnugo --mode gtp --quiet\"\n" " --results=moveordering.res\n\n" "If ITERATIONS is positive, the script will try to optimize move ordering\n" "parameters in the given number of iterations. If ITERATIONS is zero, or is\n" "omitted, the number of reading nodes with the current parameters will be\n" "printed.\n\n" "To ignore the current intermediate results, either delete the file they are\n" "stored in, or specify another (nonexistent) file with --results option.\n"; int main(int argc, array(string) argv) { if (Getopt.find_option(argv, "h", "help")) { werror(help_message); return 0; } string command_line = Getopt.find_option(argv, "p", "program", UNDEFINED, "../interface/gnugo --mode gtp --quiet"); string gtp_file = Getopt.find_option(argv, "f", "file", UNDEFINED, "reading.tst"); string results_file = Getopt.find_option(argv, "r", "results", UNDEFINED, "moveordering.res"); array(string) arguments = Getopt.get_args(argv); int iterations = 0; if (sizeof(arguments) > 2 || (sizeof(arguments) == 2 && (iterations = (int) arguments[1]) < 0)) { werror(help_message); return 1; } GtpServer server = GtpServer(command_line); if (!server) return 1; if (!server->known_command("tune_move_ordering") || !server->known_command("get_move_ordering_parameters") || !server->known_command("reset_reading_node_counter") || !server->known_command("get_reading_node_counter") || !server->known_command("clear_cache")) { werror("Error: cannot tune move ordering, GTP server doesn't support it\n"); return 1; } int exit_code = optimize_move_ordering_parameters(server, gtp_file, results_file, iterations); server->send_command("quit"); return exit_code; }