2 // "$Id: checkers.cxx 8168 2011-01-02 03:55:23Z matt $"
4 // Checkers game for the Fast Light Tool Kit (FLTK).
6 // Hours of fun: the FLTK checkers game!
7 // Based on a very old algorithm, but it still works!
9 // Copyright 1998-2010 by Bill Spitzak and others.
11 // This library is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU Library General Public
13 // License as published by the Free Software Foundation; either
14 // version 2 of the License, or (at your option) any later version.
16 // This library is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 // Library General Public License for more details.
21 // You should have received a copy of the GNU Library General Public
22 // License along with this library; if not, write to the Free Software
23 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 // Please report all bugs and problems on the following page:
28 // http://www.fltk.org/str.php
31 const char* copyright
=
33 "Copyright (C) 1997-2010 Bill Spitzak spitzak@d2.com\n"
34 "Original Pascal code:\n"
35 "Copyright 1978, Oregon Minicomputer Software, Inc.\n"
36 "2340 SW Canyon Road, Portland, Oregon 97201\n"
37 "Written by Steve Poulsen 18-Jan-79\n"
39 "This program is free software; you can redistribute it and/or modify "
40 "it under the terms of the GNU General Public License as published by "
41 "the Free Software Foundation; either version 2 of the License, or "
42 "(at your option) any later version.\n"
44 "This program is distributed in the hope that it will be useful, "
45 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
46 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
47 "GNU General Public License for more details.\n"
49 "You should have received a copy of the GNU Library General Public "
50 "License along with this library; if not, write to the Free Software "
51 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 "
54 // Define FLTK to get the fltk interface
55 // Define VT100 to get the VT100 interface
56 // Define both to get a program that takes a -t switch
70 #include <ctype.h> // toupper
73 ////////////////////////////////////////////////////////////////
76 int maxevaluate
=2500; // max number of moves to examine on a turn
77 int maxnodes
= 2500; // maximum number of nodes in search tree
78 int maxply
= 20; // maximum depth to look ahead
79 char forcejumps
= 1; // is forced jumps rule in effect?
81 // scoring parameters: (all divided by 5 from original code)
82 // some signs seem to be backwards, marked them with (-) in comment
83 const int spiece
= 800; // value of a piece
84 const int sking
= 1200; // value of a king
85 const int sadvan
= 160; // value of mypieces/theirpieces-1
86 // const int smobil = ? // moves *enemy* can make w/o being jumped
87 const int sallpin
= 80; // mobil == 0
88 const int sdeny
= 10; // moves enemy can make that will be jumped
89 const int spin
= 32; // enemy pieces that have no move except jumped
90 const int sthreat
= -10; // enemy pieces we can jump if not moved (-)
91 const int sgrad
= 1; // score of piece positions
92 const int sback
= 10; // back row occupied so enemy can't make king
93 const int smoc2
= 200; // more mobility, more center
94 const int smoc3
= -8; // less mobility, less center
95 const int smoc4
= -80; // more mobility, less center
96 const int smode2
= -14; // less mobility, less denied
97 const int smode3
= -40; // more mobility, more denied (-)
98 const int sdemmo
= -20; // more denied, more moves (-)
99 const int scent
= 10; // pieces in center
100 const int skcent
= 100; // kings in center
102 const int depthpenalty
=4; // guess
103 const int noise
=2; // values less or eq to this apart are eq
105 // const int sattackking = 4; // not used
106 // const int sattackpiece = 3;
110 node
*son
; // best son
111 node
*brother
; // next brother
112 short int value
; // value of this board position to player making move
113 unsigned char from
,to
; // the move to reach this board
114 long int jump
; // bit map of locations jumped
118 unsigned char threat
;
120 unsigned who
:1; // 0 = black's move, 1 = white's move
121 unsigned king
:1; // 1 = move causes piece to be kinged
131 int nodes
; // count of nodes
133 /* Board positions: Border positions:
136 05 06 07 08 04 XX XX XX XX
137 09 10 11 12 XX XX XX XX 13
138 14 15 16 17 13 XX XX XX XX
139 18 19 20 21 XX XX XX XX 22
140 23 24 25 26 22 XX XX XX XX
141 27 28 29 30 XX XX XX XX 31
142 32 33 34 36 31 XX XX XX XX
143 36 37 38 39 XX XX XX XX 40
150 // Piece values so that BLACK and WHITE are bit flags:
159 const piece flip
[9] = {
160 EMPTY
, WHITE
, BLACK
, 0, 0, WHITEKING
, BLACKKING
, 0, BLUE
};
162 const int offset
[9][4] = { // legal move directions
174 piece b
[45]; // current board position being considered
176 int evaluated
; // number of moves evaluated this turn
178 char centralsquares
[45];
179 char is_protected
[45];
181 piece flipboard
[45]; // swapped if enemy is black
182 piece
*tb
; // pointer to real or swapped board
184 #define FRIENDKING BLACKKING
186 #define ENEMYKING WHITEKING
188 char check(int target
,int direction
) {
189 // see if enemy at target can be jumped from direction by our piece
190 int dst
= target
-direction
;
191 if (tb
[dst
]) return(0);
192 int src
= target
+direction
;
193 if (tb
[src
] == FRIENDKING
);
194 else if (direction
< 0 || tb
[src
] != FRIEND
) return(0);
195 piece aa
= tb
[target
]; piece bb
= tb
[src
];
196 tb
[target
] = EMPTY
; tb
[src
] = EMPTY
;
198 ( (tb
[src
-4]&FRIEND
&& tb
[src
-8]&ENEMY
)
199 || (tb
[src
-5]&FRIEND
&& tb
[src
-10]&ENEMY
)
200 || (tb
[dst
-4]&ENEMY
&& !tb
[dst
+4])
201 || (tb
[dst
-5]&ENEMY
&& !tb
[dst
+5])
202 || (tb
[src
+4]&FRIEND
&& tb
[src
+8]==ENEMYKING
)
203 || (tb
[src
+5]&FRIEND
&& tb
[src
+10]==ENEMYKING
)
204 || (tb
[dst
+4]==ENEMYKING
&& !tb
[dst
-4])
205 || (tb
[dst
+5]==ENEMYKING
&& !tb
[dst
-5]));
206 tb
[target
] = aa
; tb
[src
] = bb
;
210 int deniedmoves
,undeniedmoves
;
211 void analyzemove(int direction
,int src
) {
212 int target
= src
+direction
;
214 if (!tb
[target
+direction
]) is_protected
[target
] = 1;
215 piece a
= tb
[src
]; tb
[src
] = EMPTY
;
216 if (check(target
,4) || check(target
,5) ||
217 check(target
,-4) || check(target
,-5) ||
218 (tb
[src
+4]&ENEMY
&& check(src
+4,4)) ||
219 (tb
[src
+5]&ENEMY
&& check(src
+5,5)) ||
220 (tb
[src
-4]&ENEMY
&& check(src
-4,-4)) ||
221 (tb
[src
-5]&ENEMY
&& check(src
-5,-5)))
223 else undeniedmoves
++;
228 void evaluateboard(node
*n
,int print
) {
230 if (!n
->who
) tb
= b
; // move was black's
232 for (int i
=0; i
<45; i
++) flipboard
[44-i
] = flip
[(int)b
[i
]];
236 memset(is_protected
,0,sizeof(is_protected
));
237 int friendpieces
= 0;
245 n
->mobil
= n
->deny
= n
->pin
= n
->threat
= 0;
248 for (i
=5; i
<40; i
++) switch(tb
[i
]) {
251 enemykcent
+= centralsquares
[i
];
263 enemycent
+= centralsquares
[i
];
268 if (deniedmoves
&& !undeniedmoves
) n
->pin
++;
269 n
->deny
+= deniedmoves
;
270 n
->mobil
+= undeniedmoves
;
274 friendkcent
+= centralsquares
[i
];
275 if (tb
[i
+4]&ENEMY
&& !tb
[i
+8] && !(tb
[i
+4]==ENEMYKING
&& !tb
[i
-4]))
277 if (tb
[i
+5]&ENEMY
&& !tb
[i
+10] && !(tb
[i
+5]==ENEMYKING
&& !tb
[i
-5]))
281 friendcent
+= centralsquares
[i
];
282 if (tb
[i
-4]&ENEMY
&& !tb
[i
-8] && tb
[i
+4]) n
->threat
++;
283 if (tb
[i
-5]&ENEMY
&& !tb
[i
-10] && tb
[i
+5]) n
->threat
++;
288 for (i
=4; i
<9; i
++) gradient
[i
] = tb
[i
] ? 0 : 32;
290 for (i
=9; i
<40; i
++) {
291 int x
= (gradient
[i
-4]+gradient
[i
-5])/2;
292 if (tb
[i
]==FRIEND
) total
+= x
;
293 gradient
[i
] = (tb
[i
]&FRIEND
|| (!tb
[i
] && !is_protected
[i
])) ? x
: 0;
297 n
->back
= tb
[39]==FRIEND
&& tb
[37]==FRIEND
&& !enemykings
;
301 n
->moc2
= f
->mobil
>n
->mobil
&& friendcent
>enemycent
;
302 n
->moc3
= f
->mobil
<=n
->mobil
&& friendcent
<enemycent
;
303 n
->moc4
= f
->mobil
>n
->mobil
&& friendcent
<enemycent
;
304 n
->mode2
= f
->mobil
<=n
->mobil
&& n
->deny
<f
->deny
;
305 n
->mode3
= f
->mobil
>n
->mobil
&& n
->deny
>f
->deny
;
306 n
->demmo
= n
->deny
>f
->deny
&& f
->deny
+f
->mobil
>n
->deny
+n
->mobil
;
309 spiece
* (friendpieces
- enemypieces
) +
310 (sking
-spiece
) * (friendkings
- enemykings
) +
312 sdeny
* (n
->deny
- f
->deny
) +
313 spin
* (n
->pin
- f
->pin
) +
314 sthreat
* (n
->threat
- f
->threat
) +
315 sgrad
* (n
->gradient
- f
->gradient
) +
316 sback
* (n
->back
- f
->back
) +
317 smoc2
* (n
->moc2
- f
->moc2
) +
318 smoc3
* (n
->moc3
- f
->moc3
) +
319 smoc4
* (n
->moc4
- f
->moc4
) +
320 smode2
* (n
->mode2
- f
->mode2
) +
321 smode3
* (n
->mode3
- f
->mode3
) +
322 sdemmo
* (n
->demmo
- f
->demmo
) +
323 scent
* (friendcent
- enemycent
) +
324 (skcent
-scent
) * (friendkcent
- enemykcent
);
325 if (!n
->mobil
) total
+= sallpin
;
327 if (!enemypieces
) total
= 30000;
328 else if (friendpieces
> enemypieces
)
329 total
+= (sadvan
*friendpieces
)/enemypieces
-sadvan
;
330 else total
-= (sadvan
*enemypieces
)/friendpieces
-sadvan
;
333 printf("\tParent\tNew\tScore\n");
334 printf("pieces\t%d\t%d\t%d\n",enemypieces
,friendpieces
,
335 spiece
*(friendpieces
-enemypieces
));
336 printf("kings\t%d\t%d\t%d\n",enemykings
,friendkings
,
337 (sking
-spiece
)*(friendkings
-enemykings
));
338 printf("mobil\t%d\t%d\n",f
->mobil
,n
->mobil
);
339 printf("deny\t%d\t%d\t%d\n",f
->deny
,n
->deny
,sdeny
*(n
->deny
-f
->deny
));
340 printf("pin\t%d\t%d\t%d\n",f
->pin
,n
->pin
,spin
*(n
->pin
-f
->pin
));
341 printf("threat\t%d\t%d\t%d\n",f
->threat
,n
->threat
,sthreat
*(n
->threat
-f
->threat
));
342 printf("grad\t%d\t%d\t%d\n",f
->gradient
,n
->gradient
,sgrad
*(n
->gradient
-f
->gradient
));
343 printf("back\t%d\t%d\t%d\n",f
->back
,n
->back
,sback
*(n
->back
-f
->back
));
344 printf("moc2\t%d\t%d\t%d\n",f
->moc2
,n
->moc2
,smoc2
*(n
->moc2
-f
->moc2
));
345 printf("moc3\t%d\t%d\t%d\n",f
->moc3
,n
->moc3
,smoc3
*(n
->moc3
-f
->moc3
));
346 printf("moc4\t%d\t%d\t%d\n",f
->moc4
,n
->moc4
,smoc4
*(n
->moc4
-f
->moc4
));
347 printf("mode2\t%d\t%d\t%d\n",f
->mode2
,n
->mode2
,smode2
*(n
->mode2
-f
->mode2
));
348 printf("mode3\t%d\t%d\t%d\n",f
->mode3
,n
->mode3
,smode3
*(n
->mode3
-f
->mode3
));
349 printf("demmo\t%d\t%d\t%d\n",f
->demmo
,n
->demmo
,sdemmo
*(n
->demmo
-f
->demmo
));
350 printf("cent\t%d\t%d\t%dn",enemycent
,friendcent
,scent
*(friendcent
-enemycent
));
351 printf("kcent\t%d\t%d\t%d\n",enemykcent
,friendkcent
,skcent
*(friendkcent
-enemykcent
));
352 printf("total:\t\t\t%d\n",total
);
358 } // end of evaluateboard
360 // --------------------- Tree management -----------------
364 node
*newnode(void) {
368 freelist
= n
->brother
;
370 else n
= (node
*)malloc(sizeof(node
));
371 memset(n
,0,sizeof(node
));
376 void extract(node
*n
) {
380 if (j
==n
) i
->son
= n
->brother
;
382 i
= j
; j
= j
->brother
;
383 if (j
==n
) {i
->brother
= n
->brother
; break;}
389 void killnode(node
*x
) {
392 for (y
= x
; ; y
= y
->brother
) {
394 killnode(y
->son
); y
->son
= 0;
395 if (!y
->brother
) break;
397 y
->brother
= freelist
;
401 int seed
; // current random number
403 void insert(node
*n
) {
406 for (pp
= &(n
->father
->son
); *pp
; pp
= &((*pp
)->brother
)) {
407 int val1
= (*pp
)->value
;
408 if (abs(val
-val1
) <= noise
) {
409 seed
= (seed
*13077+5051)%0100000;
410 if ((seed
& 070) >= 060) break;
412 else if (val
> val1
) break;
418 // --------------------------------------------------------------
420 void movepiece(node
* f
, int i
, node
* jnode
) {
421 static char jumphappened
;
423 for (int k
=0; k
<4; k
++) {
424 int direction
= offset
[(int)b
[i
]][k
];
425 if (!direction
) break;
428 if (!jnode
&& (!forcejumps
|| !f
->son
|| !f
->son
->jump
)) {
434 piece oldpiece
= b
[i
]; b
[i
] = EMPTY
;
435 if (!(oldpiece
&KING
) && n
->who
? (j
>=36) : (j
<=8)) {
437 b
[j
] = oldpiece
|KING
;
439 else b
[j
] = oldpiece
;
442 b
[i
] = oldpiece
; b
[j
] = EMPTY
;
444 } else if (((b
[j
]^b
[i
])&(WHITE
|BLACK
))==(WHITE
|BLACK
) && !b
[j
+direction
]) {
445 if (forcejumps
&& f
->son
&& !f
->son
->jump
) {
456 n
->jump
= (1<<(jumploc
-10));
457 piece oldpiece
= b
[i
]; b
[i
] = EMPTY
;
458 if (!(oldpiece
&KING
) && n
->who
? (j
>=36) : (j
<=8)) {
460 b
[j
] = oldpiece
|KING
;
462 else b
[j
] = oldpiece
;
464 n
->from
= jnode
->from
;
465 n
->jump
|= jnode
->jump
;
466 n
->king
|= jnode
->king
;
468 piece jumpedpiece
= b
[jumploc
];
472 if (forcejumps
&& jumphappened
) killnode(n
);
473 else {evaluateboard(n
,0); insert(n
);}
474 b
[i
] = oldpiece
; b
[j
] = EMPTY
;
475 b
[jumploc
] = jumpedpiece
;
481 void expandnode(node
*f
) {
482 if (f
->son
|| f
->value
> 28000) return; // already done
483 piece turn
= f
->who
? BLACK
: WHITE
;
484 for (int i
=5; i
<40; i
++) if (b
[i
]&turn
) movepiece(f
,i
,0);
486 f
->value
= -f
->son
->value
;
487 if (f
->brother
) f
->value
-= depthpenalty
;
489 else f
->value
= 30000;
492 void makemove(node
*n
) {
493 b
[n
->to
] = b
[n
->from
];
494 if (n
->king
) b
[n
->to
] |= KING
;
496 if (n
->jump
) for(int i
=0; i
<32; i
++) {
497 if (n
->jump
& (1<<i
)) b
[10+i
] = EMPTY
;
503 int fullexpand(node
*f
, int level
) {
504 if (didabort() || nodes
> maxnodes
-(maxply
*10) || evaluated
> maxevaluate
) return(0);
506 if (!f
->son
) return(1);
508 memmove(oldboard
,b
,sizeof(b
));
510 if (!n
->jump
&& n
->brother
) {if (level
<1) return(1); level
--;}
512 node
* sons
[32]; for (i
=0; (sons
[i
++] = n
); n
= n
->brother
);
514 for (i
=0; ret
&& (n
= sons
[i
++]);) {
516 ret
= fullexpand(n
,level
);
517 memmove(b
,oldboard
,sizeof(b
));
521 f
->value
= -f
->son
->value
;
525 int descend(node
*f
) {
527 if (didabort() || nodes
> maxnodes
|| depth
>= maxply
) return(0);
532 int ret
= descend(n
);
536 f
->value
= -f
->son
->value
;
539 else {expandnode(f
); return(1);}
544 node
*calcmove(node
*root
) { // return best move after root
546 if (!root
->son
) return(0); // no move due to loss
547 if (debug
) printf("calcmove() initial nodes = %d\n",nodes
);
549 if (root
->son
->brother
) {
551 for (x
= 1; abs(root
->value
)<28000 && fullexpand(root
,x
); x
++);
552 piece saveboard
[45]; memmove(saveboard
,b
,sizeof(b
));
553 while (abs(root
->value
)<28000) {
555 memmove(b
,saveboard
,sizeof(b
));
559 if (debug
) printf(" evaluated %d, nodes = %d\n", evaluated
, nodes
);
563 // the actual game state ----------------
565 node
*root
,*undoroot
;
567 piece jumpboards
[24][45]; // saved boards for undoing jumps
570 char user
; // 0 = black, 1 = white
577 for (n
=0; n
<5; n
++) b
[n
] = BLUE
;
578 for (n
=5; n
<18; n
++) b
[n
] = WHITE
;
579 for (n
=18; n
<27; n
++) b
[n
] = EMPTY
;
580 for (n
=27; n
<40; n
++) b
[n
] = BLACK
;
581 for (n
=40; n
<45; n
++) b
[n
] = BLUE
;
582 b
[13] = b
[22] = b
[31] = BLUE
;
584 centralsquares
[15] = centralsquares
[16] =
585 centralsquares
[19] = centralsquares
[20] =
586 centralsquares
[24] = centralsquares
[25] =
587 centralsquares
[28] = centralsquares
[29] = 1;
589 // set up initial search tree:
592 undoroot
= root
= newnode();
594 // make it white's move, so first move is black:
600 void domove(node
* move
) {
601 if (move
->jump
) memmove(jumpboards
[nextjump
++],b
,sizeof(b
));
607 if (debug
) evaluateboard(move
,1);
612 if (n
== undoroot
) return 0; // no more undo possible
613 if (n
->jump
) memmove(b
,jumpboards
[--nextjump
],sizeof(b
));
615 b
[n
->from
] = b
[n
->to
];
616 if (n
->king
) b
[n
->from
] &= (WHITE
|BLACK
);
622 root
->value
= 0; // prevent it from thinking game is over
624 if (root
== undoroot
) user
= 0;
628 const char _usermoves
[] =
629 "B1D1F1H1A2C2E2G2??B3D3F3H3A4C4E4G4??B5D5F5H5A6C6E6G6??B7D7F7H7A8C8E8G8??";
630 #define usermoves(x,y) _usermoves[2*((x)-5)+(y)-1]
632 void dumpnode(node
*n
, int help
) {
635 if (help
) printf("%c%c %c%c\t- ",
636 usermoves(x
,1),usermoves(x
,2),
637 usermoves(y
,1),usermoves(y
,2));
638 printf("%s %ss from %c%c to %c%c",
639 n
->who
? "White" : "Black",
640 n
->jump
? "jump" : "move",
641 usermoves(x
,1),usermoves(x
,2),
642 usermoves(y
,1),usermoves(y
,2));
644 for (int i
=0; i
<32; i
++) if (n
->jump
& (1<<i
))
645 printf(", %c%c",usermoves(10+i
,1),usermoves(10+i
,2));
648 printf(" (%+d).\n",n
->value
);
653 ////////////////////////////////////////////////////////////////
657 void positioncursor(int i
) {
658 printf("\033[%d;%dH",
659 usermoves(i
,2)-'0'+1,
660 2*(usermoves(i
,1)-'A')+1);
663 void outpiecename(piece n
) {
664 printf(n
&BLACK
? "\033[1;7m" : "\033[1m");
665 putchar(" BW??BW??"[n
]);
666 putchar(" BW??KK??"[n
]);
670 void VT100board(void) {
671 printf("\033<\033[H\033[J\033[10r");
673 puts(" A B C D E F G H");
674 for (int i
=0; i
<4; i
++) {
677 for (k
=0; k
<4; k
++) {
678 printf("\033[7m \033[0m");
679 outpiecename(b
[j
+k
]);
684 for (k
=0; k
<4; k
++) {
685 outpiecename(b
[j
+k
]);
686 printf("\033[7m \033[0m");
693 void VT100move(node
*n
, int) {
696 positioncursor(n
->from
);
697 outpiecename(b
[n
->from
]);
698 positioncursor(n
->to
);
699 outpiecename(b
[n
->to
]);
700 if (n
->jump
) for(int i
=0; i
<32; i
++) {
701 if (n
->jump
& (1<<i
)) {
702 positioncursor(10+i
);
703 outpiecename(b
[10+i
]);
709 int decode(char *m
) {
712 if (toupper(m
[0])==usermoves(i
,1) && m
[1]==usermoves(i
,2)) return(i
);
718 static void sigint(...) {
720 signal(SIGINT
,sigint
);
723 void fixexit(int x
) {
724 printf("\0337\033[r\0338");
728 // Returns a son, or 0 if no move specified, or root to cause "help"
729 node
*getusermove(void) {
732 char line
[100],*m1
,*m2
;
735 printf("\033[1m%s's move?\033[0m ",root
->who
? "Black" : "White");
737 printf("\033[1mCommand?\033[0m ");
739 if (!fgets(line
, sizeof(line
), stdin
)) {
741 if (feof(stdin
)) fixexit(0);
744 for (m1
= line
; *m1
&& *m1
<=' '; m1
++);
748 for (; *m2
&& *m2
<'0'; m2
++);
749 if (playing
&& m1
[1]>='0' && m1
[1]<='9') {
752 if (i
&& j
) for (t
= root
->son
; t
; t
= t
->brother
)
753 if (t
->from
== i
&& t
->to
== j
) return(t
);
754 puts("Valid moves are:");
757 switch(toupper(m1
[0])) {
760 if (playing
) autoplay
= 1;
767 printf("Debug is now %s.", debug
? "on" : "off");
770 forcejumps
= !forcejumps
;
771 printf("Forced jumps rule is now %s.",forcejumps
? "on" : "off");
772 killnode(root
->son
); root
->son
= 0;
776 if (playing
) for (t
= root
->son
; t
; t
= t
->brother
) dumpnode(t
,1);
779 return(playing
? root
: 0);
785 printf("I expect the following moves:\n");
786 for (t
= root
->son
; t
; t
= t
->son
) dumpnode(t
,0);
797 VT100move(undomove(),1);
798 VT100move(undomove(),1);
801 maxevaluate
= maxnodes
= 2*maxevaluate
;
805 maxevaluate
= maxnodes
= maxevaluate
/2;
806 J2
: printf("Moves evaluated set to %d.",maxevaluate
);
813 "F(orce jumps rule on/off)\n"
814 "L(ist legal moves)\n"
815 "M(ake a move for me)\n"
817 "P(redict next few moves)\n"
825 for (t
= root
->son
; t
; t
= t
->brother
) dumpnode(t
,1);
831 signal(SIGINT
,sigint
);
837 printf("%s has no move. Game over.",root
->who
? "Black" : "White");
838 playing
= autoplay
= 0;
842 if (playing
&& (autoplay
|| root
->who
== user
)) {
843 move
= calcmove(root
);
844 if (move
->value
<= -30000) {
845 printf("%s resigns.", move
->who
? "White" : "Black");
847 playing
= autoplay
= 0;
850 move
= getusermove();
851 if (move
== root
) move
= calcmove(root
);
863 ////////////////////////////////////////////////////////////////
868 #include <FL/Fl_Double_Window.H>
869 #include <FL/Fl_Bitmap.H>
870 #include <FL/fl_draw.H>
871 #include <FL/Fl_Menu_Item.H>
872 #include <FL/fl_ask.H>
874 //----------------------------------------------------------------
875 // old 4-level NeXT images have been seperated into bitmaps so they
876 // can be drawn with arbitrary colors and real transparency. This is
877 // rather tedious and perhaps fltk should provide a direct support
880 #include "pixmaps/black_1.xbm"
881 #include "pixmaps/black_2.xbm"
882 #include "pixmaps/black_3.xbm"
883 #include "pixmaps/black_4.xbm"
884 #include "pixmaps/white_1.xbm"
885 #include "pixmaps/white_2.xbm"
886 #include "pixmaps/white_3.xbm"
887 #include "pixmaps/white_4.xbm"
888 #include "pixmaps/blackking_1.xbm"
889 #include "pixmaps/blackking_2.xbm"
890 #include "pixmaps/blackking_3.xbm"
891 #include "pixmaps/blackking_4.xbm"
892 #include "pixmaps/whiteking_1.xbm"
893 #include "pixmaps/whiteking_2.xbm"
894 #include "pixmaps/whiteking_3.xbm"
895 #include "pixmaps/whiteking_4.xbm"
899 void make_bitmaps() {
900 if (bm
[0][0]) return;
901 bm
[0][0] = new Fl_Bitmap(black_1_bits
, black_1_width
, black_1_height
);
902 bm
[0][1] = new Fl_Bitmap(black_2_bits
, black_1_width
, black_1_height
);
903 bm
[0][2] = new Fl_Bitmap(black_3_bits
, black_1_width
, black_1_height
);
904 bm
[0][3] = new Fl_Bitmap(black_4_bits
, black_1_width
, black_1_height
);
905 bm
[1][0] = new Fl_Bitmap(white_1_bits
, black_1_width
, black_1_height
);
906 bm
[1][1] = new Fl_Bitmap(white_2_bits
, black_1_width
, black_1_height
);
907 bm
[1][2] = new Fl_Bitmap(white_3_bits
, black_1_width
, black_1_height
);
908 bm
[1][3] = new Fl_Bitmap(white_4_bits
, black_1_width
, black_1_height
);
909 bm
[2][0] = new Fl_Bitmap(blackking_1_bits
, black_1_width
, black_1_height
);
910 bm
[2][1] = new Fl_Bitmap(blackking_2_bits
, black_1_width
, black_1_height
);
911 bm
[2][2] = new Fl_Bitmap(blackking_3_bits
, black_1_width
, black_1_height
);
912 bm
[2][3] = new Fl_Bitmap(blackking_4_bits
, black_1_width
, black_1_height
);
913 bm
[3][0] = new Fl_Bitmap(whiteking_1_bits
, black_1_width
, black_1_height
);
914 bm
[3][1] = new Fl_Bitmap(whiteking_2_bits
, black_1_width
, black_1_height
);
915 bm
[3][2] = new Fl_Bitmap(whiteking_3_bits
, black_1_width
, black_1_height
);
916 bm
[3][3] = new Fl_Bitmap(whiteking_4_bits
, black_1_width
, black_1_height
);
919 #define ISIZE black_1_width
921 void draw_piece(int which
, int x
, int y
) {
922 if (!fl_not_clipped(x
,y
,ISIZE
,ISIZE
)) return;
924 case BLACK
: which
= 0; break;
925 case WHITE
: which
= 1; break;
926 case BLACKKING
: which
= 2; break;
927 case WHITEKING
: which
= 3; break;
930 fl_color(FL_BLACK
); bm
[which
][0]->draw(x
, y
);
931 fl_color(FL_INACTIVE_COLOR
); bm
[which
][1]->draw(x
, y
);
932 fl_color(FL_SELECTION_COLOR
); bm
[which
][2]->draw(x
, y
);
933 fl_color(FL_WHITE
); bm
[which
][3]->draw(x
, y
);
936 //----------------------------------------------------------------
938 class Board
: public Fl_Double_Window
{
942 void drag_piece(int, int, int);
943 void drop_piece(int);
944 void animate(node
* move
, int backwards
);
945 void computer_move(int);
946 Board(int w
, int h
) : Fl_Double_Window(w
,h
) {color(15);}
951 #define BOARDSIZE (8*BOXSIZE+BORDER)
954 static int erase_this
; // real location of dragging piece, don't draw it
955 static int dragging
; // piece being dragged
956 static int dragx
; // where it is
958 static int showlegal
; // show legal moves
960 int squarex(int i
) {return (usermoves(i
,1)-'A')*BOXSIZE
+BMOFFSET
;}
961 int squarey(int i
) {return (usermoves(i
,2)-'1')*BOXSIZE
+BMOFFSET
;}
965 // -- draw the board itself
966 fl_draw_box(box(),0,0,w(),h(),color());
967 // -- draw all dark tiles
968 fl_color((Fl_Color
)10 /*107*/);
969 int x
; for (x
=0; x
<8; x
++) for (int y
=0; y
<8; y
++) {
970 if (!((x
^y
)&1)) fl_rectf(BORDER
+x
*BOXSIZE
, BORDER
+y
*BOXSIZE
,
971 BOXSIZE
-BORDER
, BOXSIZE
-BORDER
);
973 // -- draw outlines around the fileds
975 for (x
=0; x
<9; x
++) {
976 fl_rectf(x
*BOXSIZE
,0,BORDER
,h());
977 fl_rectf(0,x
*BOXSIZE
,w(),BORDER
);
979 for (int j
= 5; j
< 40; j
++) if (j
!= erase_this
) {
980 draw_piece(b
[j
], squarex(j
), squarey(j
));
985 for (n
= root
->son
; n
; n
= showlegal
==2 ? n
->son
: n
->brother
) {
986 int x1
= squarex(n
->from
)+BOXSIZE
/2-5;
987 int y1
= squarey(n
->from
)+BOXSIZE
/2-5;
988 int x2
= squarex(n
->to
)+BOXSIZE
/2-5;
989 int y2
= squarey(n
->to
)+BOXSIZE
/2-5;
990 fl_line(x1
,y1
,x2
,y2
);
992 fl_mult_matrix(x2
-x1
,y2
-y1
,y1
-y2
,x2
-x1
,x2
,y2
);
1002 fl_font(FL_BOLD
,10);
1003 for (n
= root
->son
; n
; n
= showlegal
==2 ? n
->son
: n
->brother
) {
1004 int x1
= squarex(n
->from
)+BOXSIZE
/2-5;
1005 int y1
= squarey(n
->from
)+BOXSIZE
/2-5;
1006 int x2
= squarex(n
->to
)+BOXSIZE
/2-5;
1007 int y2
= squarey(n
->to
)+BOXSIZE
/2-5;
1008 char buf
[20]; sprintf(buf
,"%d",num
);
1009 fl_draw(buf
, x1
+int((x2
-x1
)*.85)-3, y1
+int((y2
-y1
)*.85)+5);
1013 if (dragging
) draw_piece(dragging
, dragx
, dragy
);
1016 // drag the piece on square i to dx dy, or undo drag if i is zero:
1017 void Board::drag_piece(int j
, int dx
, int dy
) {
1018 dy
= (dy
&-2) | (dx
&1); // make halftone shadows line up
1019 if (j
!= erase_this
) drop_piece(erase_this
); // should not happen
1020 if (!erase_this
) { // pick up old piece
1021 dragx
= squarex(j
); dragy
= squarey(j
);
1025 if (dx
!= dragx
|| dy
!= dragy
) {
1026 damage(FL_DAMAGE_ALL
, dragx
, dragy
, ISIZE
, ISIZE
);
1027 damage(FL_DAMAGE_ALL
, dx
, dy
, ISIZE
, ISIZE
);
1033 // drop currently dragged piece on square i
1034 void Board::drop_piece(int j
) {
1035 if (!erase_this
) return; // should not happen!
1040 if (x
!= dragx
|| y
!= dragy
) {
1041 damage(4, dragx
, dragy
, ISIZE
, ISIZE
);
1042 damage(4, x
, y
, ISIZE
, ISIZE
);
1046 // show move (call this *before* the move, *after* undo):
1047 void Board::animate(node
* move
, int backwards
) {
1048 if (showlegal
) {showlegal
= 0; redraw();}
1052 if (backwards
) {int x
= f
; f
= t
; t
= x
;}
1053 int x1
= squarex(f
);
1054 int y1
= squarey(f
);
1055 int x2
= squarex(t
);
1056 int y2
= squarey(t
);
1058 for (int j
=0; j
<STEPS
; j
++) {
1059 int x
= x1
+(x2
-x1
)*j
/STEPS
;
1060 int y
= y1
+(y2
-y1
)*j
/STEPS
;
1061 drag_piece(move
->from
,x
,y
);
1065 if (move
->jump
) redraw();
1068 int busy
; // causes pop-up abort menu
1070 void Board::computer_move(int help
) {
1071 if (!playing
) return;
1072 cursor(FL_CURSOR_WAIT
);
1074 busy
= 1; abortflag
= 0;
1075 node
* move
= calcmove(root
);
1078 if (!help
&& move
->value
<= -30000) {
1079 fl_message("%s resigns", move
->who
? "White" : "Black");
1080 playing
= autoplay
= 0;
1081 cursor(FL_CURSOR_DEFAULT
);
1089 fl_message("%s has no move", root
->who
? "Black" : "White");
1090 playing
= autoplay
= 0;
1092 if (!autoplay
) cursor(FL_CURSOR_DEFAULT
);
1095 extern Fl_Menu_Item menu
[];
1096 extern Fl_Menu_Item busymenu
[];
1098 int Board::handle(int e
) {
1100 const Fl_Menu_Item
* m
;
1103 m
= busymenu
->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0);
1104 if (m
) m
->do_callback(this, (void*)m
);
1107 m
= busymenu
->test_shortcut();
1108 if (m
) {m
->do_callback(this, (void*)m
); return 1;}
1115 static int deltax
, deltay
;
1117 const Fl_Menu_Item
* m
;
1120 if (Fl::event_button() > 1) {
1121 m
= menu
->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0);
1122 if (m
) m
->do_callback(this, (void*)m
);
1127 for (t
= root
->son
; t
; t
= t
->brother
) {
1128 int x
= squarex(t
->from
);
1129 int y
= squarey(t
->from
);
1130 if (Fl::event_inside(x
,y
,BOXSIZE
,BOXSIZE
)) {
1131 deltax
= Fl::event_x()-x
;
1132 deltay
= Fl::event_y()-y
;
1133 drag_piece(t
->from
,x
,y
);
1140 m
= menu
->test_shortcut();
1141 if (m
) {m
->do_callback(this, (void*)m
); return 1;}
1144 drag_piece(erase_this
, Fl::event_x()-deltax
, Fl::event_y()-deltay
);
1147 // find the closest legal move he dropped it on:
1148 dist
= 50*50; n
= 0;
1149 for (t
= root
->son
; t
; t
= t
->brother
) if (t
->from
==erase_this
) {
1150 int d1
= Fl::event_x()-deltax
-squarex(t
->to
);
1152 d1
= Fl::event_y()-deltay
-squarey(t
->to
);
1154 if (d
< dist
) {dist
= d
; n
= t
;}
1156 if (!n
) {drop_piece(erase_this
); return 1;} // none found
1159 if (showlegal
) {showlegal
= 0; redraw();}
1160 if (n
->jump
) redraw();
1168 void quit_cb(Fl_Widget
*, void*) {exit(0);}
1170 int FLTKmain(int argc
, char** argv
) {
1171 Fl::visual(FL_DOUBLE
|FL_INDEX
);
1172 Board
b(BOARDSIZE
,BOARDSIZE
);
1173 b
.color(FL_BACKGROUND_COLOR
);
1174 b
.callback(quit_cb
);
1179 void autoplay_cb(Fl_Widget
*bp
, void*) {
1180 if (autoplay
) {autoplay
= 0; return;}
1181 if (!playing
) return;
1182 Board
* b
= (Board
*)bp
;
1184 while (autoplay
) {b
->computer_move(0); b
->computer_move(0);}
1187 #include <FL/Fl_Box.H>
1188 Fl_Window
*copyright_window
;
1189 void copyright_cb(Fl_Widget
*, void*) {
1190 if (!copyright_window
) {
1191 copyright_window
= new Fl_Window(400,270,"Copyright");
1192 copyright_window
->color(FL_WHITE
);
1193 Fl_Box
*b
= new Fl_Box(20,0,380,270,copyright
);
1195 b
->align(FL_ALIGN_LEFT
|FL_ALIGN_INSIDE
|FL_ALIGN_WRAP
);
1196 copyright_window
->end();
1198 copyright_window
->hotspot(copyright_window
);
1199 copyright_window
->set_non_modal();
1200 copyright_window
->show();
1203 void debug_cb(Fl_Widget
*, void*v
) {
1205 ((Fl_Menu_Item
*)v
)->flags
=
1206 debug
? FL_MENU_TOGGLE
|FL_MENU_VALUE
: FL_MENU_TOGGLE
;
1209 void forced_cb(Fl_Widget
*b
, void*v
) {
1210 forcejumps
= !forcejumps
;
1211 ((Fl_Menu_Item
*)v
)->flags
=
1212 forcejumps
? FL_MENU_TOGGLE
|FL_MENU_VALUE
: FL_MENU_TOGGLE
;
1213 killnode(root
->son
); root
->son
= 0;
1214 if (showlegal
) {expandnode(root
); b
->redraw();}
1217 void move_cb(Fl_Widget
*pb
, void*) {
1218 Board
* b
= (Board
*)pb
;
1219 if (playing
) b
->computer_move(1);
1220 if (playing
) b
->computer_move(0);
1223 void newgame_cb(Fl_Widget
*b
, void*) {
1229 void legal_cb(Fl_Widget
*pb
, void*) {
1230 if (showlegal
== 1) {showlegal
= 0; ((Board
*)pb
)->redraw(); return;}
1231 if (!playing
) return;
1233 showlegal
= 1; ((Board
*)pb
)->redraw();
1236 void predict_cb(Fl_Widget
*pb
, void*) {
1237 if (showlegal
== 2) {showlegal
= 0; ((Board
*)pb
)->redraw(); return;}
1238 if (playing
) expandnode(root
);
1239 showlegal
= 2; ((Board
*)pb
)->redraw();
1242 void switch_cb(Fl_Widget
*pb
, void*) {
1244 ((Board
*)pb
)->computer_move(0);
1247 void undo_cb(Fl_Widget
*pb
, void*) {
1248 Board
* b
= (Board
*)pb
;
1249 b
->animate(undomove(),1);
1250 b
->animate(undomove(),1);
1253 //--------------------------
1255 #include <FL/Fl_Slider.H>
1256 #include <FL/Fl_Value_Output.H>
1258 Fl_Window
*intel_window
;
1259 Fl_Value_Output
*intel_output
;
1261 void intel_slider_cb(Fl_Widget
*w
, void*) {
1262 double v
= ((Fl_Slider
*)w
)->value();
1264 intel_output
->value(n
);
1265 maxevaluate
= maxnodes
= n
;
1268 void intel_cb(Fl_Widget
*, void*) {
1269 if (!intel_window
) {
1270 intel_window
= new Fl_Window(200,25,"Checkers Intelligence");
1271 Fl_Slider
* s
= new Fl_Slider(60,0,140,25);
1272 s
->type(FL_HOR_NICE_SLIDER
);
1273 s
->minimum(1); s
->maximum(500); s
->value(50);
1274 s
->callback(intel_slider_cb
);
1275 intel_output
= new Fl_Value_Output(0,0,60,25);
1276 intel_output
->value(maxevaluate
);
1277 intel_window
->resizable(s
);
1279 intel_window
->hotspot(intel_window
);
1280 intel_window
->set_non_modal();
1281 intel_window
->show();
1284 //---------------------------
1286 void stop_cb(Fl_Widget
*, void*) {abortflag
= 1;}
1288 void continue_cb(Fl_Widget
*, void*) {}
1290 Fl_Menu_Item menu
[] = {
1291 {"Autoplay", 'a', autoplay_cb
},
1292 {"Legal moves", 'l', legal_cb
},
1293 {"Move for me", 'm', move_cb
},
1294 {"New game", 'n', newgame_cb
},
1295 {"Predict", 'p', predict_cb
},
1296 {"Switch sides", 's', switch_cb
},
1297 {"Undo", 'u', undo_cb
, 0, FL_MENU_DIVIDER
},
1298 {"Forced jumps rule", 'f', forced_cb
, 0, FL_MENU_TOGGLE
|FL_MENU_VALUE
},
1299 {"Debug", 'd', debug_cb
, (void *)"d", FL_MENU_TOGGLE
},
1300 {"Intelligence...", 'i', intel_cb
, 0, FL_MENU_DIVIDER
},
1301 {"Copyright", 'c', copyright_cb
},
1302 {"Quit", 'q', quit_cb
},
1305 Fl_Menu_Item busymenu
[] = {
1306 {"Stop", '.', stop_cb
},
1307 {"Autoplay", 'a', autoplay_cb
},
1308 {"Continue", 0, continue_cb
},
1309 {"Debug", 'd', debug_cb
, (void *)"d", FL_MENU_TOGGLE
},
1310 {"Intelligence...", 'i', intel_cb
},
1311 {"Copyright", 'c', copyright_cb
},
1312 {"Quit", 'q', quit_cb
},
1317 ////////////////////////////////////////////////////////////////
1318 // parts shared by both interface:
1328 int arg(int, char **argv
, int &i
) {
1329 if (argv
[i
][1] == 't') {terminal
= 1; i
++; return 1;}
1334 int didabort(void) {
1349 int main(int argc
, char **argv
) {
1354 if (Fl::args(argc
, argv
, i
, arg
) < argc
) {
1355 fprintf(stderr
," -t : use VT100 display\n", Fl::help
);
1358 if (!getenv("DISPLAY")) terminal
= 1;
1362 return FLTKmain(argc
,argv
);
1370 // End of "$Id: checkers.cxx 8168 2011-01-02 03:55:23Z matt $".