(Temporarily) set "animate" to "none" by default (broken feature).
[gf1.git] / ai_player.cxx
blob9a7b566ca8467f1fb7350370ec4a866155290876
1 /*
2 ** $Id$
3 */
4 /*
5 ** Copyright (C) 1999 Kurt Van den Branden
6 **
7 ** This program is free software; you can redistribute it and/or modify
8 ** it under the terms of the GNU General Public License as published by
9 ** the Free Software Foundation; either version 2 of the License, or
10 ** (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software
19 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <iostream>
23 #include <malloc.h>
24 #include <stdlib.h>
25 #include <time.h>
26 #include <algorithm>
27 #include "ai_player.h"
30 // constructor
31 ai_player::ai_player ()
33 maxdepth = 2;
34 maxtime = 0;
35 memorydepth = 0;
36 randomchoose = 1;
37 player = PLAYER1;
38 lastvalue = 0;
39 state = AI_OK;
40 stopnow = 0;
42 return;
46 // destructor
47 ai_player::~ai_player ()
49 return;
53 vector<basemove *> * ai_player::minimax_ab (void * startgame, int starttype)
55 searchnode * firstnode;
56 vector<basemove *> * move;
58 state = AI_OK;
59 stopnow = 0;
61 if (randomchoose)
62 srand (time (NULL));
64 firstnode = new searchnode;
66 firstnode->gameposition = startgame;
67 firstnode->finished = 0;
68 firstnode->depth = 1;
69 firstnode->value = 0;
70 firstnode->upper = 1001;
71 firstnode->lower = -1001;
72 firstnode->type = starttype;
73 // firstnode->movelist = NULL;
74 // firstnode->childlist = NULL;
76 // move = minimax_ab (firstnode, -1001, 1001);
77 move = ab_mem (firstnode, -1001, 1001);
78 lastvalue = firstnode->value;
80 delmemtree (firstnode, 0);
82 if (move != NULL)
83 reverse (move->begin(), move->end());
84 return (move);
88 #if 0
89 /*
90 ** correct, but replaced by ab_mem
92 listheader * ai_player::minimax_ab (searchnode * node, int alfa, int beta)
94 int value,
95 ddelta,
96 chance = 1;
97 unsigned counter;
98 listheader * movel = NULL,
99 * newmovel;
100 void * moveitem;
101 searchnode * newnode;
103 // check for leafnode
104 // (end-of-game or maxdepth exceeded)
105 if ((node->finished) ||
106 (node->depth > maxdepth))
108 value = evalfunc (node->gameposition, player);
110 // create an empty move
111 movel = (listheader *) malloc (sizeof (listheader));
112 newlist (movel);
114 // search the tree
115 else
117 if (node->movelist == NULL)
119 node->movelist = listfunc (node->gameposition);
120 node->childlist = (listheader *) malloc (sizeof (listheader));
121 newlist (node->childlist);
124 if (node->type == MAX)
126 value = -1001;
128 counter = 1;
129 while ((moveitem = llitembynr (node->movelist, counter)) != NULL)
131 // generate child (if necessary)
132 newnode = nodechild (node, counter);
134 counter++;
136 if (newnode->finished == -1)
137 { // calculate new position
138 newnode->finished = 0;
139 newnode->gameposition =
140 newfunc (node->gameposition, moveitem,
141 &(newnode->finished), &ddelta,
142 &(newnode->type));
143 newnode->depth += ddelta;
146 if (newnode->gameposition == NULL)
147 continue;
149 newmovel = minimax_ab (newnode, alfa, beta);
151 // is the new move better then the previous best
152 // if (equ_h (newnode->value, value, &chance))
153 if (((node->depth == 1) &&
154 (equ_h (newnode->value, value, &chance))) ||
155 ((node->depth > 1) && (newnode->value > value)))
157 value = newnode->value;
158 delmovelist (movel);
159 movel = newmovel;
160 unshiftll (movel, copymovefunc (moveitem));
162 else
164 delmovelist (newmovel);
167 alfa = max (alfa, value);
169 if (value > beta)
170 // if (value >= beta)
172 break;
176 else // MIN
178 value = 1001;
180 counter = 1;
181 while ((moveitem = llitembynr (node->movelist, counter)) != NULL)
183 // generate child (if necessary)
184 newnode = nodechild (node, counter);
186 counter++;
188 if (newnode->finished == -1)
189 { // calculate new position
190 newnode->finished = 0;
191 newnode->gameposition =
192 newfunc (node->gameposition, moveitem,
193 &(newnode->finished), &ddelta,
194 &(newnode->type));
195 newnode->depth += ddelta;
198 if (newnode->gameposition == NULL)
199 continue;
201 newmovel = minimax_ab (newnode, alfa, beta);
203 // is the new move better then the previous best
204 // if (equ_l (newnode->value, value, &chance))
205 if (((node->depth == 1) &&
206 (equ_l (newnode->value, value, &chance))) ||
207 ((node->depth > 1) && (newnode->value < value)))
209 value = newnode->value;
210 delmovelist (movel);
211 movel = newmovel;
212 unshiftll (movel, copymovefunc (moveitem));
214 else
216 delmovelist (newmovel);
219 beta = min (beta, value);
221 if (value < alfa)
222 // if (value <= alfa)
224 break;
230 node->value = value;
232 // cleanup children-nodes
233 while ((newnode = (searchnode *) llrembynr (node->childlist, 1))
234 != NULL)
236 delmemtree (newnode);
239 // what if no valid move ?
240 // is it enough that movel contains NULL in that case ?
242 return (movel);
244 #endif
247 vector<basemove *> * ai_player::ab_mem (searchnode * node, int alfa, int beta)
249 int value,
250 ddelta,
251 chance = 1,
252 a, b;
253 unsigned len, i;
254 vector<basemove *> * movel = NULL,
255 * newmovel;
256 searchnode * newnode;
258 if (node->lower > beta) // >=
260 node->value = node->lower;
261 // create an empty move
262 movel = new vector<basemove *>;
263 return (movel);
265 if (node->upper < alfa) // <=
267 node->value = node->upper;
268 // create an empty move
269 movel = new vector<basemove *>;
270 return (movel);
272 alfa = max (alfa, node->lower);
273 beta = min (beta, node->upper);
275 // check for leafnode
276 // (end-of-game or maxdepth exceeded)
277 if ((node->finished == 1) ||
278 ((node->depth > maxdepth) && (node->finished != 2)))
280 value = evalfunc (node->gameposition, player);
282 // create an empty move
283 movel = new vector<basemove *>;
285 // search the tree
286 else
288 if (node->movelist.empty())
290 listfunc (node->gameposition, node->movelist);
293 if (node->type == MAX)
295 value = -1001;
296 a = alfa;
298 len = node->movelist.size();
299 for (i = 0; i < len; i++)
300 // while ((moveitem = llitembynr (node->movelist, counter)) != NULL)
302 // generate child (if necessary)
303 newnode = nodechild (node, i);
305 if (newnode->finished == -1)
306 { // calculate new position
307 newnode->finished = 0;
308 newnode->gameposition =
309 newfunc (node->gameposition, node->movelist[i],
310 &(newnode->finished), &ddelta,
311 &(newnode->type));
312 newnode->depth += ddelta;
315 if (newnode->gameposition == NULL)
316 continue;
318 newmovel = ab_mem (newnode, a, beta);
320 if (stopnow)
322 delmovelist (movel);
323 delmovelist (newmovel);
324 return (NULL);
326 if (outoftime ())
328 stopnow = 1;
329 state = AI_TIMEUP;
330 delmovelist (movel);
331 delmovelist (newmovel);
332 return (NULL);
334 if (stopfunc () == 1)
336 state = AI_STOPPED;
337 delmovelist (movel);
338 delmovelist (newmovel);
339 return (NULL);
342 // is the new move better then the previous best
343 if (((node->depth == 1) &&
344 (equ_h (newnode->value, value, &chance))) ||
345 ((node->depth > 1) && (newnode->value > value)))
347 value = newnode->value;
348 delmovelist (movel);
349 movel = newmovel;
350 // unshiftll (movel, node->movelist[i]->copy());
351 movel->push_back (node->movelist[i]->copy());
353 a = max (a, value);
355 if (value > beta)
357 break;
360 else
362 delmovelist (newmovel);
366 else // MIN
368 value = 1001;
369 b = beta;
371 len = node->movelist.size();
372 for (i = 0; i < len; i++)
373 // while ((moveitem = llitembynr (node->movelist, counter)) != NULL)
375 // generate child (if necessary)
376 newnode = nodechild (node, i);
378 if (newnode->finished == -1)
379 { // calculate new position
380 newnode->finished = 0;
381 newnode->gameposition =
382 newfunc (node->gameposition, node->movelist[i],
383 &(newnode->finished), &ddelta,
384 &(newnode->type));
385 newnode->depth += ddelta;
388 if (newnode->gameposition == NULL)
389 continue;
391 newmovel = ab_mem (newnode, alfa, b);
393 if (stopnow)
395 delmovelist (movel);
396 delmovelist (newmovel);
397 return (NULL);
399 if (outoftime ())
401 stopnow = 1;
402 state = AI_TIMEUP;
403 delmovelist (movel);
404 delmovelist (newmovel);
405 return (NULL);
407 if (stopfunc () == 1)
409 state = AI_STOPPED;
410 delmovelist (movel);
411 delmovelist (newmovel);
412 return (NULL);
415 // is the new move better then the previous best
416 // if (equ_l (newnode->value, value, &chance))
417 if (((node->depth == 1) &&
418 (equ_l (newnode->value, value, &chance))) ||
419 ((node->depth > 1) && (newnode->value < value)))
421 value = newnode->value;
422 delmovelist (movel);
423 movel = newmovel;
424 // unshiftll (movel, node->movelist[i]->copy());
425 movel->push_back (node->movelist[i]->copy());
427 b = min (b, value);
429 if (value < alfa)
431 break;
434 else
436 delmovelist (newmovel);
442 node->value = value;
444 if (value <= alfa)
445 node->upper = value;
446 if ((value > alfa) && (value < beta))
447 node->lower = node->upper = value;
448 if (value >= beta)
449 node->lower = value;
451 // cleanup children-nodes if past maximum memory-depth
452 if (node->depth > memorydepth)
454 for (int i = node->childlist.size() - 1; i >= 0; i--)
456 delmemtree (node->childlist[i]);
457 node->childlist.pop_back();
461 // what if no valid move ?
462 // is it enough that movel contains NULL in that case ?
464 return (movel);
468 vector<basemove *> * ai_player::mtdf (void * startgame, int starttype)
470 searchnode * firstnode;
471 vector<basemove *> * move;
473 state = AI_OK;
474 stopnow = 0;
476 if (randomchoose)
477 srand (time (NULL));
479 firstnode = new searchnode;
481 firstnode->gameposition = startgame;
482 firstnode->finished = 0;
483 firstnode->depth = 1;
484 firstnode->value = 0;
485 firstnode->upper = 1001;
486 firstnode->lower = -1001;
487 firstnode->type = starttype;
488 // firstnode->movelist = NULL;
489 // firstnode->childlist = NULL;
491 move = mtdf (firstnode);
492 lastvalue = firstnode->value;
494 delmemtree (firstnode, 0);
496 if (move != NULL)
497 reverse (move->begin(), move->end());
498 return (move);
502 vector<basemove *> * ai_player::mtdf (searchnode * node)
504 int value,
505 upper,
506 lower,
507 gamma;
508 vector<basemove *> * bestmove = NULL;
510 value = node->value;
511 upper = 1001;
512 lower = -1001;
514 while ((lower < upper) && (stopnow == 0))
516 if (value == lower)
518 gamma = value + 1;
520 else
522 gamma = value;
525 delmovelist (bestmove);
527 // bestmove = mt (node, gamma); // werkt niet
528 // bestmove = minimax_ab (node, gamma-1, gamma); // werkt wel
529 bestmove = ab_mem (node, gamma-1, gamma); // werkt ook, en goed
530 value = node->value;
532 if (value < gamma)
534 upper = value;
536 else
538 lower = value;
542 return (bestmove);
546 // iterative deepening mtdf
547 vector<basemove *> * ai_player::mtdf_id (void * startgame,
548 int starttype,
549 float mtime)
551 searchnode * firstnode;
552 int oldvalue,
553 newvalue,
554 savedepth,
556 vector<basemove *> * bestmove = NULL,
557 * newmove;
558 struct timezone tz;
560 maxtime = mtime;
561 gettimeofday (&basetime, &tz);
562 state = AI_OK;
563 stopnow = 0;
565 if (randomchoose)
566 srand (time (NULL));
568 firstnode = new searchnode;
570 firstnode->gameposition = startgame;
571 firstnode->finished = 0;
572 firstnode->depth = 1;
573 firstnode->value = 0;
574 firstnode->upper = 1001;
575 firstnode->lower = -1001;
576 firstnode->type = starttype;
577 // firstnode->movelist = NULL;
578 // firstnode->childlist = NULL;
580 savedepth = maxdepth;
581 oldvalue = newvalue = 0;
582 for (i = 1; i <= savedepth; i++)
584 maxdepth = i;
585 // each mtdf-call is not started with the value from the previous run
586 // but with the one before that
587 firstnode->value = oldvalue;
588 reset_ul (firstnode);
590 newmove = mtdf (firstnode);
592 if (stopnow)
594 delmovelist (newmove);
595 break;
598 oldvalue = newvalue;
599 newvalue = firstnode->value;
601 delmovelist (bestmove);
602 bestmove = newmove;
604 if (halfoutoftime ())
605 { /* soft timeout */
606 state = AI_TIMEUP;
607 break;
611 maxdepth = savedepth;
612 lastvalue = firstnode->value;
614 delmemtree (firstnode, 0);
616 if (bestmove != NULL)
617 reverse (bestmove->begin(), bestmove->end());
619 return (bestmove);
624 ** if flag is 0, then the gameposition will not be deleted
625 ** this should be used for the root of the nodetree
627 void ai_player::delmemtree (searchnode * node, int flag)
629 // searchnode * child;
630 // void * moveitem;
631 int i, len;
633 if (flag)
635 delgamefunc (node->gameposition);
638 if (!node->movelist.empty())
640 len = node->movelist.size();
641 for (i = 0; i < len; i++)
643 delete node->movelist[i];
647 if (!node->childlist.empty())
649 len = node->childlist.size();
650 for (i = 0; i < len; i++)
652 delmemtree (node->childlist[i]);
656 delete node;
658 return;
663 ** reset upper and lower limits in all nodes from the memory tree
665 void ai_player::reset_ul (searchnode * node)
667 // searchnode * child;
668 int i, len;
670 node->upper = 1001;
671 node->lower = -1001;
673 if (!node->childlist.empty())
675 len = node->childlist.size();
676 for (i = 0; i < len; i++)
678 reset_ul (node->childlist[i]);
682 return;
686 inline void ai_player::delmovelist (vector<basemove *> * todel)
688 if (todel != NULL)
690 int len = todel->size(),
693 for (i = 0; i < len; i++)
694 delete (*todel)[i];
696 delete todel;
699 return;
704 ** just like a normal compare (<)
705 ** returns:
706 ** 1 : nr1 < nr2
707 ** 0: nr1 > nr2
708 ** 0/1: when nr1 and nr2 are equal, chooses one at random
710 ** chance is the chance that nr1 will be chosen if nr1 == nr2
711 ** this parameter will be incremented each time an equal occurs
712 ** and will be set to 1 if nr1 < nr2
713 ** (it should be initialised to 1 by the caller and then left alone)
715 int ai_player::equ_l (int nr1, int nr2, int * chance)
717 int randnr;
719 if (randomchoose == 0)
720 return (nr1 < nr2);
722 if (nr1 < nr2)
724 *chance = 1;
725 return (1);
727 if (nr1 > nr2)
729 return (0);
732 randnr = (int) ( (*chance + 1.0) * rand() / (RAND_MAX+1.0) );
733 (*chance)++;
735 if (randnr < 1)
737 return (1);
740 return (0);
745 ** just like a normal compare (>)
746 ** returns:
747 ** 1 : nr1 > nr2
748 ** 0: nr1 < nr2
749 ** 0/1: when nr1 and nr2 are equal, chooses one at random
751 ** chance is the chance that nr1 will be chosen if nr1 == nr2
752 ** this parameter will be incremented each time an equal occurs
753 ** and will be set to 1 if nr1 > nr2
754 ** (it should be initialised to 1 by the caller and then left alone)
756 int ai_player::equ_h (int nr1, int nr2, int * chance)
758 int randnr;
760 if (randomchoose == 0)
761 return (nr1 > nr2);
763 if (nr1 > nr2)
765 *chance = 1;
766 return (1);
768 if (nr1 < nr2)
770 return (0);
773 randnr = (int) ( (*chance + 1.0) * rand() / (RAND_MAX+1.0) );
774 (*chance)++;
776 if (randnr < 1)
778 return (1);
781 return (0);
786 ** check if a child-node already exists,
787 ** if not, create a new one
789 inline searchnode * ai_player::nodechild (searchnode * node, unsigned nr)
791 searchnode * newnode;
793 if (nr < node->childlist.size())
794 return (node->childlist[nr]);
797 #if 0
798 /* just for testing */
799 /* check if the previous nr exists, it should */
800 if ((nr > 1) && (llitembynr (node->childlist, nr-1) == NULL))
802 printf ("\nERROR: ai_player::nodechild\n\n");
803 exit (0);
805 #endif
807 /* create a new node */
808 newnode = new searchnode;
809 newnode->gameposition = NULL;
810 newnode->finished = -1;
811 newnode->depth = node->depth;
812 newnode->value = 0;
813 newnode->upper = 1001;
814 newnode->lower = -1001;
815 newnode->type = node->type;
816 // newnode->movelist = NULL;
817 // newnode->childlist = NULL;
819 node->childlist.push_back (newnode);
821 return (newnode);
825 inline int ai_player::outoftime ()
827 struct timeval tv;
828 struct timezone tz;
829 float timedif;
831 if (maxtime == 0.0)
832 return (0);
834 gettimeofday (&tv, &tz);
836 timedif = (tv.tv_sec - basetime.tv_sec) +
837 (float) (tv.tv_usec - basetime.tv_usec) / 1000000;
839 if ((maxtime - timedif) < 0.0)
841 //cout << "hard timeout\n";
842 return (1);
845 return (0);
849 inline int ai_player::halfoutoftime ()
851 struct timeval tv;
852 struct timezone tz;
853 float timedif;
855 if (maxtime == 0.0)
856 return (0);
858 gettimeofday (&tv, &tz);
860 timedif = (tv.tv_sec - basetime.tv_sec) +
861 (float) (tv.tv_usec - basetime.tv_usec) / 1000000;
863 if ((maxtime/2 - timedif) < 0.0)
865 //cout << "soft timeout\n";
866 return (1);
869 return (0);