summaryrefslogtreecommitdiff
path: root/test/checkers.cxx
diff options
context:
space:
mode:
authorMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
committerMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
commitf9039b2ae21988783feae9b362818e7923e82d14 (patch)
tree6d6fe3679d73448758f9794e7d4d4f6b22a4adad /test/checkers.cxx
parent67e89232f9ba067825a158734a09e0fa21aacbe3 (diff)
Initial revision
git-svn-id: file:///fltk/svn/fltk/trunk@2 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'test/checkers.cxx')
-rw-r--r--test/checkers.cxx1340
1 files changed, 1340 insertions, 0 deletions
diff --git a/test/checkers.cxx b/test/checkers.cxx
new file mode 100644
index 000000000..fe9aa700f
--- /dev/null
+++ b/test/checkers.cxx
@@ -0,0 +1,1340 @@
+// Hours of fun: the FLTK checkers game!
+// Based on a very old algorithim, but it still works!
+
+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 <malloc.h>
+#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(4, dragx, dragy, ISIZE, ISIZE);
+ damage(4, 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) {
+ 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, "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, "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
+}