mirror of https://github.com/fltk/fltk.git
FLTK - Fast Light Tool Kit - https://github.com/fltk/fltk - cross platform GUI development
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1369 lines
36 KiB
1369 lines
36 KiB
// |
|
// "$Id: checkers.cxx,v 1.9.2.3 1999/08/09 14:38:40 mike Exp $" |
|
// |
|
// Checkers game for the Fast Light Tool Kit (FLTK). |
|
// |
|
// Hours of fun: the FLTK checkers game! |
|
// Based on a very old algorithim, but it still works! |
|
// |
|
// Copyright 1998-1999 by Bill Spitzak and others. |
|
// |
|
// This library is free software; you can redistribute it and/or |
|
// modify it under the terms of the GNU Library General Public |
|
// License as published by the Free Software Foundation; either |
|
// version 2 of the License, or (at your option) any later version. |
|
// |
|
// This library is distributed in the hope that it will be useful, |
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
// Library General Public License for more details. |
|
// |
|
// You should have received a copy of the GNU Library General Public |
|
// License along with this library; if not, write to the Free Software |
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
|
// USA. |
|
// |
|
// Please report all bugs and problems to "fltk-bugs@easysw.com". |
|
// |
|
|
|
const char* copyright = |
|
"Checkers game\n" |
|
"Copyright (C) 1997 Bill Spitzak spitzak@d2.com\n" |
|
"Original Pascal code:\n" |
|
"Copyright 1978, Oregon Minicomputer Software, Inc.\n" |
|
"2340 SW Canyon Road, Portland, Oregon 97201\n" |
|
"Written by Steve Poulsen 18-Jan-79\n" |
|
"\n" |
|
"This program is free software; you can redistribute it and/or modify " |
|
"it under the terms of the GNU General Public License as published by " |
|
"the Free Software Foundation; either version 2 of the License, or " |
|
"(at your option) any later version.\n" |
|
"\n" |
|
"This program is distributed in the hope that it will be useful, " |
|
"but WITHOUT ANY WARRANTY; without even the implied warranty of " |
|
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " |
|
"GNU General Public License for more details.\n" |
|
"\n" |
|
"You should have received a copy of the GNU Library General Public " |
|
"License along with this library; if not, write to the Free Software " |
|
"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 " |
|
"USA."; |
|
|
|
// Define FLTK to get the fltk interface |
|
// Define VT100 to get the VT100 interface |
|
// Define both to get a program that takes a -t switch |
|
|
|
#define FLTK |
|
//#define VT100 |
|
|
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <stdarg.h> |
|
#include <ctype.h> |
|
#include <time.h> |
|
|
|
//////////////////////////////////////////////////////////////// |
|
// The algorithim: |
|
|
|
int maxevaluate=2500; // max number of moves to examine on a turn |
|
int maxnodes = 2500; // maximum number of nodes in search tree |
|
int maxply = 20; // maximum depth to look ahead |
|
char forcejumps = 1; // is forced jumps rule in effect? |
|
|
|
// scoring parameters: (all divided by 5 from original code) |
|
// some signs seem to be backwards, marked them with (-) in comment |
|
const int spiece = 800; // value of a piece |
|
const int sking = 1200; // value of a king |
|
const int sadvan = 160; // value of mypieces/theirpieces-1 |
|
// const int smobil = ? // moves *enemy* can make w/o being jumped |
|
const int sallpin = 80; // mobil == 0 |
|
const int sdeny = 10; // moves enemy can make that will be jumped |
|
const int spin = 32; // enemy pieces that have no move except jumped |
|
const int sthreat = -10; // enemy pieces we can jump if not moved (-) |
|
const int sgrad = 1; // score of piece positions |
|
const int sback = 10; // back row occupied so enemy can't make king |
|
const int smoc2 = 200; // more mobility, more center |
|
const int smoc3 = -8; // less mobility, less center |
|
const int smoc4 = -80; // more mobility, less center |
|
const int smode2 = -14; // less mobility, less denied |
|
const int smode3 = -40; // more mobility, more denied (-) |
|
const int sdemmo = -20; // more denied, more moves (-) |
|
const int scent = 10; // pieces in center |
|
const int skcent = 100; // kings in center |
|
|
|
const int depthpenalty=4; // guess |
|
const int noise=2; // values less or eq to this apart are eq |
|
|
|
// const int sattackking = 4; // not used |
|
// const int sattackpiece = 3; |
|
|
|
struct node { |
|
node *father; |
|
node *son; // best son |
|
node *brother; // next brother |
|
short int value; // value of this board position to player making move |
|
unsigned char from,to; // the move to reach this board |
|
long int jump; // bit map of locations jumped |
|
unsigned char mobil; |
|
unsigned char deny; |
|
unsigned char pin; |
|
unsigned char threat; |
|
short int gradient; |
|
unsigned who:1; // 0 = black's move, 1 = white's move |
|
unsigned king:1; // 1 = move causes piece to be kinged |
|
unsigned back:1; |
|
unsigned moc2:1; |
|
unsigned moc3:1; |
|
unsigned moc4:1; |
|
unsigned mode2:1; |
|
unsigned mode3:1; |
|
unsigned demmo:1; |
|
}; |
|
|
|
int nodes; // count of nodes |
|
|
|
/* Board positions: Border positions: |
|
|
|
WHITE 00 01 02 03 04 |
|
05 06 07 08 04 XX XX XX XX |
|
09 10 11 12 XX XX XX XX 13 |
|
14 15 16 17 13 XX XX XX XX |
|
18 19 20 21 XX XX XX XX 22 |
|
23 24 25 26 22 XX XX XX XX |
|
27 28 29 30 XX XX XX XX 31 |
|
32 33 34 36 31 XX XX XX XX |
|
36 37 38 39 XX XX XX XX 40 |
|
BLACK 40 41 42 43 44 |
|
|
|
*/ |
|
|
|
typedef char piece; |
|
|
|
// Piece values so that BLACK and WHITE are bit flags: |
|
#define EMPTY 0 |
|
#define BLACK 1 |
|
#define WHITE 2 |
|
#define KING 4 |
|
#define BLACKKING 5 |
|
#define WHITEKING 6 |
|
#define BLUE 8 |
|
|
|
const piece flip[9] = { |
|
EMPTY, WHITE, BLACK, 0, 0, WHITEKING, BLACKKING, 0, BLUE}; |
|
|
|
const int offset[9][4] = { // legal move directions |
|
{0,0,0,0}, |
|
{-5,-4,0,0}, |
|
{4,5,0,0}, |
|
{0,0,0,0}, |
|
{0,0,0,0}, |
|
{4,5,-4,-5}, |
|
{4,5,-4,-5}, |
|
{0,0,0,0}, |
|
{0,0,0,0} |
|
}; |
|
|
|
piece b[45]; // current board position being considered |
|
|
|
int evaluated; // number of moves evaluated this turn |
|
|
|
char centralsquares[45]; |
|
char is_protected[45]; |
|
|
|
piece flipboard[45]; // swapped if enemy is black |
|
piece *tb; // pointer to real or swapped board |
|
#define FRIEND BLACK |
|
#define FRIENDKING BLACKKING |
|
#define ENEMY WHITE |
|
#define ENEMYKING WHITEKING |
|
|
|
char check(int target,int direction) { |
|
// see if enemy at target can be jumped from direction by our piece |
|
int dst = target-direction; |
|
if (tb[dst]) return(0); |
|
int src = target+direction; |
|
if (tb[src] == FRIENDKING); |
|
else if (direction < 0 || tb[src] != FRIEND) return(0); |
|
piece a = tb[target]; piece b = tb[src]; |
|
tb[target] = EMPTY; tb[src] = EMPTY; |
|
int safe = |
|
(tb[src-4]&FRIEND && tb[src-8]&ENEMY |
|
||tb[src-5]&FRIEND && tb[src-10]&ENEMY |
|
||tb[dst-4]&ENEMY && !tb[dst+4] |
|
||tb[dst-5]&ENEMY && !tb[dst+5] |
|
||tb[src+4]&FRIEND && tb[src+8]==ENEMYKING |
|
||tb[src+5]&FRIEND && tb[src+10]==ENEMYKING |
|
||tb[dst+4]==ENEMYKING && !tb[dst-4] |
|
||tb[dst+5]==ENEMYKING && !tb[dst-5]); |
|
tb[target] = a; tb[src] = b; |
|
return(safe); |
|
} |
|
|
|
int deniedmoves,undeniedmoves; |
|
void analyzemove(int direction,int src) { |
|
int target = src+direction; |
|
if (!tb[target]) { |
|
if (!tb[target+direction]) is_protected[target] = 1; |
|
piece a = tb[src]; tb[src] = EMPTY; |
|
if (check(target,4) || check(target,5) || |
|
check(target,-4) || check(target,-5) || |
|
(tb[src+4]&ENEMY && check(src+4,4)) || |
|
(tb[src+5]&ENEMY && check(src+5,5)) || |
|
(tb[src-4]&ENEMY && check(src-4,-4)) || |
|
(tb[src-5]&ENEMY && check(src-5,-5))) |
|
deniedmoves++; |
|
else undeniedmoves++; |
|
tb[src] = a; |
|
} |
|
} |
|
|
|
void evaluateboard(node *n,int print) { |
|
|
|
if (!n->who) tb = b; // move was black's |
|
else { |
|
for (int i=0; i<45; i++) flipboard[44-i] = flip[b[i]]; |
|
tb = flipboard; |
|
} |
|
|
|
memset(is_protected,0,sizeof(is_protected)); |
|
int friendpieces = 0; |
|
int enemypieces = 0; |
|
int friendkings = 0; |
|
int enemykings = 0; |
|
int friendkcent = 0; |
|
int friendcent = 0; |
|
int enemykcent = 0; |
|
int enemycent = 0; |
|
n->mobil = n->deny = n->pin = n->threat = 0; |
|
|
|
int i; |
|
for (i=5; i<40; i++) switch(tb[i]) { |
|
case ENEMYKING: |
|
enemykings++; |
|
enemykcent += centralsquares[i]; |
|
deniedmoves = 0; |
|
undeniedmoves = 0; |
|
if (i>8) { |
|
analyzemove(-4,i); |
|
analyzemove(-5,i); |
|
} |
|
goto J1; |
|
case ENEMY: |
|
deniedmoves = 0; |
|
undeniedmoves = 0; |
|
J1: enemypieces++; |
|
enemycent += centralsquares[i]; |
|
if (i<36) { |
|
analyzemove(4,i); |
|
analyzemove(5,i); |
|
} |
|
if (deniedmoves && !undeniedmoves) n->pin++; |
|
n->deny += deniedmoves; |
|
n->mobil += undeniedmoves; |
|
break; |
|
case FRIENDKING: |
|
friendkings++; |
|
friendkcent += centralsquares[i]; |
|
if (tb[i+4]&ENEMY && !tb[i+8] && !(tb[i+4]==ENEMYKING && !tb[i-4])) |
|
n->threat++; |
|
if (tb[i+5]&ENEMY && !tb[i+10] && !(tb[i+5]==ENEMYKING && !tb[i-5])) |
|
n->threat++; |
|
case FRIEND: |
|
friendpieces++; |
|
friendcent += centralsquares[i]; |
|
if (tb[i-4]&ENEMY && !tb[i-8] && tb[i+4]) n->threat++; |
|
if (tb[i-5]&ENEMY && !tb[i-10] && tb[i+5]) n->threat++; |
|
break; |
|
} |
|
|
|
int gradient[40]; |
|
for (i=4; i<9; i++) gradient[i] = tb[i] ? 0 : 32; |
|
int total = 0; |
|
for (i=9; i<40; i++) { |
|
int x = (gradient[i-4]+gradient[i-5])/2; |
|
if (tb[i]==FRIEND) total += x; |
|
gradient[i] = (tb[i]&FRIEND || !tb[i] && !is_protected[i]) ? x : 0; |
|
} |
|
n->gradient = total; |
|
|
|
n->back = tb[39]==FRIEND && tb[37]==FRIEND && !enemykings; |
|
|
|
node* f = n->father; |
|
|
|
n->moc2 = f->mobil>n->mobil && friendcent>enemycent; |
|
n->moc3 = f->mobil<=n->mobil && friendcent<enemycent; |
|
n->moc4 = f->mobil>n->mobil && friendcent<enemycent; |
|
n->mode2 = f->mobil<=n->mobil && n->deny<f->deny; |
|
n->mode3 = f->mobil>n->mobil && n->deny>f->deny; |
|
n->demmo = n->deny>f->deny && f->deny+f->mobil>n->deny+n->mobil; |
|
|
|
total = |
|
spiece * (friendpieces - enemypieces) + |
|
(sking-spiece) * (friendkings - enemykings) + |
|
// mobil? |
|
sdeny * (n->deny - f->deny) + |
|
spin * (n->pin - f->pin) + |
|
sthreat * (n->threat - f->threat) + |
|
sgrad * (n->gradient - f->gradient) + |
|
sback * (n->back - f->back) + |
|
smoc2 * (n->moc2 - f->moc2) + |
|
smoc3 * (n->moc3 - f->moc3) + |
|
smoc4 * (n->moc4 - f->moc4) + |
|
smode2 * (n->mode2 - f->mode2) + |
|
smode3 * (n->mode3 - f->mode3) + |
|
sdemmo * (n->demmo - f->demmo) + |
|
scent * (friendcent - enemycent) + |
|
(skcent-scent) * (friendkcent - enemykcent); |
|
if (!n->mobil) total += sallpin; |
|
|
|
if (!enemypieces) total = 30000; |
|
else if (friendpieces > enemypieces) |
|
total += (sadvan*friendpieces)/enemypieces-sadvan; |
|
else total -= (sadvan*enemypieces)/friendpieces-sadvan; |
|
|
|
if (print) { |
|
printf("\tParent\tNew\tScore\n"); |
|
printf("pieces\t%d\t%d\t%d\n",enemypieces,friendpieces, |
|
spiece*(friendpieces-enemypieces)); |
|
printf("kings\t%d\t%d\t%d\n",enemykings,friendkings, |
|
(sking-spiece)*(friendkings-enemykings)); |
|
printf("mobil\t%d\t%d\n",f->mobil,n->mobil); |
|
printf("deny\t%d\t%d\t%d\n",f->deny,n->deny,sdeny*(n->deny-f->deny)); |
|
printf("pin\t%d\t%d\t%d\n",f->pin,n->pin,spin*(n->pin-f->pin)); |
|
printf("threat\t%d\t%d\t%d\n",f->threat,n->threat,sthreat*(n->threat-f->threat)); |
|
printf("grad\t%d\t%d\t%d\n",f->gradient,n->gradient,sgrad*(n->gradient-f->gradient)); |
|
printf("back\t%d\t%d\t%d\n",f->back,n->back,sback*(n->back-f->back)); |
|
printf("moc2\t%d\t%d\t%d\n",f->moc2,n->moc2,smoc2*(n->moc2-f->moc2)); |
|
printf("moc3\t%d\t%d\t%d\n",f->moc3,n->moc3,smoc3*(n->moc3-f->moc3)); |
|
printf("moc4\t%d\t%d\t%d\n",f->moc4,n->moc4,smoc4*(n->moc4-f->moc4)); |
|
printf("mode2\t%d\t%d\t%d\n",f->mode2,n->mode2,smode2*(n->mode2-f->mode2)); |
|
printf("mode3\t%d\t%d\t%d\n",f->mode3,n->mode3,smode3*(n->mode3-f->mode3)); |
|
printf("demmo\t%d\t%d\t%d\n",f->demmo,n->demmo,sdemmo*(n->demmo-f->demmo)); |
|
printf("cent\t%d\t%d\t%dn",enemycent,friendcent,scent*(friendcent-enemycent)); |
|
printf("kcent\t%d\t%d\t%d\n",enemykcent,friendkcent,skcent*(friendkcent-enemykcent)); |
|
printf("total:\t\t\t%d\n",total); |
|
} |
|
else { |
|
n->value = total; |
|
evaluated++; |
|
} |
|
} // end of evaluateboard |
|
|
|
// --------------------- Tree management ----------------- |
|
|
|
node *freelist; |
|
|
|
node *newnode(void) { |
|
node *n; |
|
if (freelist) { |
|
n = freelist; |
|
freelist = n->brother; |
|
} |
|
else n = (node *)malloc(sizeof(node)); |
|
memset(n,0,sizeof(node)); |
|
nodes++; |
|
return(n); |
|
} |
|
|
|
void extract(node *n) { |
|
node* i = n->father; |
|
if (i) { |
|
node* j = i->son; |
|
if (j==n) i->son = n->brother; |
|
else while (j) { |
|
i = j; j = j->brother; |
|
if (j==n) {i->brother = n->brother; break;} |
|
} |
|
} |
|
n->brother = 0; |
|
} |
|
|
|
void killnode(node *x) { |
|
if (!x) return; |
|
node *y; |
|
for (y = x; ; y = y->brother) { |
|
nodes--; |
|
killnode(y->son); y->son = 0; |
|
if (!y->brother) break; |
|
} |
|
y->brother = freelist; |
|
freelist = x; |
|
} |
|
|
|
int seed; // current random number |
|
|
|
void insert(node *n) { |
|
int val = n->value; |
|
node **pp; |
|
for (pp = &(n->father->son); *pp; pp = &((*pp)->brother)) { |
|
int val1 = (*pp)->value; |
|
if (abs(val-val1) <= noise) { |
|
seed = (seed*13077+5051)%0100000; |
|
if ((seed & 070) >= 060) break; |
|
} |
|
else if (val > val1) break; |
|
} |
|
n->brother = *pp; |
|
*pp = n; |
|
} |
|
|
|
// -------------------------------------------------------------- |
|
|
|
void movepiece(node* f, int i, node* jnode) { |
|
static char jumphappened; |
|
|
|
for (int k=0; k<4; k++) { |
|
int direction = offset[b[i]][k]; |
|
if (!direction) break; |
|
int j = i+direction; |
|
if (b[j] == EMPTY) { |
|
if (!jnode && (!forcejumps || !f->son || !f->son->jump)) { |
|
node* n = newnode(); |
|
n->father = f; |
|
n->who = !f->who; |
|
n->from = i; |
|
n->to = j; |
|
piece oldpiece = b[i]; b[i] = EMPTY; |
|
if (!(oldpiece&KING) && n->who ? (j>=36) : (j<=8)) { |
|
n->king = 1; |
|
b[j] = oldpiece|KING; |
|
} |
|
else b[j] = oldpiece; |
|
evaluateboard(n,0); |
|
insert(n); |
|
b[i] = oldpiece; b[j] = EMPTY; |
|
} |
|
} else if (((b[j]^b[i])&(WHITE|BLACK))==(WHITE|BLACK) && !b[j+direction]) { |
|
if (forcejumps && f->son && !f->son->jump) { |
|
killnode(f->son); |
|
f->son = 0; |
|
} |
|
int jumploc = j; |
|
j += direction; |
|
node* n = newnode(); |
|
n->father = f; |
|
n->who = !f->who; |
|
n->from = i; |
|
n->to = j; |
|
n->jump = (1<<(jumploc-10)); |
|
piece oldpiece = b[i]; b[i] = EMPTY; |
|
if (!(oldpiece&KING) && n->who ? (j>=36) : (j<=8)) { |
|
n->king = 1; |
|
b[j] = oldpiece|KING; |
|
} |
|
else b[j] = oldpiece; |
|
if (jnode) { |
|
n->from = jnode->from; |
|
n->jump |= jnode->jump; |
|
n->king |= jnode->king; |
|
} |
|
piece jumpedpiece = b[jumploc]; |
|
b[jumploc] = EMPTY; |
|
jumphappened = 0; |
|
movepiece(f,j,n); |
|
if (forcejumps && jumphappened) killnode(n); |
|
else {evaluateboard(n,0); insert(n);} |
|
b[i] = oldpiece; b[j] = EMPTY; |
|
b[jumploc] = jumpedpiece; |
|
jumphappened = 1; |
|
} |
|
} |
|
} |
|
|
|
void expandnode(node *f) { |
|
if (f->son || f->value > 28000) return; // already done |
|
piece turn = f->who ? BLACK : WHITE; |
|
for (int i=5; i<40; i++) if (b[i]&turn) movepiece(f,i,0); |
|
if (f->son) { |
|
f->value = -f->son->value; |
|
if (f->brother) f->value -= depthpenalty; |
|
} |
|
else f->value = 30000; |
|
} |
|
|
|
void makemove(node *n) { |
|
b[n->to] = b[n->from]; |
|
if (n->king) b[n->to] |= KING; |
|
b[n->from] = EMPTY; |
|
if (n->jump) for(int i=0; i<32; i++) { |
|
if (n->jump & (1<<i)) b[10+i] = EMPTY; |
|
} |
|
} |
|
|
|
int didabort(void); |
|
|
|
int fullexpand(node *f, int level) { |
|
if (didabort() || nodes > maxnodes-(maxply*10) || evaluated > maxevaluate) return(0); |
|
expandnode(f); |
|
if (!f->son) return(1); |
|
piece oldboard[45]; |
|
memmove(oldboard,b,sizeof(b)); |
|
node* n = f->son; |
|
if (!n->jump && n->brother) {if (level<1) return(1); level--;} |
|
int i; |
|
node* sons[32]; for (i=0; (sons[i++] = n); n = n->brother); |
|
int ret = 1; |
|
for (i=0; ret && (n = sons[i++]);) { |
|
makemove(n); |
|
ret = fullexpand(n,level); |
|
memmove(b,oldboard,sizeof(b)); |
|
extract(n); |
|
insert(n); |
|
} |
|
f->value = -f->son->value; |
|
return(ret); |
|
} |
|
|
|
int descend(node *f) { |
|
static int depth; |
|
if (didabort() || nodes > maxnodes || depth >= maxply) return(0); |
|
if (f->son) { |
|
node* n = f->son; |
|
makemove(n); |
|
depth++; |
|
int ret = descend(n); |
|
depth--; |
|
extract(n); |
|
insert(n); |
|
f->value = -f->son->value; |
|
return(ret); |
|
} |
|
else {expandnode(f); return(1);} |
|
} |
|
|
|
char debug; |
|
|
|
node *calcmove(node *root) { // return best move after root |
|
expandnode(root); |
|
if (!root->son) return(0); // no move due to loss |
|
if (debug) printf("calcmove() initial nodes = %d\n",nodes); |
|
evaluated = 0; |
|
if (root->son->brother) { |
|
int x; |
|
for (x = 1; abs(root->value)<28000 && fullexpand(root,x); x++); |
|
piece saveboard[45]; memmove(saveboard,b,sizeof(b)); |
|
while (abs(root->value)<28000) { |
|
x = descend(root); |
|
memmove(b,saveboard,sizeof(b)); |
|
if (!x) break; |
|
} |
|
} |
|
if (debug) printf(" evaluated %d, nodes = %d\n", evaluated, nodes); |
|
return(root->son); |
|
} |
|
|
|
// the actual game state ---------------- |
|
|
|
node *root,*undoroot; |
|
|
|
piece jumpboards[24][45]; // saved boards for undoing jumps |
|
int nextjump; |
|
|
|
char user; // 0 = black, 1 = white |
|
char playing; |
|
char autoplay; |
|
|
|
void newgame(void) { |
|
|
|
int n; |
|
for (n=0; n<5; n++) b[n] = BLUE; |
|
for (n=5; n<18; n++) b[n] = WHITE; |
|
for (n=18; n<27; n++) b[n] = EMPTY; |
|
for (n=27; n<40; n++) b[n] = BLACK; |
|
for (n=40; n<45; n++) b[n] = BLUE; |
|
b[13] = b[22] = b[31] = BLUE; |
|
|
|
centralsquares[15] = centralsquares[16] = |
|
centralsquares[19] = centralsquares[20] = |
|
centralsquares[24] = centralsquares[25] = |
|
centralsquares[28] = centralsquares[29] = 1; |
|
|
|
// set up initial search tree: |
|
nextjump = 0; |
|
killnode(undoroot); |
|
undoroot = root = newnode(); |
|
|
|
// make it white's move, so first move is black: |
|
root->who = 1; |
|
user = 0; |
|
playing = 1; |
|
} |
|
|
|
void domove(node* move) { |
|
if (move->jump) memmove(jumpboards[nextjump++],b,sizeof(b)); |
|
makemove(move); |
|
extract(move); |
|
killnode(root->son); |
|
root->son = move; |
|
root = move; |
|
if (debug) evaluateboard(move,1); |
|
} |
|
|
|
node* undomove() { |
|
node *n = root; |
|
if (n == undoroot) return 0; // no more undo possible |
|
if (n->jump) memmove(b,jumpboards[--nextjump],sizeof(b)); |
|
else { |
|
b[n->from] = b[n->to]; |
|
if (n->king) b[n->from] &= (WHITE|BLACK); |
|
b[n->to] = EMPTY; |
|
} |
|
root = n->father; |
|
killnode(n); |
|
root->son = 0; |
|
root->value = 0; // prevent it from thinking game is over |
|
playing = 1; |
|
if (root == undoroot) user = 0; |
|
return n; |
|
} |
|
|
|
const char _usermoves[] = |
|
"B1D1F1H1A2C2E2G2??B3D3F3H3A4C4E4G4??B5D5F5H5A6C6E6G6??B7D7F7H7A8C8E8G8??"; |
|
#define usermoves(x,y) _usermoves[2*((x)-5)+(y)-1] |
|
|
|
void dumpnode(node *n, int help) { |
|
int x = n->from; |
|
int y = n->to; |
|
if (help) printf("%c%c %c%c\t- ", |
|
usermoves(x,1),usermoves(x,2), |
|
usermoves(y,1),usermoves(y,2)); |
|
printf("%s %ss from %c%c to %c%c", |
|
n->who ? "White" : "Black", |
|
n->jump ? "jump" : "move", |
|
usermoves(x,1),usermoves(x,2), |
|
usermoves(y,1),usermoves(y,2)); |
|
if (n->jump) { |
|
for (int i=0; i<32; i++) if (n->jump & (1<<i)) |
|
printf(", %c%c",usermoves(10+i,1),usermoves(10+i,2)); |
|
printf(" removed"); |
|
} |
|
printf(" (%+d).\n",n->value); |
|
} |
|
|
|
int abortflag; |
|
|
|
//////////////////////////////////////////////////////////////// |
|
// VT100 Interface: |
|
#ifdef VT100 |
|
|
|
void positioncursor(int i) { |
|
printf("\033[%d;%dH", |
|
usermoves(i,2)-'0'+1, |
|
2*(usermoves(i,1)-'A')+1); |
|
} |
|
|
|
void outpiecename(piece n) { |
|
printf(n&BLACK ? "\033[1;7m" : "\033[1m"); |
|
putchar(" BW??BW??"[n]); |
|
putchar(" BW??KK??"[n]); |
|
printf("\033[0m"); |
|
} |
|
|
|
void VT100board(void) { |
|
printf("\033<\033[H\033[J\033[10r"); |
|
int l = 0; |
|
puts(" A B C D E F G H"); |
|
for (int i=0; i<4; i++) { |
|
int j = 9*i+5; |
|
int k; |
|
for (k=0; k<4; k++) { |
|
printf("\033[7m \033[0m"); |
|
outpiecename(b[j+k]); |
|
} |
|
l++; |
|
printf("%d\n",l); |
|
j += 4; |
|
for (k=0; k<4; k++) { |
|
outpiecename(b[j+k]); |
|
printf("\033[7m \033[0m"); |
|
} |
|
l++; |
|
printf("%d\n",l); |
|
} |
|
} |
|
|
|
void VT100move(node *n, int) { |
|
if (!n) return; |
|
printf("\0337"); |
|
positioncursor(n->from); |
|
outpiecename(b[n->from]); |
|
positioncursor(n->to); |
|
outpiecename(b[n->to]); |
|
if (n->jump) for(int i=0; i<32; i++) { |
|
if (n->jump & (1<<i)) { |
|
positioncursor(10+i); |
|
outpiecename(b[10+i]); |
|
} |
|
} |
|
printf("\0338"); |
|
} |
|
|
|
int decode(char *m) { |
|
int i; |
|
for(i=5; i<=40; i++) |
|
if (toupper(m[0])==usermoves(i,1) && m[1]==usermoves(i,2)) return(i); |
|
return(0); |
|
} |
|
|
|
#include <signal.h> |
|
|
|
static void sigint(...) { |
|
abortflag = 1; |
|
signal(SIGINT,sigint); |
|
} |
|
|
|
void fixexit(int x) { |
|
printf("\0337\033[r\0338"); |
|
exit(x); |
|
} |
|
|
|
// Returns a son, or 0 if no move specified, or root to cause "help" |
|
node *getusermove(void) { |
|
int i,j; |
|
node *t; |
|
char line[100],*m1,*m2; |
|
|
|
if (playing) |
|
printf("\033[1m%s's move?\033[0m ",root->who ? "Black" : "White"); |
|
else |
|
printf("\033[1mCommand?\033[0m "); |
|
abortflag = 0; |
|
if (!gets(line)) { |
|
putchar('\n'); |
|
if (feof(stdin)) fixexit(0); |
|
return 0; |
|
} |
|
for (m1 = line; *m1 && *m1<=' '; m1++); |
|
if (!*m1) return(0); |
|
m2 = m1+1; |
|
if (*m2) m2++; |
|
for (; *m2 && *m2<'0'; m2++); |
|
if (playing && m1[1]>='0' && m1[1]<='9') { |
|
i = decode(m1); |
|
j = decode(m2); |
|
if (i && j) for (t = root->son; t; t = t->brother) |
|
if (t->from == i && t->to == j) return(t); |
|
puts("Valid moves are:"); |
|
m1[0] = 'L'; |
|
} |
|
switch(toupper(m1[0])) { |
|
case 0: return(0); |
|
case 'A': |
|
if (playing) autoplay = 1; |
|
return(root); |
|
case 'C': |
|
puts(copyright); |
|
break; |
|
case 'D': |
|
debug = !debug; |
|
printf("Debug is now %s.", debug ? "on" : "off"); |
|
break; |
|
case 'F': |
|
forcejumps = !forcejumps; |
|
printf("Forced jumps rule is now %s.",forcejumps ? "on" : "off"); |
|
killnode(root->son); root->son = 0; |
|
return(0); |
|
case 'L': |
|
expandnode(root); |
|
if (playing) for (t = root->son; t; t = t->brother) dumpnode(t,1); |
|
break; |
|
case 'M': |
|
return(playing ? root : 0); |
|
case 'N': |
|
newgame(); |
|
VT100board(); |
|
return(0); |
|
case 'P': |
|
printf("I expect the following moves:\n"); |
|
for (t = root->son; t; t = t->son) dumpnode(t,0); |
|
break; |
|
case 'Q': |
|
fixexit(0); |
|
case 'R': |
|
VT100board(); |
|
break; |
|
case 'S': |
|
user = !user; |
|
return(root); |
|
case 'U': |
|
VT100move(undomove(),1); |
|
VT100move(undomove(),1); |
|
return(0); |
|
case '+': |
|
maxevaluate = maxnodes = 2*maxevaluate; |
|
goto J2; |
|
case '-': |
|
if (maxevaluate > 1) |
|
maxevaluate = maxnodes = maxevaluate/2; |
|
J2: printf("Moves evaluated set to %d.",maxevaluate); |
|
break; |
|
default: |
|
puts( |
|
"A(utoplay)\n" |
|
"C(opyright)\n" |
|
"D(ebug on/off)\n" |
|
"F(orce jumps rule on/off)\n" |
|
"L(ist legal moves)\n" |
|
"M(ake a move for me)\n" |
|
"N(ew game)\n" |
|
"P(redict next few moves)\n" |
|
"Q(uit)\n" |
|
"R(edraw screen)\n" |
|
"S(witch sides)\n" |
|
"U(ndo)\n" |
|
"+ - smarter\n" |
|
"- - stupider"); |
|
expandnode(root); |
|
for (t = root->son; t; t = t->brother) dumpnode(t,1); |
|
} |
|
return(0); |
|
} |
|
|
|
int VT100main() { |
|
signal(SIGINT,sigint); |
|
VT100board(); |
|
for (;;) { |
|
if (playing) { |
|
expandnode(root); |
|
if (!root->son) { |
|
printf("%s has no move. Game over.",root->who ? "Black" : "White"); |
|
playing = autoplay = 0; |
|
} |
|
} |
|
node* move; |
|
if (playing && (autoplay || root->who == user)) { |
|
move = calcmove(root); |
|
if (move->value <= -30000) { |
|
printf("%s resigns.", move->who ? "White" : "Black"); |
|
move = 0; |
|
playing = autoplay = 0; |
|
} |
|
} else { |
|
move = getusermove(); |
|
if (move == root) move = calcmove(root); |
|
} |
|
if (move) { |
|
dumpnode(move,0); |
|
domove(move); |
|
VT100move(move,0); |
|
} |
|
} |
|
} |
|
|
|
#endif |
|
|
|
//////////////////////////////////////////////////////////////// |
|
// fltk interface: |
|
#ifdef FLTK |
|
|
|
#include <FL/Fl.H> |
|
#include <FL/Fl_Double_Window.H> |
|
#include <FL/Fl_Bitmap.H> |
|
#include <FL/fl_draw.H> |
|
#include <FL/Fl_Menu_Item.H> |
|
#include <FL/fl_ask.H> |
|
|
|
//---------------------------------------------------------------- |
|
// old 4-level NeXT images have been seperated into bitmaps so they |
|
// can be drawn with arbitrary colors and real transparency. This is |
|
// rather tedious and perhaps fltk should provide a direct support |
|
// to do this: |
|
|
|
#include "black_1.xbm" |
|
#include "black_2.xbm" |
|
#include "black_3.xbm" |
|
#include "black_4.xbm" |
|
#include "white_1.xbm" |
|
#include "white_2.xbm" |
|
#include "white_3.xbm" |
|
#include "white_4.xbm" |
|
#include "blackking_1.xbm" |
|
#include "blackking_2.xbm" |
|
#include "blackking_3.xbm" |
|
#include "blackking_4.xbm" |
|
#include "whiteking_1.xbm" |
|
#include "whiteking_2.xbm" |
|
#include "whiteking_3.xbm" |
|
#include "whiteking_4.xbm" |
|
|
|
Fl_Bitmap *bm[4][4]; |
|
|
|
void make_bitmaps() { |
|
if (bm[0][0]) return; |
|
bm[0][0] = new Fl_Bitmap(black_1_bits, black_1_width, black_1_height); |
|
bm[0][1] = new Fl_Bitmap(black_2_bits, black_1_width, black_1_height); |
|
bm[0][2] = new Fl_Bitmap(black_3_bits, black_1_width, black_1_height); |
|
bm[0][3] = new Fl_Bitmap(black_4_bits, black_1_width, black_1_height); |
|
bm[1][0] = new Fl_Bitmap(white_1_bits, black_1_width, black_1_height); |
|
bm[1][1] = new Fl_Bitmap(white_2_bits, black_1_width, black_1_height); |
|
bm[1][2] = new Fl_Bitmap(white_3_bits, black_1_width, black_1_height); |
|
bm[1][3] = new Fl_Bitmap(white_4_bits, black_1_width, black_1_height); |
|
bm[2][0] = new Fl_Bitmap(blackking_1_bits, black_1_width, black_1_height); |
|
bm[2][1] = new Fl_Bitmap(blackking_2_bits, black_1_width, black_1_height); |
|
bm[2][2] = new Fl_Bitmap(blackking_3_bits, black_1_width, black_1_height); |
|
bm[2][3] = new Fl_Bitmap(blackking_4_bits, black_1_width, black_1_height); |
|
bm[3][0] = new Fl_Bitmap(whiteking_1_bits, black_1_width, black_1_height); |
|
bm[3][1] = new Fl_Bitmap(whiteking_2_bits, black_1_width, black_1_height); |
|
bm[3][2] = new Fl_Bitmap(whiteking_3_bits, black_1_width, black_1_height); |
|
bm[3][3] = new Fl_Bitmap(whiteking_4_bits, black_1_width, black_1_height); |
|
} |
|
|
|
#define ISIZE black_1_width |
|
|
|
void draw_piece(int which, int x, int y) { |
|
if (!fl_not_clipped(x,y,ISIZE,ISIZE)) return; |
|
switch (which) { |
|
case BLACK: which = 0; break; |
|
case WHITE: which = 1; break; |
|
case BLACKKING: which = 2; break; |
|
case WHITEKING: which = 3; break; |
|
default: return; |
|
} |
|
fl_color(FL_BLACK); bm[which][0]->draw(x, y); |
|
fl_color(FL_INACTIVE_COLOR); bm[which][1]->draw(x, y); |
|
fl_color(FL_SELECTION_COLOR); bm[which][2]->draw(x, y); |
|
fl_color(FL_WHITE); bm[which][3]->draw(x, y); |
|
} |
|
|
|
//---------------------------------------------------------------- |
|
|
|
class Board : public Fl_Double_Window { |
|
void draw(); |
|
int handle(int); |
|
public: |
|
void drag_piece(int, int, int); |
|
void drop_piece(int); |
|
void animate(node* move, int backwards); |
|
void computer_move(int); |
|
Board(int w, int h) : Fl_Double_Window(w,h) {color(15);} |
|
}; |
|
|
|
#define BOXSIZE 52 |
|
#define BORDER 4 |
|
#define BOARDSIZE (8*BOXSIZE+BORDER) |
|
#define BMOFFSET 5 |
|
|
|
static int erase_this; // real location of dragging piece, don't draw it |
|
static int dragging; // piece being dragged |
|
static int dragx; // where it is |
|
static int dragy; |
|
static int showlegal; // show legal moves |
|
|
|
int squarex(int i) {return (usermoves(i,1)-'A')*BOXSIZE+BMOFFSET;} |
|
int squarey(int i) {return (usermoves(i,2)-'1')*BOXSIZE+BMOFFSET;} |
|
|
|
void Board::draw() { |
|
make_bitmaps(); |
|
fl_draw_box(box(),0,0,w(),h(),color()); |
|
fl_color((Fl_Color)10 /*107*/); |
|
int x; for (x=0; x<8; x++) for (int y=0; y<8; y++) { |
|
if (!((x^y)&1)) fl_rectf(BORDER+x*BOXSIZE, BORDER+y*BOXSIZE, |
|
BOXSIZE-BORDER, BOXSIZE-BORDER); |
|
} |
|
fl_color(FL_DARK3 /*FL_GRAY_RAMP+4*/); |
|
for (x=0; x<9; x++) { |
|
fl_rectf(x*BOXSIZE,0,BORDER,h()); |
|
fl_rectf(0,x*BOXSIZE,w(),BORDER); |
|
} |
|
for (int i = 5; i < 40; i++) if (i != erase_this) { |
|
draw_piece(b[i], squarex(i), squarey(i)); |
|
} |
|
if (showlegal) { |
|
fl_color(FL_WHITE); |
|
node* n; |
|
for (n = root->son; n; n = showlegal==2 ? n->son : n->brother) { |
|
int x1 = squarex(n->from)+BOXSIZE/2-5; |
|
int y1 = squarey(n->from)+BOXSIZE/2-5; |
|
int x2 = squarex(n->to)+BOXSIZE/2-5; |
|
int y2 = squarey(n->to)+BOXSIZE/2-5; |
|
fl_line(x1,y1,x2,y2); |
|
fl_push_matrix(); |
|
fl_mult_matrix(x2-x1,y2-y1,y1-y2,x2-x1,x2,y2); |
|
fl_begin_polygon(); |
|
fl_vertex(0,0); |
|
fl_vertex(-.3, .1); |
|
fl_vertex(-.3, -.1); |
|
fl_end_polygon(); |
|
fl_pop_matrix(); |
|
} |
|
int num = 1; |
|
fl_color(FL_BLACK); |
|
fl_font(FL_BOLD,10); |
|
for (n = root->son; n; n = showlegal==2 ? n->son : n->brother) { |
|
int x1 = squarex(n->from)+BOXSIZE/2-5; |
|
int y1 = squarey(n->from)+BOXSIZE/2-5; |
|
int x2 = squarex(n->to)+BOXSIZE/2-5; |
|
int y2 = squarey(n->to)+BOXSIZE/2-5; |
|
char buf[20]; sprintf(buf,"%d",num); |
|
fl_draw(buf, x1+int((x2-x1)*.85)-3, y1+int((y2-y1)*.85)+5); |
|
num++; |
|
} |
|
} |
|
if (dragging) draw_piece(dragging, dragx, dragy); |
|
} |
|
|
|
// drag the piece on square i to dx dy, or undo drag if i is zero: |
|
void Board::drag_piece(int i, int dx, int dy) { |
|
dy = (dy&-2) | dx&1; // make halftone shadows line up |
|
if (i != erase_this) drop_piece(erase_this); // should not happen |
|
if (!erase_this) { // pick up old piece |
|
dragx = squarex(i); dragy = squarey(i); |
|
erase_this = i; |
|
dragging = b[i]; |
|
} |
|
if (dx != dragx || dy != dragy) { |
|
damage(FL_DAMAGE_ALL, dragx, dragy, ISIZE, ISIZE); |
|
damage(FL_DAMAGE_ALL, dx, dy, ISIZE, ISIZE); |
|
} |
|
dragx = dx; |
|
dragy = dy; |
|
} |
|
|
|
// drop currently dragged piece on square i |
|
void Board::drop_piece(int i) { |
|
if (!erase_this) return; // should not happen! |
|
erase_this = 0; |
|
dragging = 0; |
|
int x = squarex(i); |
|
int y = squarey(i); |
|
if (x != dragx || y != dragy) { |
|
damage(4, dragx, dragy, ISIZE, ISIZE); |
|
damage(4, x, y, ISIZE, ISIZE); |
|
} |
|
} |
|
|
|
// show move (call this *before* the move, *after* undo): |
|
void Board::animate(node* move, int backwards) { |
|
if (showlegal) {showlegal = 0; redraw();} |
|
if (!move) return; |
|
int f = move->from; |
|
int t = move->to; |
|
if (backwards) {int x = f; f = t; t = x;} |
|
int x1 = squarex(f); |
|
int y1 = squarey(f); |
|
int x2 = squarex(t); |
|
int y2 = squarey(t); |
|
const int STEPS=35; |
|
for (int i=0; i<STEPS; i++) { |
|
int x = x1+(x2-x1)*i/STEPS; |
|
int y = y1+(y2-y1)*i/STEPS; |
|
drag_piece(move->from,x,y); |
|
Fl::flush(); |
|
} |
|
drop_piece(t); |
|
if (move->jump) redraw(); |
|
} |
|
|
|
int busy; // causes pop-up abort menu |
|
|
|
void message(const char* m, ...) { |
|
char buffer[2048]; |
|
va_list a; |
|
va_start(a,m); |
|
vsprintf(buffer, m, a); |
|
va_end(a); |
|
fl_message(buffer); |
|
} |
|
|
|
void Board::computer_move(int help) { |
|
if (!playing) return; |
|
cursor(FL_CURSOR_WAIT); |
|
Fl::flush(); |
|
busy = 1; abortflag = 0; |
|
node* move = calcmove(root); |
|
busy = 0; |
|
if (move) { |
|
if (!help && move->value <= -30000) { |
|
message("%s resigns", move->who ? "White" : "Black"); |
|
playing = autoplay = 0; |
|
cursor(FL_CURSOR_DEFAULT); |
|
return; |
|
} |
|
animate(move,0); |
|
domove(move); |
|
} |
|
expandnode(root); |
|
if (!root->son) { |
|
message("%s has no move", root->who ? "Black" : "White"); |
|
playing = autoplay = 0; |
|
} |
|
if (!autoplay) cursor(FL_CURSOR_DEFAULT); |
|
} |
|
|
|
extern Fl_Menu_Item menu[]; |
|
extern Fl_Menu_Item busymenu[]; |
|
|
|
int Board::handle(int e) { |
|
if (busy) { |
|
const Fl_Menu_Item* m; |
|
switch(e) { |
|
case FL_PUSH: |
|
m = busymenu->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0); |
|
if (m) m->do_callback(this, (void*)m); |
|
return 1; |
|
case FL_SHORTCUT: |
|
m = busymenu->test_shortcut(); |
|
if (m) {m->do_callback(this, (void*)m); return 1;} |
|
return 0; |
|
default: |
|
return 0; |
|
} |
|
} |
|
node *t, *n; |
|
static int deltax, deltay; |
|
int dist; |
|
const Fl_Menu_Item* m; |
|
switch (e) { |
|
case FL_PUSH: |
|
if (Fl::event_button() > 1) { |
|
m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0); |
|
if (m) m->do_callback(this, (void*)m); |
|
return 1; |
|
} |
|
if (playing) { |
|
expandnode(root); |
|
for (t = root->son; t; t = t->brother) { |
|
int x = squarex(t->from); |
|
int y = squarey(t->from); |
|
if (Fl::event_inside(x,y,BOXSIZE,BOXSIZE)) { |
|
deltax = Fl::event_x()-x; |
|
deltay = Fl::event_y()-y; |
|
drag_piece(t->from,x,y); |
|
return 1; |
|
} |
|
} |
|
} |
|
return 0; |
|
case FL_SHORTCUT: |
|
m = menu->test_shortcut(); |
|
if (m) {m->do_callback(this, (void*)m); return 1;} |
|
return 0; |
|
case FL_DRAG: |
|
drag_piece(erase_this, Fl::event_x()-deltax, Fl::event_y()-deltay); |
|
return 1; |
|
case FL_RELEASE: |
|
// find the closest legal move he dropped it on: |
|
dist = 50*50; n = 0; |
|
for (t = root->son; t; t = t->brother) if (t->from==erase_this) { |
|
int d1 = Fl::event_x()-deltax-squarex(t->to); |
|
int d = d1*d1; |
|
d1 = Fl::event_y()-deltay-squarey(t->to); |
|
d += d1*d1; |
|
if (d < dist) {dist = d; n = t;} |
|
} |
|
if (!n) {drop_piece(erase_this); return 1;} // none found |
|
drop_piece(n->to); |
|
domove(n); |
|
if (showlegal) {showlegal = 0; redraw();} |
|
if (n->jump) redraw(); |
|
computer_move(0); |
|
return 1; |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
void quit_cb(Fl_Widget*, void*) {exit(0);} |
|
|
|
int FLTKmain(int argc, char** argv) { |
|
Fl::visual(FL_DOUBLE|FL_INDEX); |
|
Board b(BOARDSIZE,BOARDSIZE); |
|
b.callback(quit_cb); |
|
b.show(argc,argv); |
|
return Fl::run(); |
|
} |
|
|
|
void autoplay_cb(Fl_Widget*bp, void*) { |
|
if (autoplay) {autoplay = 0; return;} |
|
if (!playing) return; |
|
Board* b = (Board*)bp; |
|
autoplay = 1; |
|
while (autoplay) {b->computer_move(0); b->computer_move(0);} |
|
} |
|
|
|
#include <FL/Fl_Box.H> |
|
Fl_Window *copyright_window; |
|
void copyright_cb(Fl_Widget*, void*) { |
|
if (!copyright_window) { |
|
copyright_window = new Fl_Window(400,270,"Copyright"); |
|
copyright_window->color(FL_WHITE); |
|
Fl_Box *b = new Fl_Box(20,0,380,270,copyright); |
|
b->labelsize(10); |
|
b->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP); |
|
copyright_window->end(); |
|
} |
|
copyright_window->hotspot(copyright_window); |
|
copyright_window->set_non_modal(); |
|
copyright_window->show(); |
|
} |
|
|
|
void debug_cb(Fl_Widget*, void*v) { |
|
debug = !debug; |
|
((Fl_Menu_Item*)v)->flags = |
|
debug ? FL_MENU_TOGGLE|FL_MENU_VALUE : FL_MENU_TOGGLE; |
|
} |
|
|
|
void forced_cb(Fl_Widget*b, void*v) { |
|
forcejumps = !forcejumps; |
|
((Fl_Menu_Item*)v)->flags = |
|
forcejumps ? FL_MENU_TOGGLE|FL_MENU_VALUE : FL_MENU_TOGGLE; |
|
killnode(root->son); root->son = 0; |
|
if (showlegal) {expandnode(root); b->redraw();} |
|
} |
|
|
|
void move_cb(Fl_Widget*pb, void*) { |
|
Board* b = (Board*)pb; |
|
if (playing) b->computer_move(1); |
|
if (playing) b->computer_move(0); |
|
} |
|
|
|
void newgame_cb(Fl_Widget*b, void*) { |
|
showlegal = 0; |
|
newgame(); |
|
b->redraw(); |
|
} |
|
|
|
void legal_cb(Fl_Widget*pb, void*) { |
|
if (showlegal == 1) {showlegal = 0; ((Board*)pb)->redraw(); return;} |
|
if (!playing) return; |
|
expandnode(root); |
|
showlegal = 1; ((Board*)pb)->redraw(); |
|
} |
|
|
|
void predict_cb(Fl_Widget*pb, void*) { |
|
if (showlegal == 2) {showlegal = 0; ((Board*)pb)->redraw(); return;} |
|
if (playing) expandnode(root); |
|
showlegal = 2; ((Board*)pb)->redraw(); |
|
} |
|
|
|
void switch_cb(Fl_Widget*pb, void*) { |
|
user = !user; |
|
((Board*)pb)->computer_move(0); |
|
} |
|
|
|
void undo_cb(Fl_Widget*pb, void*) { |
|
Board* b = (Board*)pb; |
|
b->animate(undomove(),1); |
|
b->animate(undomove(),1); |
|
} |
|
|
|
//-------------------------- |
|
|
|
#include <FL/Fl_Slider.H> |
|
#include <FL/Fl_Value_Output.H> |
|
|
|
Fl_Window *intel_window; |
|
Fl_Value_Output *intel_output; |
|
|
|
void intel_slider_cb(Fl_Widget*w, void*) { |
|
double v = ((Fl_Slider*)w)->value(); |
|
int n = int(v*v); |
|
intel_output->value(n); |
|
maxevaluate = maxnodes = n; |
|
} |
|
|
|
void intel_cb(Fl_Widget*, void*) { |
|
if (!intel_window) { |
|
intel_window = new Fl_Window(200,25,"Checkers Intelligence"); |
|
Fl_Slider* s = new Fl_Slider(60,0,140,25); |
|
s->type(FL_HOR_NICE_SLIDER); |
|
s->minimum(1); s->maximum(500); s->value(50); |
|
s->callback(intel_slider_cb); |
|
intel_output = new Fl_Value_Output(0,0,60,25); |
|
intel_output->value(maxevaluate); |
|
intel_window->resizable(s); |
|
} |
|
intel_window->hotspot(intel_window); |
|
intel_window->set_non_modal(); |
|
intel_window->show(); |
|
} |
|
|
|
//--------------------------- |
|
|
|
void stop_cb(Fl_Widget*, void*) {abortflag = 1;} |
|
|
|
void continue_cb(Fl_Widget*, void*) {} |
|
|
|
Fl_Menu_Item menu[] = { |
|
{"Autoplay", 'a', autoplay_cb}, |
|
{"Legal moves", 'l', legal_cb}, |
|
{"Move for me", 'm', move_cb}, |
|
{"New game", 'n', newgame_cb}, |
|
{"Predict", 'p', predict_cb}, |
|
{"Switch sides", 's', switch_cb}, |
|
{"Undo", 'u', undo_cb, 0, FL_MENU_DIVIDER}, |
|
{"Forced jumps rule", 'f', forced_cb, 0, FL_MENU_TOGGLE|FL_MENU_VALUE}, |
|
{"Debug", 'd', debug_cb, (void *)"d", FL_MENU_TOGGLE}, |
|
{"Intelligence...", 'i', intel_cb, 0, FL_MENU_DIVIDER}, |
|
{"Copyright", 'c', copyright_cb}, |
|
{"Quit", 'q', quit_cb}, |
|
{0}}; |
|
|
|
Fl_Menu_Item busymenu[] = { |
|
{"Stop", '.', stop_cb}, |
|
{"Autoplay", 'a', autoplay_cb}, |
|
{"Continue", 0, continue_cb}, |
|
{"Debug", 'd', debug_cb, (void *)"d", FL_MENU_TOGGLE}, |
|
{"Intelligence...", 'i', intel_cb}, |
|
{"Copyright", 'c', copyright_cb}, |
|
{"Quit", 'q', quit_cb}, |
|
{0}}; |
|
|
|
#endif |
|
|
|
//////////////////////////////////////////////////////////////// |
|
// parts shared by both interface: |
|
|
|
#ifdef FLTK |
|
#ifdef VT100 |
|
#define BOTH |
|
#endif |
|
#endif |
|
|
|
#ifdef BOTH |
|
int terminal; |
|
int arg(int, char **argv, int &i) { |
|
if (argv[i][1] == 't') {terminal = 1; i++; return 1;} |
|
return 0; |
|
} |
|
#endif |
|
|
|
int didabort(void) { |
|
#ifdef FLTK |
|
#ifdef BOTH |
|
if (!terminal) |
|
#endif |
|
Fl::check(); |
|
#endif |
|
if (abortflag) { |
|
autoplay = 0; |
|
abortflag = 0; |
|
return 1; |
|
} |
|
return(0); |
|
} |
|
|
|
int main(int argc, char **argv) { |
|
seed = time(0); |
|
newgame(); |
|
#ifdef BOTH |
|
int i = 1; |
|
if (Fl::args(argc, argv, i, arg) < argc) { |
|
fprintf(stderr," -t : use VT100 display\n", Fl::help); |
|
exit(1); |
|
} |
|
if (!getenv("DISPLAY")) terminal = 1; |
|
if (!terminal) |
|
#endif |
|
#ifdef FLTK |
|
return FLTKmain(argc,argv); |
|
#endif |
|
#ifdef VT100 |
|
return VT100main(); |
|
#endif |
|
} |
|
|
|
// |
|
// End of "$Id: checkers.cxx,v 1.9.2.3 1999/08/09 14:38:40 mike Exp $". |
|
//
|
|
|