/* 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 #include #include #include #include #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;iactMaxDepth) 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( TQString("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 */ 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 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;i8 && 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;imoveValue(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;i1) { 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); } TQString Board::getASCIIState(int moveNo) { TQString 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 TQString& 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) 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; } TQString Board::getState(int moveNo) { TQString state; TQString 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(TQString& _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"