11 #include <sys/time.h>
\r
12 #include <sys/types.h>
\r
14 #include <sys/wait.h>
\r
22 bool pipe_logging = false;
\r
23 int only_opening = -1;
\r
24 FILE *pgn_log = NULL;
\r
25 FILE *match_log = NULL;
\r
34 void strip_newline(char *s)
\r
40 if (!s[i] || s[i] == '\r' || s[i] == '\n')
\r
48 void copy_result(char *to, char *from)
\r
52 for (i = 0; i < strlen(from); ++i)
\r
53 if (from[i] == '0' || from[i] == '1')
\r
55 strcpy(to, &from[i]);
\r
56 for (i = 0; i < strlen(to); ++i)
\r
69 #include <pthread.h>
\r
70 #include <semaphore.h>
\r
80 pthread_mutex_t the_lock;
\r
85 pthread_mutex_init(&the_lock, NULL);
\r
92 if (pthread_mutex_lock(&the_lock) == 0) // EBUSY??
\r
98 void MyLock::release()
\r
100 pthread_mutex_unlock(&the_lock);
\r
115 Settings() { count = 0; };
\r
116 bool read(char *filename);
\r
117 char * get(char *name);
\r
120 char map[20][2][1000];
\r
126 bool Settings::read(char *filename)
\r
132 printf("Reading settings file: %s\n", filename);
\r
134 f = fopen(filename, "r");
\r
137 printf("Couldn't open file.\n");
\r
143 fgets(line, 1000, f);
\r
146 strip_newline(line);
\r
147 sscanf(line, "%s", var);
\r
148 strcpy(map[count][0], var);
\r
149 strcpy(map[count][1], line + strlen(var) + 1);
\r
156 char * Settings::get(char *name)
\r
160 for (i = 0; i < count; ++i)
\r
161 if (!strcmp(map[i][0], name))
\r
163 printf("Setting not found: %s\n", name);
\r
178 virtual ~ChildProc();
\r
180 virtual void start(char *cmd_line);
\r
181 virtual void stop();
\r
182 void send(char *line);
\r
183 void receive(char *line, int size);
\r
186 char * get_cmd() { return cmd; }
\r
201 ChildProc::ChildProc()
\r
206 ChildProc::~ChildProc()
\r
211 void ChildProc::init()
\r
220 void ChildProc::start(char *cmd_line)
\r
223 char arg_buff[100][100];
\r
224 int args = 0, i = 0, j = 0;
\r
226 argv[0] = arg_buff[0];
\r
229 argv[args][i] = cmd_line[j];
\r
230 if (!argv[args][i] || argv[args][i] == ' ')
\r
234 argv[args] = arg_buff[args];
\r
235 i = -1; // will get incremented to 0
\r
248 if ((pid = fork()) == 0) // what to do with glock here?
\r
250 dup2(to_prog[0], 0);
\r
251 dup2(from_prog[1], 1);
\r
254 close(from_prog[0]);
\r
255 close(from_prog[1]);
\r
256 dup2(1, fileno(stderr)); // force stderr to the pipe
\r
257 execvp(argv[0], argv);
\r
263 printf("fork() failed, command line: %s\n", cmd_line);
\r
267 close(from_prog[1]);
\r
268 from = fdopen(from_prog[0], "r");
\r
269 to = fdopen(to_prog[1], "w");
\r
270 setbuf(from, NULL);
\r
275 strcpy(cmd, argv[0]);
\r
278 void ChildProc::stop()
\r
288 close(from_prog[0]);
\r
290 kill(pid, SIGTERM);
\r
291 waitpid(pid, &x, 0);
\r
295 void ChildProc::send(char *line)
\r
302 strcpy(temp, line);
\r
303 strip_newline(temp);
\r
305 fprintf(to, "%s\n", temp);
\r
308 printf("%s send: %s\n", cmd, temp);
\r
311 void ChildProc::receive(char *line, int size)
\r
321 fgets(line, size, from);
\r
323 strip_newline(line);
\r
325 printf("%s recv: %s\n", cmd, line);
\r
328 bool ChildProc::poll()
\r
335 FD_SET(from_prog[0], &myset);
\r
337 mytime.tv_usec = 0;
\r
339 r = select(from_prog[0] + 1, &myset, NULL, NULL, &mytime);
\r
341 return (r > 0) ? true : false;
\r
344 void ChildProc::flush()
\r
350 receive(line, 1000);
\r
363 class ChessEngine : private ChildProc
\r
366 void start(char *cmd_line, char *init_str_arg);
\r
369 void send_move(char *move);
\r
370 void get_move(char *move);
\r
371 bool is_game_over() { return game_over; }
\r
372 char * get_result() { return result; }
\r
377 char init_str[1000];
\r
380 void ChessEngine::start(char *cmd_line, char *init_str_arg)
\r
382 ChildProc::start(cmd_line);
\r
384 strcpy(init_str, init_str_arg);
\r
387 void ChessEngine::stop()
\r
393 void ChessEngine::new_game()
\r
397 // stop the engine and flush leftover output
\r
398 // only necessary when reusing the engine
\r
405 sprintf(temp, "%s\n", init_str);
\r
412 void ChessEngine::send_move(char *move)
\r
418 sprintf(s, "%s\n", move);
\r
422 void ChessEngine::get_move(char *move)
\r
428 while (poll()) // does the engine think the game is over?
\r
430 receive(line, 1000);
\r
431 if (strchr(line, '{'))
\r
434 copy_result(result, line);
\r
442 receive(line, 1000);
\r
448 if (strchr(line, '{'))
\r
451 copy_result(result, line);
\r
454 sscanf(line, "%s", tok);
\r
455 if (!strcmp(tok, "move"))
\r
457 sscanf(line, "move %s", move);
\r
472 const int max_work_items = 5000;
\r
473 const int max_game_ply = 400;
\r
477 volatile int status;
\r
483 char move[max_game_ply + 2][10];
\r
488 WorkItem *work_item[max_work_items];
\r
491 bool make_work_items(char *filename)
\r
498 printf("Creating work items from file: %s\n", filename);
\r
500 f = fopen(filename, "r");
\r
503 printf("Couldn't open file.\n");
\r
509 fgets(line, 1000, f);
\r
512 strip_newline(line);
\r
515 if (only_opening != -1 && openings != only_opening)
\r
520 w->opening_id = openings;
\r
521 strcpy(w->opening, line);
\r
522 w->white_engine = 1;
\r
523 work_item[work_items++] = w;
\r
527 w->opening_id = openings;
\r
528 strcpy(w->opening, line);
\r
529 w->white_engine = 2;
\r
530 work_item[work_items++] = w;
\r
533 printf("%d work items created.\n", work_items);
\r
537 void print_game(WorkItem *w)
\r
539 char white_name[100];
\r
540 char black_name[100];
\r
542 char short_result[100];
\r
544 int m, i, line_break;
\r
546 if (w->white_engine == 1)
\r
548 strcpy(white_name, settings.get("engine1"));
\r
549 strcpy(black_name, settings.get("engine2"));
\r
553 strcpy(white_name, settings.get("engine2"));
\r
554 strcpy(black_name, settings.get("engine1"));
\r
556 sprintf(line, "Opening: %d, %s vs. %s, result: %s\n",
\r
562 fprintf(match_log, line);
\r
565 strcpy(short_result, w->result);
\r
566 temp = strchr(short_result, ' ');
\r
569 fprintf(pgn_log, "[Round \"%d.%d\"]\n", w->opening_id, w->white_engine);
\r
570 fprintf(pgn_log, "[White \"%s\"]\n", white_name);
\r
571 fprintf(pgn_log, "[Black \"%s\"]\n", black_name);
\r
572 fprintf(pgn_log, "[Result \"%s\"]\n", short_result);
\r
573 fprintf(pgn_log, "\n");
\r
580 fprintf(pgn_log, "%s\n", w->result);
\r
583 fprintf(pgn_log, "%d. %s ", i, w->move[m++]);
\r
585 fprintf(pgn_log, "%s ", w->move[m++]);
\r
586 if (m > line_break + 8)
\r
588 fprintf(pgn_log, "\n");
\r
592 fprintf(pgn_log, "\n");
\r
598 void print_stats(int items)
\r
601 int win = 0, lose = 0, draw = 0;
\r
605 char elo_sign = '+';
\r
609 double percent_done;
\r
611 int eta_min, eta_sec;
\r
613 for (i = 0; i < items; ++i)
\r
615 if (work_item[i]->status != 2) // shouldn't happen
\r
617 if (strstr(work_item[i]->result, "1-0"))
\r
619 if (work_item[i]->white_engine == 1)
\r
624 if (strstr(work_item[i]->result, "0-1"))
\r
626 if (work_item[i]->white_engine == 1)
\r
631 if (strstr(work_item[i]->result, "1/2-1/2"))
\r
634 games = win + lose + draw;
\r
636 percent = (double)win + (0.5 * (double)draw);
\r
637 percent /= (double)games;
\r
638 if (percent > 0.99999)
\r
640 else if (percent < 0.00001)
\r
643 elo = -400.0 * log(1.0 / percent - 1.0) / log(10.0);
\r
649 elapsed_time = time(NULL) - start_time;
\r
650 sec = elapsed_time;
\r
654 spg = (double)(time(NULL) - start_time) / (double)games;
\r
655 percent_done = (double)games / (double)work_items;
\r
656 eta = (double)work_items - (double)games;
\r
658 eta_sec = (int)eta;
\r
659 eta_min = eta_sec / 60;
\r
660 eta_sec -= eta_min * 60;
\r
662 printf("%s vs. %s: +%d -%d =%d, %.2f%c, ELO: %c%d\n",
\r
663 settings.get("engine1"),
\r
664 settings.get("engine2"),
\r
666 (float)(percent * 100.0), '%',
\r
667 elo_sign, (int)elo);
\r
668 printf("%d games in %d:%02d, %.2f seconds/game, %.1f%c done, ETA: %d:%02d\n",
\r
669 games, min, sec, (float)spg,
\r
670 (float)(percent_done * 100.0), '%',
\r
684 void do_work(int id_arg);
\r
685 void do_item(WorkItem *w);
\r
688 void do_opening(WorkItem *w);
\r
689 void add_move(WorkItem *w, char *move);
\r
692 ChessEngine engine1, engine2;
\r
695 void WorkerThread::do_work(int id_arg)
\r
703 for (i = 0; i < work_items; ++i)
\r
704 if (work_item[i]->status == 0)
\r
706 if (i != work_items)
\r
707 work_item[i]->status = 1;
\r
710 if (i == work_items)
\r
712 do_item(work_item[i]);
\r
716 void WorkerThread::do_item(WorkItem *w)
\r
719 ChessEngine *etm; // engine to move
\r
720 ChessEngine *etw; // engine to wait
\r
723 w->printed = false;
\r
726 engine1.start(settings.get("cmd1"), settings.get("init1"));
\r
727 engine2.start(settings.get("cmd2"), settings.get("init2"));
\r
728 engine1.new_game();
\r
729 engine2.new_game();
\r
734 if ((w->moves & 1) == 1 && w->white_engine == 1)
\r
739 if ((w->moves & 1) == 0 && w->white_engine == 2)
\r
746 etm->get_move(move);
\r
747 if (etm->is_game_over())
\r
749 strcpy(w->result, etm->get_result());
\r
752 etw->send_move(move);
\r
754 if (w->moves >= max_game_ply)
\r
756 strcpy(w->result, "1/2-1/2 {Game too long}");
\r
769 void WorkerThread::do_opening(WorkItem *w)
\r
776 move[i] = w->opening[j];
\r
777 if (!move[i] || move[i] == ' ')
\r
780 engine1.send_move(move);
\r
781 engine2.send_move(move);
\r
785 if (!w->opening[j])
\r
792 void WorkerThread::add_move(WorkItem *w, char *move)
\r
794 strcpy(w->move[w->moves++], move);
\r
804 WorkerThread *worker[100];
\r
805 pthread_t *mythread[100];
\r
806 volatile int thread_count = 0;
\r
808 void * thread_proc(void *arg)
\r
813 id = thread_count++;
\r
815 worker[id] = new WorkerThread;
\r
816 worker[id]->do_work(id);
\r
818 pthread_exit(&mythread[id]);
\r
819 delete mythread[id]; // ?
\r
822 void start_threads(int count)
\r
827 printf("Starting threads...\n");
\r
828 for (i = 0; i < count; ++i)
\r
830 mythread[i] = new pthread_t;
\r
831 pthread_create(mythread[i], NULL, thread_proc, (void *)&x);
\r
833 printf("Started %d threads.\n", i);
\r
843 main(int argc, char *argv[])
\r
846 char settings_filename[100];
\r
849 start_time = time(NULL);
\r
850 strcpy(settings_filename, "settings.txt");
\r
856 if (!strcmp(argv[i], "-l"))
\r
858 pipe_logging = true;
\r
860 else if (!strcmp(argv[i], "-o"))
\r
862 only_opening = atoi(argv[++i]);
\r
864 else if (!strcmp(argv[i], "-t"))
\r
866 threads = atoi(argv[++i]);
\r
868 else if (argv[i][0] == '-')
\r
870 printf("Usage: shiai [options] [settings file]\n");
\r
872 printf(" -h : Display this screen\n");
\r
873 printf(" -l : Display I/O with engines (\"log\")\n");
\r
874 printf(" -o # : Run opening number #\n");
\r
875 printf(" -t # : Run with # threads\n");
\r
881 strcpy(settings_filename, argv[i]);
\r
886 if (!settings.read(settings_filename))
\r
888 if (!make_work_items(settings.get("openings")))
\r
891 pgn_log = fopen(settings.get("pgnlog"), "w");
\r
894 printf("Couldn't open %s for writing.\n", settings.get("pgnlog"));
\r
897 match_log = fopen(settings.get("matchlog"), "w");
\r
900 printf("Couldn't open %s for writing.\n", settings.get("matchlog"));
\r
904 if (threads <= 0 || threads > 16)
\r
906 start_threads(threads);
\r
907 for (i = 0; i < work_items; ++i)
\r
909 while (work_item[i]->status != 2)
\r
911 print_game(work_item[i]);
\r
912 print_stats(i + 1);
\r