diff options
Diffstat (limited to 'kenolaba/Board.cpp')
-rw-r--r-- | kenolaba/Board.cpp | 1493 |
1 files changed, 1493 insertions, 0 deletions
diff --git a/kenolaba/Board.cpp b/kenolaba/Board.cpp new file mode 100644 index 00000000..b8546fbc --- /dev/null +++ b/kenolaba/Board.cpp @@ -0,0 +1,1493 @@ +/* Class Board + * + * with methods for + * - play/take back moves + * - generate allowed moves + * - calculate rating for position + * - search for best move + * + * Josef Weidendorfer, 28.8.97 +*/ + +#include <stdio.h> + +#include <qdatetime.h> +#include <qstrlist.h> + +#include <kconfig.h> +#include <krandomsequence.h> + +#include "Board.h" +#include "EvalScheme.h" + +// #define MYTRACE 1 + +#if 0 +#define CHECK(b) Q_ASSERT(b) +#else +#define CHECK(b) +#endif + + +static int ratedPositions, wonPositions, searchCalled, moveCount; +static int normalCount, pushCount, outCount, cutoffCount; + +/*********************** Class PrincipalVariation *************************/ + +void PrincipalVariation::clear(int d) +{ + int i,j; + + for(i=0;i<maxDepth;i++) + for(j=0;j<maxDepth;j++) { + move[i][j].type = Move::none; + } + actMaxDepth = (d<maxDepth) ? d:maxDepth-1; +} + +void PrincipalVariation::update(int d, Move& m) +{ + int i; + + if (d>actMaxDepth) return; + for(i=d+1;i<=actMaxDepth;i++) { + move[d][i]=move[d+1][i]; + move[d+1][i].type = Move::none; + } + move[d][d]=m; +} + + + +/****************************** Class Board ****************************/ + + +/* Board for start of a game */ +int Board::startBoard[]={ + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 1, 1, 1, 1, 1, 10, 10, 10, 10, 10, + 10, 1, 1, 1, 1, 1, 1, 10, 10, 10, 10, + 10, 0, 0, 1, 1, 1, 0, 0, 10, 10, 10, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 10, 10, 10, 0, 0, 2, 2, 2, 0, 0, 10, + 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; + + +/* first centrum of board, then rings around (numbers are indexes) */ +int Board::order[]={ + 60, + 61,72,71,59,48,49, + 62,73,84,83,82,70,58,47,36,37,38,50, + 63,74,85,96,95,94,93,81,69,57,46,35,24,25,26,27,39,51, + 64,75,86,97,108,107,106,105,104,92,80,68,56,45,34,23,12, + 13,14,15,16,28,40,52 }; + +/* See EvalScheme.{h|cpp} + * + // Ratings for fields are calculated out of these values + // (see setFieldValues) + int Board::ringValue[] = { 45, 35, 25, 10, 0 }; + int Board::ringDiff[] = { 0, 10, 10, 8, 5 }; + + // Value added to board rating according to the difference of + // stones in game of player1 and player2 + int Board::stoneValue[]= { 0,-800,-1800,-3000,-4400,-6000 }; + + // Default Values for moves values (see Move.h) + int Board::moveValue[]= { 40,30,30, 15,14,13, 5,5,5, 2,2,2, 1 }; + + // Default Values for inARow values (see Move.h) + int Board::inARowValue[]= { 2, 5, 4, 3 }; +*/ + +int Board::fieldValue[61]; +int Board::direction[]= { -11,1,12,11,-1,-12,-11,1 }; + + +Board::Board() +{ + clear(); + breakOut = bUpdateSpy = false; + spyLevel = 1; + spyDepth = 0; + debug = 0; + realMaxDepth = 1; + _evalScheme = 0; +} + +void Board::setEvalScheme(EvalScheme* scheme) +{ + if (!scheme) + scheme = new EvalScheme( QString("Default") ); + + _evalScheme = scheme; + setFieldValues(); +} + +void Board::setFieldValues() +{ + if (!_evalScheme) return; + + int i, j = 0, k = 59; + int ringValue[5], ringDiff[5]; + + for(i=0;i<5;i++) { + ringDiff[i] = _evalScheme->ringDiff(i); + ringValue[i] = _evalScheme->ringValue(i); + if (ringDiff[i]<1) ringDiff[i]=1; + } + + fieldValue[0] = ringValue[0]; + for(i=1;i<7;i++) + fieldValue[i] = ringValue[1] + ((j+=k) % ringDiff[1]); + for(i=7;i<19;i++) + fieldValue[i] = ringValue[2] + ((j+=k) % ringDiff[2]); + for(i=19;i<37;i++) + fieldValue[i] = ringValue[3] + ((j+=k) % ringDiff[3]); + for(i=37;i<61;i++) + fieldValue[i] = ringValue[4] + ((j+=k) % ringDiff[4]); +} + + + +void Board::begin(int startColor) +{ + int i; + + for(i=0;i<AllFields;i++) + field[i] = startBoard[i]; + storedFirst = storedLast = 0; + color = startColor; + color1Count = color2Count = 14; + random.setSeed(0); // Initialize random sequence +} + +void Board::clear() +{ + int i; + + for(i=0;i<AllFields;i++) + field[i] = (startBoard[i] == out) ? out: free; + storedFirst = storedLast = 0; + color1Count = color2Count = 0; +} + +/* generate moves starting at field <startField> */ +void Board::generateFieldMoves(int startField, MoveList& list) +{ + int d, dir, c, actField; + bool left, right; + int opponent = (color == color1) ? color2 : color1; + + Q_ASSERT( field[startField] == color ); + + /* 6 directions */ + for(d=1;d<7;d++) { + dir = direction[d]; + + /* 2nd field */ + c = field[actField = startField+dir]; + if (c == free) { + /* (c .) */ + list.insert(startField, d, Move::move1); + continue; + } + if (c != color) + continue; + + /* 2nd == color */ + + left = (field[startField+direction[d-1]] == free); + if (left) { + left = (field[actField+direction[d-1]] == free); + if (left) + /* 2 left */ + list.insert(startField, d, Move::left2); + } + + right = (field[startField+direction[d+1]] == free); + if (right) { + right = (field[actField+direction[d+1]] == free); + if (right) + /* 2 right */ + list.insert(startField, d, Move::right2); + } + + /* 3rd field */ + c = field[actField += dir]; + if (c == free) { + /* (c c .) */ + list.insert(startField, d, Move::move2); + continue; + } + else if (c == opponent) { + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c o .) */ + list.insert(startField, d, Move::push1with2); + } + else if (c == out) { + /* (c c o |) */ + list.insert(startField, d, Move::out1with2); + } + continue; + } + if (c != color) + continue; + + /* 3nd == color */ + + if (left) { + if (field[actField+direction[d-1]] == free) + /* 3 left */ + list.insert(startField, d, Move::left3); + } + + if (right) { + if (field[actField+direction[d+1]] == free) + /* 3 right */ + list.insert(startField, d, Move::right3); + } + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c .) */ + list.insert(startField, d, Move::move3); + continue; + } + if (c != opponent) + continue; + + /* 4nd == opponent */ + + /* 5. field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c o .) */ + list.insert(startField, d, Move::push1with3); + continue; + } + else if (c == out) { + /* (c c c o |) */ + list.insert(startField, d, Move::out1with3); + continue; + } + if (c != opponent) + continue; + + /* 5nd == opponent */ + + /* 6. field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c o o .) */ + list.insert(startField, d, Move::push2); + } + else if (c == out) { + /* (c c c o o |) */ + list.insert(startField, d, Move::out2); + } + } +} + + +void Board::generateMoves(MoveList& list) +{ + int actField, f; + + for(f=0;f<RealFields;f++) { + actField = order[f]; + if ( field[actField] == color) + generateFieldMoves(actField, list); + } +} + + + +void Board::playMove(const Move& m) +{ + int f, dir, dir2; + int opponent = (color == color1) ? color2:color1; + + CHECK( isConsistent() ); + + if (++storedLast == MvsStored) storedLast = 0; + + /* Buffer full -> delete oldest entry */ + if (storedLast == storedFirst) + if (++storedFirst == MvsStored) storedFirst = 0; + + storedMove[storedLast] = m; + + f = m.field; + CHECK( (m.type >= 0) && (m.type < Move::none)); + CHECK( field[f] == color ); + field[f] = free; + dir = direction[m.direction]; + + switch(m.type) { + case Move::out2: /* (c c c o o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == out ); + field[f + 3*dir] = color; + break; + case Move::out1with3: /* (c c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == out ); + field[f + 3*dir] = color; + break; + case Move::move3: /* (c c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == free ); + field[f + 3*dir] = color; + break; + case Move::out1with2: /* (c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == opponent ); + CHECK( field[f + 3*dir] == out ); + field[f + 2*dir] = color; + break; + case Move::move2: /* (c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == free ); + field[f + 2*dir] = color; + break; + case Move::push2: /* (c c c o o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == free ); + field[f + 3*dir] = color; + field[f + 5*dir] = opponent; + break; + case Move::left3: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + CHECK( field[f + 2*dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::right3: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + CHECK( field[f + 2*dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::push1with3: /* (c c c o .) => (. c c c o) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == free ); + field[f + 3*dir] = color; + field[f + 4*dir] = opponent; + break; + case Move::push1with2: /* (c c o .) => (. c c o) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == opponent ); + CHECK( field[f + 3*dir] == free ); + field[f + 2*dir] = color; + field[f + 3*dir] = opponent; + break; + case Move::left2: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::right2: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::move1: /* (c .) => (. c) */ + CHECK( field[f + dir] == free ); + field[f + dir] = color; + break; + default: + break; + } + + if (m.isOutMove()) { + if (color == color1) + color2Count--; + else + color1Count--; + } + + /* change actual color */ + color = opponent; + + CHECK( isConsistent() ); + +} + +bool Board::takeBack() +{ + int f, dir, dir2; + int opponent = color; + Move& m = storedMove[storedLast]; + + CHECK( isConsistent() ); + + if (storedFirst == storedLast) return false; + + /* change actual color */ + color = (color == color1) ? color2:color1; + + if (m.isOutMove()) { + if (color == color1) + color2Count++; + else + color1Count++; + } + + f = m.field; + CHECK( field[f] == free ); + field[f] = color; + dir = direction[m.direction]; + + switch(m.type) { + case Move::out2: /* (. c c c o |) => (c c c o o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == out ); + field[f + 3*dir] = opponent; + break; + case Move::out1with3: /* (. c c c |) => (c c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == out ); + field[f + 3*dir] = opponent; + break; + case Move::move3: /* (. c c c) => (c c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + field[f + 3*dir] = free; + break; + case Move::out1with2: /* (. c c | ) => (c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == out ); + field[f + 2*dir] = opponent; + break; + case Move::move2: /* (. c c) => (c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + field[f + 2*dir] = free; + break; + case Move::push2: /* (. c c c o o) => (c c c o o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == opponent ); + field[f + 3*dir] = opponent; + field[f + 5*dir] = free; + break; + case Move::left3: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + 2*dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + CHECK( field[f + 2*dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::right3: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + 2*dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + CHECK( field[f + 2*dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::push1with3: /* (. c c c o) => (c c c o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == opponent ); + field[f + 3*dir] = opponent; + field[f + 4*dir] = free; + break; + case Move::push1with2: /* (. c c o) => (c c o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + field[f + 2*dir] = opponent; + field[f + 3*dir] = free; + break; + case Move::left2: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::right2: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::move1: /* (. c) => (c .) */ + CHECK( field[f + dir] == color ); + field[f + dir] = free; + break; + default: + break; + } + + if (--storedLast < 0) storedLast = MvsStored-1; + + CHECK( isConsistent() ); + + return true; +} + +int Board::movesStored() +{ + int c = storedLast - storedFirst; + if (c<0) c+= MvsStored; + return c; +} + + +/** countFrom + * + * Used for board evaluation to count allowed move types and + * connectiveness. VERY similar to move generation. + * + * Returns number of found moves + */ +void Board::countFrom(int startField, int color, + MoveTypeCounter& TCounter, + InARowCounter& CCounter) +{ + int d, dir, c, actField, c2; + bool left, right; + + /* 6 directions */ + for(d=1;d<7;d++) { + dir = direction[d]; + + /* 2nd field */ + c = field[actField = startField+dir]; + if (c == free) { + TCounter.incr( Move::move1 ); + continue; + } + + if (c != color) + continue; + + /* 2nd == color */ + + CCounter.incr( InARowCounter::inARow2 ); + + /* left side move 2 */ + left = (field[startField+direction[d-1]] == free); + if (left) { + left = (field[actField+direction[d-1]] == free); + if (left) + TCounter.incr( Move::left2 ); + } + + /* right side move 2 */ + right = (field[startField+direction[d+1]] == free); + if (right) { + right = (field[actField+direction[d+1]] == free); + if (right) + TCounter.incr( Move::right2 ); + } + + /* 3rd field */ + c = field[actField += dir]; + if (c == free) { + /* (c c .) */ + TCounter.incr( Move::move2 ); + continue; + } + else if (c == out) { + continue; + } + else if (c != color) { + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c o .) */ + TCounter.incr( Move::push1with2 ); + } + else if (c == out) { + /* (c c o |) */ + TCounter.incr( Move::out1with2 ); + } + continue; + } + + /* 3nd == color */ + + CCounter.incr( InARowCounter::inARow3 ); + + /* left side move 3 */ + if (left) { + left = (field[actField+direction[d-1]] == free); + if (left) + TCounter.incr( Move::left3 ); + } + + /* right side move 3 */ + if (right) { + right = (field[actField+direction[d+1]] == free); + if (right) + TCounter.incr( Move::right3 ); + } + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c .) */ + TCounter.incr( Move::move3 ); + continue; + } + else if (c == out) { + continue; + } + else if (c != color) { + + /* 4nd == opponent */ + + /* 5. field */ + c2 = field[actField += dir]; + if (c2 == free) { + /* (c c c o .) */ + TCounter.incr( Move::push1with3 ); + continue; + } + else if (c2 == out) { + /* (c c c o |) */ + TCounter.incr( Move::out1with3 ); + continue; + } + if (c2 != c) + continue; + + /* 5nd == opponent */ + + /* 6. field */ + c2 = field[actField += dir]; + if (c2 == free) { + /* (c c c o o .) */ + TCounter.incr( Move::push2 ); + } + else if (c2 == out) { + /* (c c c o o |) */ + TCounter.incr( Move::out2 ); + } + + continue; + } + + /* 4nd == color */ + + CCounter.incr( InARowCounter::inARow4 ); + + /* 5th field */ + c = field[actField += dir]; + if (c != color) + continue; + + /* 4nd == color */ + + CCounter.incr( InARowCounter::inARow5 ); + } +} + +/** indent + * + * Internal: for debugging output only + */ +void indent(int d) +{ + char tmp[]=" "; + tmp[d*3] = 0; + printf("> %s",tmp); +} + + + +/** validState + * + * Check for a valid board position to play from: + * (1) Number of balls for each color has to be between 9 and 14 + * (2) There must be a move possible for actual color + */ +int Board::validState() +{ + MoveTypeCounter tc; + InARowCounter cc; + + int c1 = 0, c2 = 0; + int i,j, moveCount, res; + + for(i=0;i<AllFields;i++) { + j=field[i]; + if (j == color1) c1++; + if (j == color2) c2++; + if (j == color) + countFrom( i, color, tc, cc); + } + + color1Count = c1; + color2Count = c2; + moveCount = tc.sum(); + + if (c1==0 && c2==0) + res = empty; + else if (c1>8 && c1<15 && c2>8 && c2<15 && moveCount>0 ) + res = valid; + else + res = invalid; + +#ifdef MYTRACE + if (spyLevel>2) { + indent(spyDepth); + printf("Valid: %s (Color1 %d, Color2 %d, moveCount of %d: %d)\n", + (res == empty) ? "empty" : (res==valid) ? "valid":"invalid", + c1,c2,color,moveCount); + } +#endif + + return res; +} + + + +/* Calculate a evaluation for actual position + * + * A higher value means a better position for opponent + * NB: This means a higher value for better position of + * 'color before last move' + */ +int Board::calcEvaluation() +{ + MoveTypeCounter tcColor, tcOpponent; + InARowCounter ccColor, ccOpponent; + + int f,i,j; + + /* different evaluation types */ + int fieldValueSum=0, stoneValueSum=0; + int moveValueSum=0, inARowValueSum=0; + int valueSum; + + /* First check simple winner condition */ + if (color1Count <9) + valueSum = (color==color1) ? 16000 : -16000; + else if (color2Count <9) + valueSum = (color==color2) ? 16000 : -16000; + else { + + /* Calculate fieldValueSum and count move types and connectivity */ + for(i=0;i<RealFields;i++) { + j=field[f=order[i]]; + if (j == free) continue; + if (j == color) { + countFrom( f, j, tcColor, ccColor ); + fieldValueSum -= fieldValue[i]; + } + else { + countFrom( f, j, tcOpponent, ccOpponent ); + fieldValueSum += fieldValue[i]; + } + } + + /* If color can't do any moves, opponent wins... */ + if (tcColor.sum() == 0) + valueSum = 16000; + else { + + for(int t=0;t < Move::typeCount;t++) + moveValueSum += _evalScheme->moveValue(t) * + (tcOpponent.get(t) - tcColor.get(t)); + + for(int i=0;i < InARowCounter::inARowCount;i++) + inARowValueSum += _evalScheme->inARowValue(i) * + (ccOpponent.get(i) - ccColor.get(i)); + + if (color == color2) + stoneValueSum = _evalScheme->stoneValue(14 - color1Count) - + _evalScheme->stoneValue(14 - color2Count); + else + stoneValueSum = _evalScheme->stoneValue(14 - color2Count) - + _evalScheme->stoneValue(14 - color1Count); + + valueSum = fieldValueSum + moveValueSum + + inARowValueSum + stoneValueSum; + } + } + +#ifdef MYTRACE + if (spyLevel>2) { + indent(spyDepth); + printf("Eval %d (field %d, move %d, inARow %d, stone %d)\n", + valueSum, fieldValueSum, moveValueSum, + inARowValueSum, stoneValueSum ); + } +#endif + + return valueSum; +} + +bool Board::isConsistent() +{ + int c1 = 0, c2 = 0; + int i,j; + + for(i=0;i<RealFields;i++) { + j=field[order[i]]; + if (j == color1) c1++; + if (j == color2) c2++; + } + return (color1Count == c1 && color2Count == c2); +} + +void Board::changeEvaluation() +{ + int i,tmp; + + /* innermost ring */ + tmp=fieldValue[1]; + for(i=1;i<6;i++) + fieldValue[i] = fieldValue[i+1]; + fieldValue[6] = tmp; + + tmp=fieldValue[7]; + for(i=7;i<18;i++) + fieldValue[i] = fieldValue[i+1]; + fieldValue[18] = tmp; + + tmp=fieldValue[19]; + for(i=19;i<36;i++) + fieldValue[i] = fieldValue[i+1]; + fieldValue[36] = tmp; + + /* the outermost ring */ + tmp=fieldValue[37]; + for(i=37;i<60;i++) + fieldValue[i] = fieldValue[i+1]; + fieldValue[60] = tmp; +} + +/* +void Board::showHist() +{ + Move m1, m2; + + printf("After playing "); + (m1=lastMove()).print(); + print(); + printf("TakeBack "); m1.print(); + takeBack(); print(); + printf("TakeBack "); + (m2=lastMove()).print(); + takeBack(); print(); + printf("Play "); m2.print(); + playMove(m2); print(); + printf("Play "); m1.print(); + playMove(m1); print(); + getchar(); +} +*/ + + + +/* + * We always try the maximize the board value + */ +int Board::search(int depth, int alpha, int beta) +{ + int actValue= -14999+depth, value; + Move m; + MoveList list; + bool depthPhase, doDepthSearch; + + searchCalled++; + + /* We make a depth search for the following move types... */ + int maxType = (depth < maxDepth-1) ? Move::maxMoveType() : + (depth < maxDepth) ? Move::maxPushType() : + Move::maxOutType(); + + generateMoves(list); + +#ifdef MYTRACE + + int oldRatedPositions; + int oldWonPositions; + int oldSearchCalled; + int oldMoveCount; + int oldNormalCount; + int oldPushCount; + int oldOutCount; + int oldCutoffCount; + + spyDepth = depth; + + moveCount += list.getLength(); + + /* + if (spyLevel>1) { + indent(depth); + printf("%s (%6d .. %6d) MaxType %s\n", depth, + (color==color1)?"O":"X", alpha,beta, + (depth < maxDepth-1) ? "Moving" : + (depth < maxDepth)? "Pushing" : "PushOUT" ); + } + */ +#endif + + /* check for a old best move in main combination */ + if (inPrincipalVariation) { + m = pv[depth]; + + if ((m.type != Move::none) && + (!list.isElement(m, 0, true))) + m.type = Move::none; + + if (m.type == Move::none) + inPrincipalVariation = false; + +#ifdef MYTRACE + else { + if (spyLevel>1) { + indent(spyDepth); + printf("Got from pv !\n" ); + } + } +#endif + } + + // first, play all moves with depth search + depthPhase = true; + + while (1) { + + // get next move + if (m.type == Move::none) { + if (depthPhase) + depthPhase = list.getNext(m, maxType); + if (!depthPhase) + if (!list.getNext(m, Move::none)) break; + } + // we could start with a non-depth move from principal variation + doDepthSearch = depthPhase && (m.type <= maxType); + +#ifdef MYTRACE + + if (m.isOutMove()) outCount++; + else if (m.isPushMove()) pushCount++; + else normalCount++; + + if (doDepthSearch) { + oldRatedPositions = ratedPositions; + oldWonPositions = wonPositions; + oldSearchCalled = searchCalled; + oldMoveCount = moveCount; + oldNormalCount = normalCount; + oldPushCount = pushCount; + oldOutCount = outCount; + oldCutoffCount = cutoffCount; + + if (spyLevel>1) { + indent(spyDepth); + printf("%s [%6d .. %6d] ", + (color==color1)?"O":"X", alpha,beta); + m.print(); + printf("\n"); + } + +#ifdef SPION + if (bUpdateSpy) emit update(depth, 0, m, false); +#endif + } +#endif + + playMove(m); + if (!isValid()) { + /* Possibility (1) to win: Ball Count <9 */ + value = 14999-depth; + // value = ((depth < maxDepth) ? 15999:14999) - depth; +#ifdef MYTRACE + wonPositions++; +#endif + } + else { + + if (doDepthSearch) { + /* opponent searches for his maximum; but we want the + * minimum: so change sign (for alpha/beta window too!) + */ + value = - search(depth+1,-beta,-alpha); + } + else { + ratedPositions++; + + value = calcEvaluation(); + } + } + takeBack(); + + /* For GUI response */ + if (doDepthSearch && (maxDepth - depth >2)) + emit searchBreak(); + +#ifdef MYTRACE + + if (doDepthSearch) { + spyDepth = depth; + + if (spyLevel>1) { + + indent(spyDepth); + if (oldSearchCalled < searchCalled) { + printf(" %d Calls", searchCalled-oldSearchCalled); + if (cutoffCount>oldCutoffCount) + printf(" (%d Cutoffs)", cutoffCount-oldCutoffCount); + printf(", GenMoves %d (%d/%d/%d played)", + moveCount - oldMoveCount, + normalCount - oldNormalCount, + pushCount-oldPushCount, + outCount-oldOutCount); + printf(", Rate# %d", + ratedPositions+wonPositions + - oldRatedPositions - oldWonPositions); + if (wonPositions > oldWonPositions) + printf(" (%d Won)", wonPositions- oldWonPositions); + printf("\n"); + indent(spyDepth); + } + + printf(" => Rated %d%s\n", + value, + (value>14900) ? ", WON !": + (value>=beta) ? ", CUTOFF !": + (value>actValue) ? ", Best !": ""); + } + } + else { + if (spyLevel>2) { + indent(spyDepth); + printf("%s (%6d .. %6d) %-25s => Rating %6d%s\n", + (color==color1)?"O":"X", alpha,beta, + m.name().latin1(), + value, + (value>14900) ? ", WON !": + (value>=beta) ? ", CUTOFF !": + (value>actValue) ? ", Best !": ""); + } + } + + if (value>=beta) cutoffCount++; +#endif + +#ifdef SPION + if (bUpdateSpy) { + if (value > actValue) + emit updateBest(depth, value, m, value >= beta); + emit update(depth, value, m, true); + } +#endif + if (value > actValue) { + actValue = value; + pv.update(depth, m); + + // Only update best move if not stopping search + if (!breakOut && (depth == 0)) { + _bestMove = m; + + if (bUpdateSpy) { + emit updateBestMove(m, actValue); +#ifdef MYTRACE + if (spyLevel>0) { + int i; + printf("> New pv (Rating %d):", actValue); + for(i=0;i<=maxDepth;i++) { + printf("\n> D %d: %s", + i, pv[i].name().latin1() ); + } + printf("\n>\n"); + } +#endif + } + } + + if (actValue>14900 || actValue >= beta) + return actValue; + + /* maximize alpha */ + if (actValue > alpha) alpha = actValue; + } + + if (breakOut) depthPhase=false; + m.type = Move::none; + } + + return actValue; +} + + +Move& Board::bestMove() +{ + int alpha=-15000,beta=15000; + int nalpha,nbeta, actValue; + + if (!_evalScheme) { + // Use default values if not set + setEvalScheme(); + } + + pv.clear(realMaxDepth); + _bestMove.type = Move::none; + + maxDepth=1; + show = false; + breakOut = false; + spyDepth = 0; + + if (spyLevel>0) + printf("\n> New Search\n>\n"); + + /* iterative deepening loop */ + do { + if (spyLevel>0) + printf("> MaxDepth: %d\n>\n", maxDepth); + + /* searches on same level with different alpha/beta windows */ + while(1) { + if (spyLevel>0) + printf("> AB-Window: (%d ... %d)\n>\n", alpha, beta); + + nalpha=alpha, nbeta=beta; + inPrincipalVariation = (pv[0].type != Move::none); + + /* Statistics */ + searchCalled = 0; + moveCount = 0; + ratedPositions = 0; + wonPositions = 0; + normalCount = 0; + pushCount = 0; + outCount = 0; + cutoffCount = 0; + + actValue = search(0,alpha,beta); + + if (spyLevel>0) + { + int i; + if (spyLevel>1) + printf(">\n"); + printf("> Got PV with Rating %d:",actValue); + for(i=0;i<=maxDepth;i++) { + printf("\n> D %d: ", i); + pv[i].print(); + } + printf("\n>\n"); + + printf("> Search called : %6d / %d Cutoffs\n", + searchCalled, cutoffCount); + printf("> Moves generated : %6d / %d Played\n", + moveCount, normalCount+pushCount+outCount); + printf("> Nrml/Push/Out : %6d / %d / %d\n", + normalCount,pushCount,outCount); + printf("> Positions rated : %6d / %d Won\n>\n", + ratedPositions+wonPositions, wonPositions); + + } + + if (actValue > 14900 || actValue < -14900) + breakOut=true; + + /* Don't break out if we haven't found a move */ + if (_bestMove.type == Move::none) + breakOut=false; + + if (breakOut) break; + + // widen alpha-beta window if needed + if (actValue <= nalpha) { + alpha = -15000; + if (beta<15000) beta=actValue+1; + continue; + } + if (actValue >= nbeta) { + if (alpha > -15000) alpha = actValue-1; + beta=15000; + continue; + } + break; + } + + /* Window in both directions cause of deepening */ + alpha=actValue-200, beta=actValue+200; + + if (breakOut) break; + + maxDepth++; + } + while(maxDepth <= realMaxDepth); + + /* If Spy is On, we want replayable search: don't change rating! */ + if (spyLevel==0) + changeEvaluation(); + else { + printf(">>> Got Move : "); + pv[0].print(); + printf("\n\n"); + } + + spyDepth = 0; + + return _bestMove; +} + +Move Board::randomMove() +{ + Move m; + MoveList list; + + generateMoves(list); + int l = list.getLength(); + + int j = random.getLong(l) +1; + + while(j != 0) { + list.getNext(m, Move::none); + j--; + } + + return m; +} + + +void Board::print(int ) +{ + int row,i; + char spaces[]=" "; + const char *z[]={". ","O ","X ", "o ", "x "}; + + printf("\n -----------\n"); + for(row=0;row<4;row++) { + printf("%s/ ",spaces+row); + for(i=0;i<5+row;i++) printf("%s",z[field[row*11+12+i]]); + printf("\\\n"); + } + printf(" | "); + for(i=0;i<9;i++) printf("%s",z[field[56+i]]); + printf("|\n"); + for(row=0;row<4;row++) { + printf("%s\\ ",spaces+3-row); + for(i=0;i<8-row;i++) printf("%s",z[field[68+row*12+i]]); + printf("/\n"); + } + printf(" ----------- O: %d X: %d\n", + color1Count, color2Count); +} + +QString Board::getASCIIState(int moveNo) +{ + QString state, tmp; + + int row,i; + char spaces[]=" "; + const char *z[]={". ","O ","X ", "o ", "x "}; + + state += tmp.sprintf("\n #%-3d ----------- O: %d X: %d\n", + moveNo, color1Count, color2Count); + for(row=0;row<4;row++) { + state += tmp.sprintf("%s/ ",spaces+row); + for(i=0;i<5+row;i++) + state += tmp.sprintf("%s",z[field[row*11+12+i]]); + state += "\\\n"; + } + state += " | "; + for(i=0;i<9;i++) + state += tmp.sprintf("%s",z[field[56+i]]); + state += "|\n"; + for(row=0;row<4;row++) { + state += tmp.sprintf("%s\\ ",spaces+3-row); + for(i=0;i<8-row;i++) + state += tmp.sprintf("%s",z[field[68+row*12+i]]); + state += "/\n"; + } + state += " -----------\n\n"; + + return state; +} + +int Board::setASCIIState(const QString& state) +{ + int moveNo=-1, index; + int len = state.length(); + int color1Count = 0; + int color2Count = 0; + + /* get moveNo if supplied */ + if ((index = state.find("#"))>=0) + moveNo = state.mid(index+1,3).toInt(); + + int f=12, row=0, rowEnd = 17; + char c = ' '; + + index=state.find("/"); + + while(index>=0) { + + while(++index<len) { + c = state[index].latin1(); + if (c !=' ') break; + } + + if (c=='.') field[f++] = 0; + else if (c=='o' || c=='O') { + // printf(".. F %d: O\n", f); + field[f++] = 1; + color1Count++; + } + else if (c=='x' || c=='X') { + // printf(".. F %d: X\n", f); + field[f++] = 2; + color2Count++; + } + else { + if (index >= len) index=-1; + continue; + } + + if (f == rowEnd) { + row++; + if (row <4) { + index = state.find("/",index); + f = 12 + row*11; + rowEnd = row*12+17; + } + else if (row==4) { + index = state.find("|",index); + f = 56; + rowEnd = 65; + } + else if (row <9) { + index = state.find("\\",index); + f = 8 + row*12; + rowEnd = 21 + row*11; + } + else + break; + // printf("Row %d: %d - %d, Idx %d\n", row, f, rowEnd, index); + } + } + return moveNo; +} + + +QString Board::getState(int moveNo) +{ + QString state; + QString entry, tmp; + int i; + + /* Color + Counts */ + state += (char) ('A' + moveNo /25 ); + state += (char) ('A' + moveNo %25 ); + state += (char) ('A' + color1Count); + state += (char) ('A' + color2Count); + state += (char) ('A' + 4*color + field[order[0]]); + + /* Board (field values can be 0-3; 2 fields coded in one char */ + for(i=1;i<61;i+=2) + state+= (char) ('A' + 4*field[order[i]] + field[order[i+1]] ); + + /* -> 35 chars */ + return state; +} + +int Board::setState(QString& _state) +{ + int moveNo; + const char *state = _state.ascii(); + + if (_state.length() != 35) return 0; + + moveNo = 25*(state[0] - 'A') + (state[1] - 'A'); + color1Count = state[2] - 'A'; + color2Count = state[3] - 'A'; + color = (state[4] - 'A') / 4; + field[order[0]] = (state[4] - 'A') %4; + + int i = 1; + for(int j = 5; j<35; j++) { + int w = state[j] - 'A'; + field[order[i++]] = w/4; + field[order[i++]] = w % 4; + } + return moveNo; +} + +void Board::setSpyLevel(int level) +{ + spyLevel = level; +} +#include "Board.moc" |