]> git.sesse.net Git - stupid/blob - stupid.c
Initial checkin for move to Git (no prior version history available).
[stupid] / stupid.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <time.h>
4 #include <sys/time.h>
5 #include <ctype.h>
6 #include <math.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <stdbool.h>
10 #include <assert.h>
11 #include <unistd.h>
12 #include <stdarg.h>
13 #include <signal.h>
14
15 #include "common.h"
16 #include "hash.h"
17 #include "evaluator.h"
18         
19 FILE *ucilog;
20
21 unsigned total_nodes, min_depth;
22 unsigned move_num;
23
24 struct killer_move killer_moves[64][2];  // two for each depth
25 unsigned killer_currdepth;               // easier to have as a global :-/
26 unsigned max_depth, max_selective_depth; // my oh my
27 bool abort_search;
28
29 int find_best_move(const struct position * const pos, unsigned long long phash, unsigned *ret_from, unsigned *ret_to, unsigned depth, unsigned seldepth, int alpha, int beta, bool search_castling);
30
31 void uciprintf(const char *format, ...)
32 {
33         va_list ap;
34         char buf[4096];
35
36         va_start(ap, format);
37         vsnprintf(buf, 4096, format, ap);
38         va_end(ap);
39
40         fputs(buf, stdout);
41         fputs("=> ", ucilog);
42         fputs(buf, ucilog);
43
44         fflush(stdout);
45         fflush(ucilog);
46 }
47
48 int estimate_score(struct position *pos, unsigned depth)
49 {
50         unsigned long long phash, bucket;
51         struct hash_entry *he;
52
53         // hack -- looking up the hash table is too expensive just to get
54         // a good estimate, so we don't do it except at a certain level
55         if (depth > 3) {
56                 bool found_any = false;
57                 unsigned best_depth = 10000;
58                 int best_depth_score;
59
60                 // see if we have something in the hash -- we don't really care at what
61                 // depth, _anything_ is better than the static evaluation, really
62                 phash = hash_position(pos);
63                 bucket = phash % HASH_BUCKETS;
64                 he = transposition_table[bucket];
65
66                 while (he != NULL) {
67                         if (he->phash == phash)
68                                 return he->score;
69
70                         if (he->phash == phash && he->depth < best_depth) {
71                                 found_any = true;
72                                 best_depth = he->depth;
73                                 best_depth_score = he->score;
74                         }
75                         he = he->next;
76                 }
77
78                 if (found_any)
79                         return best_depth_score;
80         }
81
82         return (pos->white_to_move) ? evaluate(pos) : -evaluate(pos);
83 }
84
85 #define ADD_MOVE(newsquare)                                                                             \
86         do {                                                                                            \
87                 assert(IS_WHITE(pos->board[square]) == pos->white_to_move);                             \
88                 moves[num_moves].pos = *pos;                                                            \
89                 moves[num_moves].from = square;                                                         \
90                 moves[num_moves].to = newsquare;                                                        \
91                 if (pos->white_to_move)                                                                 \
92                         real_make_move(&moves[num_moves].pos, square, newsquare, &moves[num_moves].pos.white_pieces[i], moves[num_moves].pos.black_pieces);     \
93                 else                                                                                    \
94                         real_make_move(&moves[num_moves].pos, square, newsquare, &moves[num_moves].pos.black_pieces[i], moves[num_moves].pos.white_pieces);     \
95                 moves[num_moves].estimate_score = -estimate_score(&moves[num_moves].pos, depth);        \
96                 indexes[num_moves] = num_moves;                                                         \
97                 heapify(moves, indexes, num_moves);                                                     \
98                 ++num_moves;                                                                            \
99         } while (0)
100
101 struct binheap_node {
102         int estimate_score;
103         struct position pos;
104         unsigned char from, to;
105 };
106
107 bool bh_greaterthan(struct binheap_node *moves, unsigned *indexes, unsigned i1, unsigned i2)
108 {
109         bool killer1 = (killer_moves[killer_currdepth][0].from == moves[indexes[i1]].from &&
110                         killer_moves[killer_currdepth][0].to   == moves[indexes[i1]].to) ||
111                        (killer_moves[killer_currdepth][1].from == moves[indexes[i1]].from &&
112                         killer_moves[killer_currdepth][1].to   == moves[indexes[i1]].to);
113         bool killer2 = (killer_moves[killer_currdepth][0].from == moves[indexes[i2]].from &&
114                         killer_moves[killer_currdepth][0].to   == moves[indexes[i2]].to) ||
115                        (killer_moves[killer_currdepth][1].from == moves[indexes[i2]].from &&
116                         killer_moves[killer_currdepth][1].to   == moves[indexes[i2]].to);
117
118         if (killer1 && !killer2)
119                 return true;
120         if (killer2 && !killer1)
121                 return false;
122
123         return (moves[indexes[i1]].estimate_score > moves[indexes[i2]].estimate_score);
124 }
125
126 void heapify(struct binheap_node *moves, unsigned *indexes, unsigned i)
127 {
128         unsigned parent;
129
130         if (i == 0)
131                 return;
132
133         parent = (i-1)/2;
134         if (bh_greaterthan(moves, indexes, i, parent)) {
135                 unsigned tmp = indexes[i];
136                 indexes[i] = indexes[parent];
137                 indexes[parent] = tmp;
138
139                 heapify(moves, indexes, parent);
140         }
141 }
142
143 void downheapify(struct binheap_node *moves, unsigned *indexes, unsigned i, unsigned num_elem)
144 {
145         unsigned child1 = 2 * i + 1;
146         unsigned child2 = 2 * i + 2;
147         unsigned compare_ind;
148
149         // no children
150         if (child1 >= num_elem)
151                 return;
152         
153         if (child2 >= num_elem) {
154                 // one child
155                 compare_ind = child1;
156         } else {
157                 // two children -- find the largest one
158                 if (bh_greaterthan(moves, indexes, child1, child2)) {
159                         compare_ind = child1;
160                 } else {
161                         compare_ind = child2;
162                 }
163         }
164         
165         // see if we should swap
166         if (bh_greaterthan(moves, indexes, compare_ind, i)) {
167                 unsigned tmp = indexes[i];
168                 indexes[i] = indexes[compare_ind];
169                 indexes[compare_ind] = tmp;
170
171                 downheapify(moves, indexes, compare_ind, num_elem);
172         }
173 }
174
175 // generate all possible moves, stick them in a binary heap based on estimated score, and then try
176 // them in the estimated order
177 int find_best_move(const struct position * const pos, unsigned long long phash, unsigned *ret_from, unsigned *ret_to, unsigned depth, unsigned seldepth, int alpha, int beta, bool search_castling)
178 {
179         unsigned i;
180         const struct piece * const ptr = (pos->white_to_move) ? pos->white_pieces : pos->black_pieces;
181         int score;
182         unsigned best_from = 21, best_to = 21;  // a1-a1 (easy to pick out visually)
183
184         struct binheap_node moves[128];   // should be plenty?
185         unsigned indexes[128];            // much cheaper to move around
186         unsigned num_moves = 0;
187
188         assert(depth > 0);
189
190         if (abort_search)
191                 return 0;
192         if (depth + seldepth < min_depth)
193                 min_depth = depth + seldepth;
194
195         // see if we can find this position in our hash tables
196         if (lookup_hash(pos, phash, depth, seldepth, ret_from, ret_to, &score)) {
197                 return score;
198         }
199
200         killer_currdepth = depth + seldepth;
201
202         for (i = 0; i < 16; ++i) {
203                 unsigned piece, type, square;
204
205                 type = ptr[i].type;           // without color
206                 if (type == PIECE_EMPTY)
207                         continue;
208         
209                 square = ptr[i].square;
210                 piece = pos->board[square];   // with color
211
212                 if (type == PIECE_PAWN) {
213                         int pawn_step = IS_WHITE(piece) ? 10 : -10;
214
215                         // could it move forwards one step?
216                         if (pos->board[ptr[i].square + pawn_step] == PIECE_EMPTY) {
217                                 ADD_MOVE(square + pawn_step);
218                         
219                                 // so that's OK, what about two?
220                                 if (pos->board[square + pawn_step * 2] == PIECE_EMPTY &&
221                                     ((IS_WHITE(piece) && NUM_TO_RANK(square) == 1) ||
222                                      (IS_BLACK(piece) && NUM_TO_RANK(square) == 6))) {
223                                         ADD_MOVE(square + pawn_step * 2);
224                                 }
225                         }
226
227                         // could it capture left?
228                         if ((pos->board[square + pawn_step - 1] == PIECE_EMPTY && (square + pawn_step - 1 == pos->ep_square)) ||
229                             (pos->board[square + pawn_step - 1] != PIECE_EMPTY && NUM_TO_FILE(square) != 0 && OPPOSITE_COLOR(piece, pos->board[square + pawn_step - 1]))) {
230                                 ADD_MOVE(square + pawn_step - 1);
231                         }
232
233                         // could it capture right?
234                         if ((pos->board[square + pawn_step + 1] == PIECE_EMPTY && (square + pawn_step + 1 == pos->ep_square)) ||
235                             (pos->board[square + pawn_step + 1] != PIECE_EMPTY && NUM_TO_FILE(square) != 7 && OPPOSITE_COLOR(piece, pos->board[square + pawn_step + 1]))) {
236                                 ADD_MOVE(square + pawn_step + 1);
237                         }
238
239                         continue;
240                 }
241
242                 if (type == PIECE_KNIGHT) {
243                         // these are all the possible knight moves
244                         if (CAN_GOTO(piece, pos->board[square + 12]))
245                                 ADD_MOVE(square + 12);
246                         if (CAN_GOTO(piece, pos->board[square - 12]))
247                                 ADD_MOVE(square - 12);
248                         if (CAN_GOTO(piece, pos->board[square - 21]))
249                                 ADD_MOVE(square - 21);
250                         if (CAN_GOTO(piece, pos->board[square + 21]))
251                                 ADD_MOVE(square + 21);
252                         if (CAN_GOTO(piece, pos->board[square + 19]))
253                                 ADD_MOVE(square + 19);
254                         if (CAN_GOTO(piece, pos->board[square - 19]))
255                                 ADD_MOVE(square - 19);
256                         if (CAN_GOTO(piece, pos->board[square + 8]))
257                                 ADD_MOVE(square + 8);
258                         if (CAN_GOTO(piece, pos->board[square - 8]))
259                                 ADD_MOVE(square - 8);
260
261                         continue;
262                 }
263
264                 if (type == PIECE_KING) {
265                         // these are all the possible king moves
266                         if (CAN_GOTO(piece, pos->board[square + 1]))
267                                 ADD_MOVE(square + 1);
268                         if (CAN_GOTO(piece, pos->board[square - 1]))
269                                 ADD_MOVE(square - 1);
270                         if (CAN_GOTO(piece, pos->board[square + 11]))
271                                 ADD_MOVE(square + 11);
272                         if (CAN_GOTO(piece, pos->board[square - 11]))
273                                 ADD_MOVE(square - 11);
274                         if (CAN_GOTO(piece, pos->board[square + 10]))
275                                 ADD_MOVE(square + 10);
276                         if (CAN_GOTO(piece, pos->board[square - 10]))
277                                 ADD_MOVE(square - 10);
278                         if (CAN_GOTO(piece, pos->board[square + 9]))
279                                 ADD_MOVE(square + 9);
280                         if (CAN_GOTO(piece, pos->board[square - 9]))
281                                 ADD_MOVE(square - 9);
282
283                         // evaluate if white can castle or not; quite ugly, but OK
284                         if (search_castling) {
285                                 bool c_short, c_long;
286                                 if (pos->white_to_move) {
287                                         c_short = pos->wc_short;
288                                         c_long = pos->wc_long;
289                                 } else {
290                                         c_short = pos->bc_short;
291                                         c_long = pos->bc_long;
292                                 }
293
294                                 if (pos->board[square + 1] != PIECE_EMPTY ||
295                                     pos->board[square + 2] != PIECE_EMPTY)
296                                         c_short = false;
297                                 if (pos->board[square - 1] != PIECE_EMPTY ||
298                                     pos->board[square - 2] != PIECE_EMPTY ||
299                                     pos->board[square - 3] != PIECE_EMPTY ||
300                                     pos->board[square - 4] != PIECE_EMPTY)
301                                         c_long = false;
302
303                                 if (c_short || c_long) {
304                                         struct position npos = *pos;
305                                         npos.white_to_move = !npos.white_to_move;
306                                         npos.ep_square = 0;
307
308                                         // score -20000 => our king is in check
309                                         score = find_best_move(&npos, hash_position(&npos), NULL, NULL, 1, 0, -40000, 40000, false);
310
311                                         if (score >= 14500)
312                                                 continue;
313
314                                         if (c_short) {
315                                                 // check if the middle square is in check
316                                                 npos = *pos;
317                                                 if (pos->white_to_move)
318                                                         real_make_move(&npos, square, square + 1, &npos.white_pieces[i], npos.black_pieces);
319                                                 else
320                                                         real_make_move(&npos, square, square + 1, &npos.black_pieces[i], npos.white_pieces);
321                                                 score = find_best_move(&npos, hash_position(&npos), NULL, NULL, 1, 0, -40000, 40000, false);
322
323                                                 if (score < 14500)
324                                                         ADD_MOVE(square + 2);
325                                         }
326                                         if (c_long) {
327                                                 // check if the middle square is in check
328                                                 npos = *pos;
329                                                 if (pos->white_to_move)
330                                                         real_make_move(&npos, square, square - 1, &npos.white_pieces[i], npos.black_pieces);
331                                                 else
332                                                         real_make_move(&npos, square, square - 1, &npos.black_pieces[i], npos.white_pieces);
333                                                 score = find_best_move(&npos, hash_position(&npos), NULL, NULL, 1, 0, -40000, 40000, false);
334
335                                                 if (score < 14500)
336                                                         ADD_MOVE(square - 2);
337                                         }
338                                 }
339                         }
340
341                         continue;
342                 }
343
344                 if (SLIDES_STRAIGHT(type)) {
345                         // slide to the right for as long we can
346                         int nsq = square + 1;
347                         for ( ;; ) {
348                                 if (CAN_GOTO(piece, pos->board[nsq]))
349                                         ADD_MOVE(nsq);
350                                 if (pos->board[nsq] != PIECE_EMPTY)
351                                         break;
352                                 ++nsq;
353                         }
354
355                         // to the left
356                         nsq = square - 1;
357                         for ( ;; ) {
358                                 if (CAN_GOTO(piece, pos->board[nsq]))
359                                         ADD_MOVE(nsq);
360                                 if (pos->board[nsq] != PIECE_EMPTY)
361                                         break;
362                                 --nsq;
363                         }
364
365                         // up
366                         nsq = square + 10;
367                         for ( ;; ) {
368                                 if (CAN_GOTO(piece, pos->board[nsq]))
369                                         ADD_MOVE(nsq);
370                                 if (pos->board[nsq] != PIECE_EMPTY)
371                                         break;
372                                 nsq += 10;
373                         }
374                         
375                         // and down
376                         nsq = square - 10;
377                         for ( ;; ) {
378                                 if (CAN_GOTO(piece, pos->board[nsq]))
379                                         ADD_MOVE(nsq);
380                                 if (pos->board[nsq] != PIECE_EMPTY)
381                                         break;
382                                 nsq -= 10;
383                         }
384                 }
385
386                 if (SLIDES_DIAGONALLY(type)) {
387                         // slide to the upper-right for as long as we can
388                         int nsq = square + 11;
389                         for ( ;; ) {
390                                 if (CAN_GOTO(piece, pos->board[nsq]))
391                                         ADD_MOVE(nsq);
392                                 if (pos->board[nsq] != PIECE_EMPTY)
393                                         break;
394                                 nsq += 11;
395                         }
396
397                         // to the upper-left
398                         nsq = square + 9;
399                         for ( ;; ) {
400                                 if (CAN_GOTO(piece, pos->board[nsq]))
401                                         ADD_MOVE(nsq);
402                                 if (pos->board[nsq] != PIECE_EMPTY)
403                                         break;
404                                 nsq += 9;
405                         }
406
407                         // lower-right
408                         nsq = square - 9;
409                         for ( ;; ) {
410                                 if (CAN_GOTO(piece, pos->board[nsq]))
411                                         ADD_MOVE(nsq);
412                                 if (pos->board[nsq] != PIECE_EMPTY)
413                                         break;
414                                 nsq -= 9;
415                         }
416                         
417                         // and lower-left
418                         nsq = square - 11;
419                         for ( ;; ) {
420                                 if (CAN_GOTO(piece, pos->board[nsq]))
421                                         ADD_MOVE(nsq);
422                                 if (pos->board[nsq] != PIECE_EMPTY)
423                                         break;
424                                 nsq -= 11;
425                         }
426                 }
427         }
428
429         // null-move heuristic
430         if (depth > 6) {
431                 struct position npos = *pos;
432                 unsigned long long nphash;
433                 npos.white_to_move = !npos.white_to_move;
434                 npos.ep_square = 0;
435                 
436                 nphash = hash_position(&npos);
437                 score = -find_best_move(&npos, nphash, NULL, NULL, depth - 1, seldepth, alpha, beta, true);
438
439                 if (score > alpha) {
440                         alpha = score;                                                                  
441                         best_from = moves[indexes[0]].from;                                                             
442                         best_to = moves[indexes[0]].to;
443                         
444                         if (alpha >= beta)                                                              
445                                 goto early_exit;                                                        
446                 }
447         }
448
449         // now go through all the moves
450         while (num_moves > 0) {
451                 unsigned tmp;
452                 const struct position * const npos = &moves[indexes[0]].pos;
453                 unsigned long long nphash;
454
455                 /* 
456                  * If this move estimated way too good score, it means we just captured
457                  * the king, and this position is definitely not legal. Stop the search,
458                  * since there's no point of searching illegal positions.
459                  */
460                 if (moves[indexes[0]].estimate_score < -15000) {
461                         alpha = -15000 + (max_depth + max_selective_depth - depth - seldepth);
462                         best_from = moves[indexes[0]].from;
463                         best_to = moves[indexes[0]].to;
464                                 
465                         killer_moves[depth][phash & 1].from = best_from;
466                         killer_moves[depth][phash & 1].to = best_to;
467
468                         goto early_exit;
469                 }
470                 if (moves[indexes[0]].estimate_score > 15000) {
471                         alpha = 15000 - (max_depth + max_selective_depth - depth - seldepth);
472                         best_from = moves[indexes[0]].from;
473                         best_to = moves[indexes[0]].to;
474                                 
475                         killer_moves[depth][phash & 1].from = best_from;
476                         killer_moves[depth][phash & 1].to = best_to;
477
478                         goto early_exit;
479                 }
480
481                 nphash = hash_position(&moves[indexes[0]].pos);
482                 if (lookup_record(nphash) >= 1) {
483                         // ouch, move repetition, it's going to be a draw
484                         score = -50;
485                 } else if (depth == 1) {
486                         // terminal node: static evaluation
487                         score = (pos->white_to_move) ? evaluate(npos) : -evaluate(npos);
488                 } else {
489                         // non-terminal node: consider the opponent's best move
490
491                         // if we're close to the end, and this is a capture, make a move extension
492                         if (depth <= 1 && seldepth > 0 && pos->board[moves[indexes[0]].to] != PIECE_EMPTY)
493                                 score = -find_best_move(npos, nphash, NULL, NULL, depth, seldepth - 1, -beta, -alpha, true);
494                         else
495                                 score = -find_best_move(npos, nphash, NULL, NULL, depth - 1, seldepth, -beta, -alpha, true);
496                 }
497
498                 if (score > alpha) {
499                         alpha = score;                                                                  
500                         best_from = moves[indexes[0]].from;                                                             
501                         best_to = moves[indexes[0]].to;
502
503                         if (alpha >= beta) {
504                                 // replace a random killer move
505                                 killer_moves[depth][phash & 1].from = best_from;
506                                 killer_moves[depth][phash & 1].to = best_to;
507
508                                 goto early_exit;
509                         }
510                 }
511
512                 // swap this element with the last one
513                 tmp = indexes[num_moves - 1];
514                 indexes[num_moves - 1] = indexes[0];
515                 indexes[0] = tmp;
516                 --num_moves;
517
518                 downheapify(moves, indexes, 0, num_moves);
519         }
520         
521         if ((best_from != 21 || best_to != 21) && search_castling)
522                 insert_hash(pos, phash, depth, seldepth, best_from, best_to, alpha);
523
524 early_exit:
525         if (ret_from)
526                 *ret_from = best_from;
527         if (ret_to)
528                 *ret_to = best_to;
529         return alpha;
530 }
531
532 void alarm_sounded(int signal)
533 {
534         abort_search = true;
535         fprintf(ucilog, "Aborting search due to signal %u\n", signal);
536 }
537
538 void ai_move(struct position *pos, unsigned think_time)
539 {
540         unsigned from, to, best_from, best_to;
541         int score, depth, seldepth;
542         struct timeval think_start, now;
543                                 
544         total_nodes = 0;
545         gettimeofday(&think_start, NULL);
546
547         depth = 2;
548         seldepth = depth;
549         clear_hash();
550
551         signal(SIGALRM, alarm_sounded);
552         alarm(think_time);
553         abort_search = false;
554
555         for ( ;; ) {
556                 struct position npos;
557                 int pdepth = depth;
558                 double time_spent;
559
560                 // set the globals
561                 max_depth = depth;
562                 max_selective_depth = seldepth;
563
564                 //fprintf(stderr, "\n\n\nThinking at depth %u:\n\n", depth);
565                 min_depth = depth + seldepth;
566                 score = find_best_move(pos, hash_position(pos), &from, &to, depth, seldepth, -40000, 40000, true);
567
568                 if (abort_search)
569                         break;
570
571                 best_from = from;
572                 best_to = to;
573         
574                 gettimeofday(&now, NULL);
575                 time_spent = (now.tv_sec - think_start.tv_sec) +
576                         1e-6 * (now.tv_usec - think_start.tv_usec);
577                         
578                 uciprintf("info depth %u seldepth %u score cp %d time %u hashfull %u nodes %u nps %u pv",
579                         depth, depth + seldepth - min_depth + 1, score,
580                         (unsigned)(time_spent * 1000.0),
581                         100 * hash_used / HASH_MAX_ELEMS,
582                         total_nodes, (unsigned)(total_nodes / time_spent + 0.5));
583                 uciprintf(" %c%u%c%u",
584                         "abcdefgh"[NUM_TO_FILE(from)], NUM_TO_RANK(from) + 1,
585                         "abcdefgh"[NUM_TO_FILE(to)], NUM_TO_RANK(to) + 1);
586
587                 // reconstruct the pv (hopefully from the hash table!)
588                 npos = *pos;
589                 make_move(&npos, from, to);
590
591                 while (pdepth > 1) {
592                         unsigned pvf, pvt;
593                 
594                         --pdepth;
595                         find_best_move(&npos, hash_position(&npos), &pvf, &pvt, pdepth, seldepth, -40000, 40000, true);
596                         
597                         uciprintf(" %c%u%c%u",
598                                 "abcdefgh"[NUM_TO_FILE(pvf)], NUM_TO_RANK(pvf) + 1,
599                                 "abcdefgh"[NUM_TO_FILE(pvt)], NUM_TO_RANK(pvt) + 1);
600                         
601                         //print_position(&npos);
602                         make_move(&npos, pvf, pvt);
603                 }
604
605                 uciprintf("\n");
606                 fflush(stdout);
607
608                 // this was a much better idea before we had selective search :-)
609 #if 0
610                 if (score > 15000 || score < -15000)
611                         break;
612 #endif
613
614                 if (seldepth < depth * 2)
615                         seldepth += (depth+1)/2;
616                 else {
617                         ++depth;
618                         seldepth = depth;
619                 }
620         }
621        
622         make_move(pos, best_from, best_to);
623                 
624         uciprintf("bestmove %c%u%c%u\n",
625                 "abcdefgh"[NUM_TO_FILE(best_from)], NUM_TO_RANK(best_from) + 1,
626                 "abcdefgh"[NUM_TO_FILE(best_to)], NUM_TO_RANK(best_to) + 1);
627         fflush(stdout);
628 }
629
630 int main(void)
631 {
632         struct position curr_pos;
633         ucilog = fopen("ucilog.txt", "w");
634
635         move_num = 0;
636
637         init_evaluator();
638         init_hash();
639
640         for ( ;; ) {
641                 char buf[4096], *ptr;
642                 fgets(buf, 4096, stdin);
643                 
644                 // strip newlines
645                 ptr = strchr(buf, '\n');
646                 if (ptr != NULL)
647                         *ptr = 0;
648                 
649                 ptr = strchr(buf, '\r');
650                 if (ptr != NULL)
651                         *ptr = 0;
652                 
653                 fprintf(ucilog, "<= %s\n", buf);
654                 fflush(ucilog);
655
656                 if (strcmp(buf, "uci") == 0) {
657                         uciprintf("id name stupid\n");
658                         uciprintf("id author Steinar H. Gunderson\n");
659                         uciprintf("uciok\n");
660                         fflush(stdout);
661                         continue;
662                 }
663                 if (strcmp(buf, "isready") == 0) {
664                         uciprintf("readyok\n");
665                         fflush(stdout);
666                         continue;
667                 }
668                 if (strcmp(buf, "ucinewgame") == 0) {
669                         // TODO: clear history and possibly pawn structure stuff?
670                         continue;
671                 }
672                 if (strncmp(buf, "position", 8) == 0) {
673                         ptr = buf + 9;
674                         if (strncmp(ptr, "startpos", 8) == 0) {
675                                 init_position(&curr_pos);
676                                 ptr += 9;
677                         } 
678                         if (strncmp(ptr, "fen", 3) == 0) {
679                                 // no fen handling yet
680                                 ptr = strchr(ptr, ' ') + 1;
681                         }
682                         if (strncmp(ptr, "moves", 5) == 0) {
683                                 ptr = strchr(ptr, ' ');
684                                 while (ptr != NULL) {
685                                         unsigned from_file, from_rank, to_file, to_rank;
686                                         from_file = tolower(ptr[1]) - 'a';
687                                         from_rank = ptr[2] - '1';
688                                         to_file = tolower(ptr[3]) - 'a';
689                                         to_rank = ptr[4] - '1';
690
691                                         make_move(&curr_pos, SQUARE_TO_NUM(from_file, from_rank), SQUARE_TO_NUM(to_file, to_rank));
692                                         record_move(&curr_pos);
693                                         ++move_num;
694                                         ptr = strchr(ptr + 1, ' ');
695                                 }
696                         }
697                         continue;
698                 }
699                 if (strncmp(buf, "go", 2) == 0) {
700                         // see if there's a clock for us -- if not, use ten seconds
701                         unsigned thinking_time = 10;
702
703                         if (curr_pos.white_to_move) 
704                                 ptr = strstr(buf, "wtime ");
705                         else
706                                 ptr = strstr(buf, "btime ");
707
708                         if (ptr != NULL) {
709                                 unsigned total_ms = atoi(ptr + 6);
710                                 unsigned moves = 40;
711                                 ptr = strstr(buf, "movestogo ");
712                                 if (ptr != NULL) 
713                                         moves = atoi(ptr + 10);
714
715                                 thinking_time = ((total_ms - 5000) / (moves + 2) + 500) / 1000;
716                 
717                                 // also allocate 90% of the increment, if there's any
718                                 if (curr_pos.white_to_move) 
719                                         ptr = strstr(buf, "winc ");
720                                 else
721                                         ptr = strstr(buf, "binc ");
722                                 if (ptr != NULL)
723                                         thinking_time += atoi(ptr + 5) * 9 / 10000;
724
725                                 if (thinking_time < 1)
726                                         thinking_time = 1;
727
728                                 fprintf(ucilog, "Allocating %u seconds to think for this move.\n", thinking_time);
729                         }
730                                         
731                         ai_move(&curr_pos, thinking_time);
732                         record_move(&curr_pos);
733                         ++move_num;
734                         continue;
735                 }
736         
737                 fprintf(ucilog, "Unknown UCI command '%s'!\n", buf);
738                 fflush(ucilog);
739         }
740
741         exit(0);
742 }