1998-10-20 17:25:25 +04:00
|
|
|
//
|
2000-06-06 01:21:24 +04:00
|
|
|
// "$Id: checkers.cxx,v 1.9.2.5 2000/06/05 21:21:16 mike Exp $"
|
1998-10-20 17:25:25 +04:00
|
|
|
//
|
|
|
|
// Checkers game for the Fast Light Tool Kit (FLTK).
|
|
|
|
//
|
1998-10-06 22:21:25 +04:00
|
|
|
// Hours of fun: the FLTK checkers game!
|
|
|
|
// Based on a very old algorithim, but it still works!
|
1998-10-20 17:25:25 +04:00
|
|
|
//
|
2000-04-26 02:17:00 +04:00
|
|
|
// Copyright 1998-2000 by Bill Spitzak and others.
|
1998-10-20 17:25:25 +04:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2000-06-06 01:21:24 +04:00
|
|
|
// Please report all bugs and problems to "fltk-bugs@fltk.org".
|
1998-10-20 17:25:25 +04:00
|
|
|
//
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
1998-12-09 00:04:40 +03:00
|
|
|
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);
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------
|
|
|
|
|
|
|
|
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) {
|
1998-10-21 18:21:44 +04:00
|
|
|
damage(FL_DAMAGE_ALL, dragx, dragy, ISIZE, ISIZE);
|
|
|
|
damage(FL_DAMAGE_ALL, dx, dy, ISIZE, ISIZE);
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
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) {
|
1998-10-21 18:21:44 +04:00
|
|
|
damage(4, dragx, dragy, ISIZE, ISIZE);
|
|
|
|
damage(4, x, y, ISIZE, ISIZE);
|
1998-10-06 22:21:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
1998-10-20 01:00:26 +04:00
|
|
|
Fl::visual(FL_DOUBLE|FL_INDEX);
|
1998-10-06 22:21:25 +04:00
|
|
|
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},
|
1999-08-09 18:38:40 +04:00
|
|
|
{"Debug", 'd', debug_cb, (void *)"d", FL_MENU_TOGGLE},
|
1998-10-06 22:21:25 +04:00
|
|
|
{"Intelligence...", 'i', intel_cb, 0, FL_MENU_DIVIDER},
|
|
|
|
{"Copyright", 'c', copyright_cb},
|
|
|
|
{"Quit", 'q', quit_cb},
|
1999-08-09 18:38:40 +04:00
|
|
|
{0}};
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
Fl_Menu_Item busymenu[] = {
|
|
|
|
{"Stop", '.', stop_cb},
|
|
|
|
{"Autoplay", 'a', autoplay_cb},
|
|
|
|
{"Continue", 0, continue_cb},
|
1999-08-09 18:38:40 +04:00
|
|
|
{"Debug", 'd', debug_cb, (void *)"d", FL_MENU_TOGGLE},
|
1998-10-06 22:21:25 +04:00
|
|
|
{"Intelligence...", 'i', intel_cb},
|
|
|
|
{"Copyright", 'c', copyright_cb},
|
|
|
|
{"Quit", 'q', quit_cb},
|
1999-08-09 18:38:40 +04:00
|
|
|
{0}};
|
1998-10-06 22:21:25 +04:00
|
|
|
|
|
|
|
#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
|
|
|
|
}
|
1998-10-20 17:25:25 +04:00
|
|
|
|
|
|
|
//
|
2000-06-06 01:21:24 +04:00
|
|
|
// End of "$Id: checkers.cxx,v 1.9.2.5 2000/06/05 21:21:16 mike Exp $".
|
1998-10-20 17:25:25 +04:00
|
|
|
//
|