public Move get1stMove( Game game, CheckersSetup setup, checkersSetup.Board board, ArrayList<Move> choices) { // BasicCheckersAIFunctions.delayCompALittle(); // start fresh after every turn: viewedPos = null; // TESTING numPosRecorded = 0; // END TESTING numJumpsAlreadyDoneThisTurn = 0; BasicCheckersAIFunctions.printAllAIPlayerOptions(setup, isDarkPlayer); desiredTurn = getBestMoveBasedOnSearchOfDepthN(setup, isDarkPlayer, this.depth); ArrayList<Move> desiredMoves = desiredTurn.getMovesFor1Turn(); BasicCheckersAIFunctions.PrintDesiredMoves(desiredMoves); if (desiredMoves.get(0).isJump()) { numJumpsAlreadyDoneThisTurn++; } // TESTING (Display) System.out.println("Number of positions analysed for AI with Tree: " + numPosRecorded); if (viewedPos != null) { System.out.println("Height of tree: " + viewedPos.getHeightOfTree()); } System.out.println(); // END TESTING return desiredMoves.get(0); }
public Move getNextJumps( CheckersSetup setup, checkersSetup.Board board, ArrayList<Move> multiJumpChoices) { // BasicCheckersAIFunctions.delayCompALittle(); // get the next move in the desired multi jump choice: Move move = desiredTurn.getMovesFor1Turn().get(numJumpsAlreadyDoneThisTurn); // Testing BasicCheckersAIFunctions.doSanityCheckMakeSureCompMoveIsRealMove( desiredTurn.getMovesFor1Turn().get(numJumpsAlreadyDoneThisTurn), multiJumpChoices); // Testing numJumpsAlreadyDoneThisTurn++; return move; }
// pre: this position MUST be at the beginning of a turn. // post: returns the utility of the checkers position for whoever's turn it happens to be. public double getEstUtilityOfPosition( CheckersSetup setup, boolean isDarkPlayerCurrentTurn, int curdepth) { double ret; // this key identifies the setup of the board and who's turn it is in 3 numbers: int key[] = AnalysedPosition.makeKey(setup, isDarkPlayerCurrentTurn); // ret pos!!!! // Check to see if the position has already been analysed Double alreadyFoundUtility = getUtilityIfAlreadyFound(key, curdepth); if (alreadyFoundUtility != null) { return new Double(alreadyFoundUtility).doubleValue(); } // end check // Find the utility from scratch and recursion: if (curdepth == 0) { if (isDarkPlayerCurrentTurn) { return utilityFunc.getEstimatedUtilityForDarkPlayer(setup, isDarkPlayerCurrentTurn); } else { return -utilityFunc.getEstimatedUtilityForDarkPlayer(setup, isDarkPlayerCurrentTurn); } } else { ArrayList<checkersSetup.Turn> turnChoices = BasicCheckersAIFunctions.getAllPossiblePlayerOptionsFor1Turn( setup, isDarkPlayerCurrentTurn); if (turnChoices.size() == 0) { // horrible position: return -BasicCheckersAIFunctions.DARK_WIN_UTILITY; } setup.playTurn(turnChoices.get(0)); // op = OPPONENT! double opWorstPosUtil = getEstUtilityOfPosition(setup, !isDarkPlayerCurrentTurn, curdepth - 1); setup.reverseTurn(turnChoices.get(0)); double currOpPosUtil; for (int i = 1; i < turnChoices.size(); i++) { setup.playTurn(turnChoices.get(i)); currOpPosUtil = getEstUtilityOfPosition(setup, !isDarkPlayerCurrentTurn, curdepth - 1); if (currOpPosUtil < opWorstPosUtil) { opWorstPosUtil = currOpPosUtil; } setup.reverseTurn(turnChoices.get(i)); } ret = -opWorstPosUtil; // End finding utility from scratch // TESTING SLOW if (debug) { sanityTestKeyBeforeAndAfter(setup, isDarkPlayerCurrentTurn, key); } // END TESTING // ADD analysed position to tree: addPositionToListOfAnalysedPositions(key, isDarkPlayerCurrentTurn, curdepth, ret); return ret; } }
// pre: this position MUST be at the beginning of a turn // post: returns the best move assuming that the opponent plays perfectly. // This search only looks (depth) moves ahead and is very inefficient. public checkersSetup.Turn getBestMoveBasedOnSearchOfDepthN( CheckersSetup setup, boolean isDarkPlayer, int curdepth) { int numAlternativelyGoodMoves = 0; if (curdepth > 0) { ArrayList<checkersSetup.Turn> turnChoices = BasicCheckersAIFunctions.getAllPossiblePlayerOptionsFor1Turn(setup, isDarkPlayer); favTurn = new Turn[turnChoices.size()]; favTurn[0] = turnChoices.get(0); setup.playTurn(turnChoices.get(0)); // note: op = OPPONENT! double opWorstPosUtil = getEstUtilityOfPosition(setup, !isDarkPlayer, curdepth - 1); setup.reverseTurn(turnChoices.get(0)); double opPosUtil; for (int i = 1; i < turnChoices.size(); i++) { setup.playTurn(turnChoices.get(i)); opPosUtil = getEstUtilityOfPosition(setup, !isDarkPlayer, curdepth - 1); if (opPosUtil < opWorstPosUtil) { opWorstPosUtil = opPosUtil; favTurn[0] = turnChoices.get(i); numAlternativelyGoodMoves = 0; } else if (opPosUtil == opWorstPosUtil) { numAlternativelyGoodMoves++; favTurn[numAlternativelyGoodMoves] = turnChoices.get(i); } setup.reverseTurn(turnChoices.get(i)); } if (opWorstPosUtil <= -BasicCheckersAIFunctions.DARK_WIN_UTILITY) { System.out.println("YOU ARE F****D!!"); } // TODO // opUtilityOfLastMove = -opWorstPosUtil; } else { System.out.println("Error: the depth should be bigger than 0!"); System.exit(1); // stub to stop java from complaining: favTurn = new Turn[0]; } // if AI is NOT deterministic AND // there's several choices that seem similarly good, the AI randomly choses a move if (numAlternativelyGoodMoves > 0 && this.deterministic == false) { System.out.println( "aitree is randomly choosing beetween " + (numAlternativelyGoodMoves + 1) + " ways to do his turn"); numFavMoves = numAlternativelyGoodMoves + 1; return favTurn[(int) (Math.random() * (numAlternativelyGoodMoves + 1))]; } else { numFavMoves = 1; return favTurn[0]; } }