13 #include <sys/types.h>
23 bool pipe_logging = false;
24 int only_opening = -1;
26 FILE *match_log = NULL;
35 void strip_newline(char *s)
41 if (!s[i] || s[i] == '\r' || s[i] == '\n')
49 void copy_result(char *to, char *from)
53 for (i = 0; i < strlen(from); ++i)
54 if (from[i] == '0' || from[i] == '1')
57 for (i = 0; i < strlen(to); ++i)
71 #include <semaphore.h>
81 pthread_mutex_t the_lock;
86 pthread_mutex_init(&the_lock, NULL);
93 if (pthread_mutex_lock(&the_lock) == 0) // EBUSY??
99 void MyLock::release()
101 pthread_mutex_unlock(&the_lock);
116 Settings() { count = 0; };
117 bool read(char *filename);
118 char * get(char *name);
121 char map[20][2][1000];
127 bool Settings::read(char *filename)
133 printf("Reading settings file: %s\n", filename);
135 f = fopen(filename, "r");
138 printf("Couldn't open file.\n");
144 fgets(line, 1000, f);
148 sscanf(line, "%s", var);
149 strcpy(map[count][0], var);
150 strcpy(map[count][1], line + strlen(var) + 1);
157 char * Settings::get(char *name)
161 for (i = 0; i < count; ++i)
162 if (!strcmp(map[i][0], name))
164 printf("Setting not found: %s\n", name);
183 class ChessEngine : private ChildProc
186 void start(char *cmd_line, char *init_str_arg);
189 void send_move(char *move);
190 void get_move(char *move);
191 bool is_game_over() { return game_over; }
192 char * get_result() { return result; }
200 void ChessEngine::start(char *cmd_line, char *init_str_arg)
202 ChildProc::start(cmd_line);
204 strcpy(init_str, init_str_arg);
207 void ChessEngine::stop()
213 void ChessEngine::new_game()
217 // stop the engine and flush leftover output
218 // only necessary when reusing the engine
225 sprintf(temp, "%s\n", init_str);
232 void ChessEngine::send_move(char *move)
238 sprintf(s, "%s\n", move);
242 void ChessEngine::get_move(char *move)
248 while (poll()) // does the engine think the game is over?
251 if (strchr(line, '{'))
254 copy_result(result, line);
268 if (strchr(line, '{'))
271 copy_result(result, line);
274 sscanf(line, "%s", tok);
275 if (!strcmp(tok, "move"))
277 sscanf(line, "move %s", move);
292 const int max_work_items = 5000;
293 const int max_game_ply = 400;
303 char move[max_game_ply + 2][10];
308 WorkItem *work_item[max_work_items];
311 bool make_work_items(char *filename)
318 printf("Creating work items from file: %s\n", filename);
320 f = fopen(filename, "r");
323 printf("Couldn't open file.\n");
329 fgets(line, 1000, f);
335 if (only_opening != -1 && openings != only_opening)
340 w->opening_id = openings;
341 strcpy(w->opening, line);
343 work_item[work_items++] = w;
347 w->opening_id = openings;
348 strcpy(w->opening, line);
350 work_item[work_items++] = w;
353 printf("%d work items created.\n", work_items);
357 void print_game(WorkItem *w)
359 char white_name[100];
360 char black_name[100];
362 char short_result[100];
364 int m, i, line_break;
366 if (w->white_engine == 1)
368 strcpy(white_name, settings.get("engine1"));
369 strcpy(black_name, settings.get("engine2"));
373 strcpy(white_name, settings.get("engine2"));
374 strcpy(black_name, settings.get("engine1"));
376 sprintf(line, "Opening: %d, %s vs. %s, result: %s\n",
382 fprintf(match_log, line);
385 strcpy(short_result, w->result);
386 temp = strchr(short_result, ' ');
389 fprintf(pgn_log, "[Round \"%d.%d\"]\n", w->opening_id, w->white_engine);
390 fprintf(pgn_log, "[White \"%s\"]\n", white_name);
391 fprintf(pgn_log, "[Black \"%s\"]\n", black_name);
392 fprintf(pgn_log, "[Result \"%s\"]\n", short_result);
393 fprintf(pgn_log, "\n");
400 fprintf(pgn_log, "%s\n", w->result);
403 fprintf(pgn_log, "%d. %s ", i, w->move[m++]);
405 fprintf(pgn_log, "%s ", w->move[m++]);
406 if (m > line_break + 8)
408 fprintf(pgn_log, "\n");
412 fprintf(pgn_log, "\n");
418 void print_stats(int items)
421 int win = 0, lose = 0, draw = 0;
431 int eta_min, eta_sec;
433 for (i = 0; i < items; ++i)
435 if (work_item[i]->status != 2) // shouldn't happen
437 if (strstr(work_item[i]->result, "1-0"))
439 if (work_item[i]->white_engine == 1)
444 if (strstr(work_item[i]->result, "0-1"))
446 if (work_item[i]->white_engine == 1)
451 if (strstr(work_item[i]->result, "1/2-1/2"))
454 games = win + lose + draw;
456 percent = (double)win + (0.5 * (double)draw);
457 percent /= (double)games;
458 if (percent > 0.99999)
460 else if (percent < 0.00001)
463 elo = -400.0 * log(1.0 / percent - 1.0) / log(10.0);
469 elapsed_time = time(NULL) - start_time;
474 spg = (double)(time(NULL) - start_time) / (double)games;
475 percent_done = (double)games / (double)work_items;
476 eta = (double)work_items - (double)games;
479 eta_min = eta_sec / 60;
480 eta_sec -= eta_min * 60;
482 printf("%s vs. %s: +%d -%d =%d, %.2f%c, ELO: %c%d\n",
483 settings.get("engine1"),
484 settings.get("engine2"),
486 (float)(percent * 100.0), '%',
488 printf("%d games in %d:%02d, %.2f seconds/game, %.1f%c done, ETA: %d:%02d\n",
489 games, min, sec, (float)spg,
490 (float)(percent_done * 100.0), '%',
504 void do_work(int id_arg);
505 void do_item(WorkItem *w);
508 void do_opening(WorkItem *w);
509 void add_move(WorkItem *w, char *move);
512 ChessEngine engine1, engine2;
515 void WorkerThread::do_work(int id_arg)
523 for (i = 0; i < work_items; ++i)
524 if (work_item[i]->status == 0)
527 work_item[i]->status = 1;
532 do_item(work_item[i]);
536 void WorkerThread::do_item(WorkItem *w)
539 ChessEngine *etm; // engine to move
540 ChessEngine *etw; // engine to wait
546 engine1.start(settings.get("cmd1"), settings.get("init1"));
547 engine2.start(settings.get("cmd2"), settings.get("init2"));
554 if ((w->moves & 1) == 1 && w->white_engine == 1)
559 if ((w->moves & 1) == 0 && w->white_engine == 2)
567 if (etm->is_game_over())
569 strcpy(w->result, etm->get_result());
572 etw->send_move(move);
574 if (w->moves >= max_game_ply)
576 strcpy(w->result, "1/2-1/2 {Game too long}");
589 void WorkerThread::do_opening(WorkItem *w)
596 move[i] = w->opening[j];
597 if (!move[i] || move[i] == ' ')
600 engine1.send_move(move);
601 engine2.send_move(move);
612 void WorkerThread::add_move(WorkItem *w, char *move)
614 strcpy(w->move[w->moves++], move);
624 WorkerThread *worker[100];
625 pthread_t *mythread[100];
626 volatile int thread_count = 0;
628 void * thread_proc(void *arg)
635 worker[id] = new WorkerThread;
636 worker[id]->do_work(id);
638 pthread_exit(&mythread[id]);
639 delete mythread[id]; // ?
643 void start_threads(int count)
648 printf("Starting threads...\n");
649 for (i = 0; i < count; ++i)
651 mythread[i] = new pthread_t;
652 pthread_create(mythread[i], NULL, thread_proc, (void *)&x);
654 printf("Started %d threads.\n", i);
664 main(int argc, char *argv[])
667 char settings_filename[100];
670 start_time = time(NULL);
671 strcpy(settings_filename, "settings.txt");
677 if (!strcmp(argv[i], "-l"))
681 else if (!strcmp(argv[i], "-o"))
683 only_opening = atoi(argv[++i]);
685 else if (!strcmp(argv[i], "-t"))
687 threads = atoi(argv[++i]);
689 else if (argv[i][0] == '-')
691 printf("Usage: shiai [options] [settings file]\n");
693 printf(" -h : Display this screen\n");
694 printf(" -l : Display I/O with engines (\"log\")\n");
695 printf(" -o # : Run opening number #\n");
696 printf(" -t # : Run with # threads\n");
702 strcpy(settings_filename, argv[i]);
707 if (!settings.read(settings_filename))
709 if (!make_work_items(settings.get("openings")))
713 pgn_log = fopen(settings.get("pgnlog"), "w");
716 printf("Couldn't open %s for writing.\n", settings.get("pgnlog"));
720 match_log = fopen(settings.get("matchlog"), "w");
723 printf("Couldn't open %s for writing.\n", settings.get("matchlog"));
727 if (threads <= 0 || threads > 16)
729 start_threads(threads);
730 for (i = 0; i < work_items; ++i)
732 while (work_item[i]->status != 2)
734 print_game(work_item[i]);