vector<Card*> TestControllerv2::ProposeAttacks(Player* attackPlayer, Player* blockPlayer, double &value, int recur, bool needVal) { // TODO: Consider the number of states that must be calculated and if too large consider no attacks, all attacks and then // remove creatures that die, and repeat. vector<Card*> attacking; vector<Card*> potAttacks; potAttacks = GetPotentialAttackers(attackPlayer); // for(int i=0; i<potAttacks.size(); i++) { // if(potAttacks[i]->CanAttack()) { // attacking.push_back(potAttacks[i]); // } // } if(potAttacks.size() == 0 && !needVal) { return attacking; } vector<bool> isAttacking; for(int i=0; i< potAttacks.size(); i++) { isAttacking.push_back(false); } vector<bool> bestAttacks; double* attackBoard = new double[CardList::size]; double* blockBoard = new double[CardList::size]; GetBoardVec(attackPlayer,attackBoard); GetBoardVec(blockPlayer,blockBoard); double bestVal = -0.1; GenerateAttacks(isAttacking,0,potAttacks.size(),bestVal, &bestAttacks, attackBoard, blockBoard, potAttacks, attackPlayer, blockPlayer, recur); // cout << "bestVal is " << bestVal << endl; for(int i=0; i<bestAttacks.size(); i++) { if(bestAttacks[i]) { attacking.push_back(potAttacks[i]); } } value = bestVal; delete attackBoard; delete blockBoard; return attacking; }
double TestControllerv2::GenerateAttacks(vector<bool> isAttacking, int beg, int end, double& bestVal, vector<bool>* bestAttacks, double* attackState, double* blockState, vector<Card*> potAttacks, Player* attackPlayer, Player* blockPlayer, int recur) { if(beg!=end) { for(int i=0; i<2; i++) { if(i==1) { isAttacking[beg] = true; } double val = 0; val = GenerateAttacks(isAttacking,beg+1,end,bestVal,bestAttacks,attackState,blockState,potAttacks,attackPlayer,blockPlayer,recur); // cout << "val = " << val << endl; if(val>=bestVal) { // cout << val << " > " << bestVal << endl; bestVal = val; *bestAttacks = isAttacking; } else if(val==bestVal) { // Break ties by having the most creatures attack int newAtt=0; int oldAtt=0; for(int k=0; k<isAttacking.size(); k++) { if(isAttacking[k]) { newAtt++; } if(bestAttacks[0][k]) { oldAtt++; } } if(newAtt>oldAtt) { bestVal = val; *bestAttacks = isAttacking; } } } return 0.; } else { // if(recur==1) { // cout << "isAttacking: "; // for(int i=0; i<isAttacking.size(); i++) { // cout << isAttacking[i] << " "; // } // cout << endl; // } // else { // cout << "propAttack: "; // for(int i=0; i<isAttacking.size(); i++) { // cout << isAttacking[i] << " "; // } // cout << endl; // } double value; vector<Card*> attackers; for(int i=0; i<isAttacking.size(); i++) { if(isAttacking[i]) { attackers.push_back(potAttacks[i]); } } ProposeBlocks(attackers,blockPlayer,attackPlayer,value,recur,true); value = 1-value; if(bestVal==-0.1) { // cout << val << " > " << bestVal << endl; bestVal = value; *bestAttacks = isAttacking; } // if(recur==1) { // cout << "Attack Value = " << value << endl; // } // else { // cout << "Prop attack Value = " << value << endl; // } // if(attackers.size()>0) { // ProposeBlocks(attackers,blockPlayer,attackPlayer,value,recur,true); // if(value!=-1) { //// Convert from blocker's value to attackers value // value = 1-value; // } // else { //// If no possible blockers // double* blockBoardTemp = new double[totalCards]; // double* attackBoardTemp = new double[totalCards]; // memcpy(blockBoardTemp,blockState,totalCards*sizeof(double)); // memcpy(attackBoardTemp,attackState,totalCards*sizeof(double)); // int damage=0; // for(int j=0; j<attackers.size(); j++) { // damage += attackers[j]->GetPower(); // } // if(recur>0) { // PredictCrackback(blockPlayer,attackPlayer,blockBoardTemp,attackBoardTemp,attackers,damage,value); // value = 1-value; // } // else { // value = AssessGameState(attackBoardTemp,blockBoardTemp,attackPlayer->lifeTotal,blockPlayer->lifeTotal-damage); // } // delete blockBoardTemp; // delete attackBoardTemp; // } // } // else { //// There were no attacks // double* blockBoardTemp = new double[totalCards]; // double* attackBoardTemp = new double[totalCards]; // memcpy(blockBoardTemp,blockState,totalCards*sizeof(double)); // memcpy(attackBoardTemp,attackState,totalCards*sizeof(double)); // if(recur>0) { // vector<Card*> emptyAttack; // PredictCrackback(blockPlayer,attackPlayer,blockBoardTemp,attackBoardTemp,emptyAttack,0,value); // value = 1-value; // } // else { // value = AssessGameState(attackBoardTemp,blockBoardTemp,attackPlayer->lifeTotal,blockPlayer->lifeTotal); // } // delete blockBoardTemp; // delete attackBoardTemp; // } return value; } }
/* Evaluate the current board position for the side stored in the board * structure. Score for white. (Score for black = -1* this.) We also pass the values * of alpha and beta to this routine on the expectation that we might be able to get a * cutoff if the score is particularly high or low without having to do the time consuming * part of the eval(). */ int EvalMain(Board *B, int alpha, int beta) { int sq,p,npw=0,tpts=B->WPts+B->BPts,sq2,def, score=0; int *SpDef, *SpWon, lazyscore; BITBOARD pieces = B->All; BOOL one_sided; #ifdef DEBUG_EVAL int control = 0, oldscore = 0, contestcount = 0; #endif /* Check for a drawn position */ if (IsDrawnMaterial(B)) return ((Current_Board.side == WHITE) ? (DRAW_SCORE) : -(DRAW_SCORE)); /* Check if the game is theoretically won/lost */ score = IsWonGame(B); #ifdef DEBUG_EVAL if (score != 0) fprintf(stdout,"\nPosition Theoretically Won : %d\n\n",score); #endif /* Check to see if we can just cutoff here - this score is so good that * we needn't bother working it out exactly - it's going to cause a cutoff. * We have to be very careful because the score difference gained by doing * a proper eval here might be huge, therefore we only cutoff if this * position is theoretically won, and beta isn't, or it is theoretically * lost and alpha isn't. We can't just use standard futility cutoffs like * we do below, because in theoretically won positions, the score returned * by LazyEval() will almost always be much larger than EVAL_FUTILITY. */ if (USE_EVAL_SC && score!=0 && ((score > 0 && beta<T_WIN_BOUND) || (score < 0 && alpha>-(T_WIN_BOUND)))) { EvalCuts++; #ifdef DEBUG_EVAL fprintf(stdout,"Early Cut [1] %d (A=%d,B=%d)\n",score,alpha,beta); #endif return score; } /* Get a lazy score evaluation * (material score & simple positional terms plus passed pawns) */ lazyscore = LazyEval(B) + score; /* Check to see if we can just cutoff here. The expectation is that the LazyEval * is alway within EVAL_FUTILITY of the true score. Of course this isn't always * true, but we hope that it is true most of the time. */ if (USE_EVAL_SC && (lazyscore > (beta+EVAL_FUTILITY) || lazyscore < (alpha-EVAL_FUTILITY))) { EvalCuts++; #ifdef DEBUG_EVAL fprintf(stdout,"Early Cut [2] %d (A=%d,B=%d)\n",lazyscore,alpha,beta); #endif return lazyscore; } /* We didn't get a cutoff so we have to be more careful and evaluate the board properly. * Begin with the LazyEval() score we obtained BEFORE any possible truncation due to lack * of mating material (we don't want to do this twice!). */ score += prematscore; #ifdef DEBUG_EVAL fprintf(stdout,"\nFull Eval\n---------\n"); #endif /* Generate arrays storing how many times each side attacks each square on the board. * This takes quite some time, but it is worth the effort! */ GenerateAttacks(B); /* Now loop through all the pieces on the board and sum their contributions. * Also include the contributions of the empty spaces, measuring board * domination and territorial control. * There is the specialised version for the endgame below. */ if (B->Gamestage < Endgame) { /* Work out how much square possession is worth */ SpWon = SpaceWon[B->Gamestage]; SpDef = SpaceDefended[B->Gamestage]; /* Loop through the board */ for (sq=0;sq<64;sq++) { p = B->pieces[sq]; #ifdef DEBUG_EVAL fprintf(stdout,"Square %c%d [",File(sq)+97,8-Rank(sq)); PrintPiece(p); fprintf(stdout,"] : "); #endif switch(p) { /* White Piece */ case (wpawn) : score += TactPawn(sq,B,WHITE); npw += SquareColour[sq]; break; case (wrook) : score += TactRook(sq,B,WHITE); break; case (wknight): score += TactKnight(sq,B,WHITE); break; case (wbishop): score += TactBishop(sq,B,WHITE); break; case (wqueen) : score += TactQueen(sq,B,WHITE); break; case (wking) : score += TactKing(sq,B,WHITE); break; /* Black Piece */ case (bpawn) : score -= TactPawn(sq,B,BLACK); npw += SquareColour[sq]; break; case (brook) : score -= TactRook(sq,B,BLACK); break; case (bknight): score -= TactKnight(sq,B,BLACK); break; case (bbishop): score -= TactBishop(sq,B,BLACK); break; case (bqueen) : score -= TactQueen(sq,B,BLACK); break; case (bking) : score -= TactKing(sq,B,BLACK); break; /* Empty square - reward for possession by one side */ case (empty) : sq2=ConvertSq[sq]; #ifdef DEBUG_EVAL oldscore = score; #endif /* If only one side is attacking a square, then it is won * by that side. Otherwise it can only be defended. */ one_sided = (!(B->WAttacks[sq2]) || !(B->BAttacks[sq2])); def = B->WAttacks[sq2] - B->BAttacks[sq2]; if (one_sided) { if (def>0) score += SpWon[sq]; else if (def<0) score -= SpWon[Flip[sq]]; } else { /* We have no clear winner, so evaluate the ownership of the square (slow!!) */ def = EvaluateOwnership(B,sq); if (def > 0) score += SpDef[sq]; else if (def < 0) score -= SpDef[Flip[sq]]; #ifdef DEBUG_EVAL contestcount++; #endif } #ifdef DEBUG_EVAL control += score-oldscore; fprintf(stdout,"%d", score-oldscore); #endif break; } #ifdef DEBUG_EVAL fprintf(stdout,"\n"); #endif } } /* If we're in the endgame then use this simpler version */ else { /* Loop through the pieces */ while (pieces) { sq = FirstPiece(pieces); p = B->pieces[sq]; #ifdef DEBUG_EVAL fprintf(stdout,"Square %c%d [",File(sq)+97,8-Rank(sq)); PrintPiece(p); fprintf(stdout,"] : "); #endif switch(p) { /* White Piece */ case (wpawn) : score += TactPawn(sq,B,WHITE); break; case (wrook) : score += TactRook(sq,B,WHITE); break; case (wknight): score += TactKnight(sq,B,WHITE); break; case (wbishop): score += TactBishop(sq,B,WHITE); break; case (wqueen) : score += TactQueen(sq,B,WHITE); break; case (wking) : score += TactKing(sq,B,WHITE); break; /* Black Piece */ case (bpawn) : score -= TactPawn(sq,B,BLACK); break; case (brook) : score -= TactRook(sq,B,BLACK); break; case (bknight): score -= TactKnight(sq,B,BLACK); break; case (bbishop): score -= TactBishop(sq,B,BLACK); break; case (bqueen) : score -= TactQueen(sq,B,BLACK); break; case (bking) : score -= TactKing(sq,B,BLACK); break; } RemoveFirst(pieces); #ifdef DEBUG_EVAL fprintf(stdout,"\n"); #endif } } #ifdef DEBUG_EVAL fprintf(stdout,"After Piece Tactics : %d\n",score); fprintf(stdout,"Control Balance = %d\n",control); fprintf(stdout,"Contested Empty Squares : %d\n\n",contestcount); #endif /* Add on general positional score */ if (B->Gamestage <= Middle) score += TacticsPositional(B); /* -- General score modifiers -- */ /* Modifiers for pawns blocking bishops. Your pawns should be on different squares to your * own bishops, but the same as your opponent's */ /* White Bishops */ if (B->WhiteBishops) { /* Only black square bishops */ if (!(B->WhiteBishops & WhiteMask)) score += npw*PAWN_BLOCK; /* Only white square bishops */ else if (!(B->WhiteBishops & BlackMask)) score -= npw*PAWN_BLOCK; /* Bonus for having two bishops (or more) on opposite colours */ else score += TWO_BISHOPS; #ifdef DEBUG_EVAL if (npw!=0) { if (!(B->WhiteBishops & WhiteMask)) fprintf(stdout,"White has only black square bishops : Pawn Block = %d\n",-npw*PAWN_BLOCK); else if (!(B->WhiteBishops & BlackMask)) fprintf(stdout,"White has only white square bishops : Pawn Block = %d\n",npw*PAWN_BLOCK); } if ((B->WhiteBishops & WhiteMask) && (B->WhiteBishops & BlackMask)) fprintf(stdout,"White has a bishop pair [+%d]\n",TWO_BISHOPS); #endif } /* Black Bishops */ if (B->BlackBishops) { /* Only black square bishops */ if (!(B->BlackBishops & WhiteMask)) score -= npw*PAWN_BLOCK; /* Only white square bishops */ else if (!(B->BlackBishops & BlackMask)) score += npw*PAWN_BLOCK; /* Penalty for opponent having two bishops (or more) on opposite colours */ else score -= TWO_BISHOPS; #ifdef DEBUG_EVAL if (npw!=0) { if (!(B->BlackBishops & WhiteMask)) fprintf(stdout,"Black has only black square bishops : Pawn Block = %d\n",npw*PAWN_BLOCK); else if (!(B->BlackBishops & BlackMask)) fprintf(stdout,"Black has only white square bishops : Pawn Block = %d\n",-npw*PAWN_BLOCK); } if ((B->BlackBishops & WhiteMask) && (B->BlackBishops & BlackMask)) fprintf(stdout,"Black has a bishop pair [-%d]\n",TWO_BISHOPS); #endif } /* Penalty for having no pawns */ if (!B->WhitePawns) score -= NO_PAWNS; if (!B->BlackPawns) score += NO_PAWNS; /* If there is no mating material for one side then the score cannot favour that side * (Note - we calculated this in the LazyEval and retained the results) */ if (!wmat) score = min(0,score); if (!bmat) score = max(0,score); #ifdef DEBUG_EVAL if (!wmat) fprintf(stdout,"No mating material for white\n"); if (!bmat) fprintf(stdout,"No mating material for black\n"); #endif #ifdef DRAWISH_ENDINGS /* Test for a 'drawish' ending, and reduce the score if so */ if (Drawish(B)) score = (score * (50+tpts)) / 100; #ifdef DEBUG_EVAL if (Drawish(B)) fprintf(stdout,"Drawish Position (score reduced)\n"); #endif #endif #ifdef DEBUG_EVAL fprintf(stdout,"Final Score : %d [Delta %d]\n",score,score-lazyscore); #endif // Return score, possibly altered by the skill level if (Skill == 10) return score; return score + Random(((10-Skill) * 4) + 1) + 2 * Skill - 20; }