package player.gamer.statemachine.simple; import java.util.List; import player.gamer.statemachine.StateMachineGamer; import player.gamer.statemachine.reflex.event.ReflexMoveSelectionEvent; import player.gamer.statemachine.reflex.gui.ReflexDetailPanel; import util.statemachine.MachineState; import util.statemachine.Move; import util.statemachine.StateMachine; import util.statemachine.exceptions.GoalDefinitionException; import util.statemachine.exceptions.MoveDefinitionException; import util.statemachine.exceptions.TransitionDefinitionException; import util.statemachine.implementation.prover.cache.CachedProverStateMachine; import apps.player.detail.DetailPanel; /** * SimpleMonteCarloGamer is a simple state-machine-based Gamer. It will use a * pure Monte Carlo approach towards picking roles, doing simulations and then * choosing the move that has the highest expected score. It should be slightly * more challenging than the RandomGamer, while still playing reasonably fast. * * However, right now it isn't challenging at all. It's extremely mediocre, and * doesn't even block obvious one-move wins. This is partially due to the speed * of the default state machine (which is slow) and mostly due to the algorithm * assuming that the opponent plays completely randomly, which is inaccurate. * * @author Sam Schreiber */ public final class SimpleMonteCarloGamer extends StateMachineGamer { /** * Does nothing */ @Override public void stateMachineMetaGame(long timeout) throws TransitionDefinitionException, MoveDefinitionException, GoalDefinitionException { // Do nothing. } /** * Employs a simple "Monte Carlo" algorithm. */ @Override public Move stateMachineSelectMove(long timeout) throws TransitionDefinitionException, MoveDefinitionException, GoalDefinitionException { StateMachine theMachine = getStateMachine(); long start = System.currentTimeMillis(); long finishBy = timeout - 1000; List<Move> moves = theMachine.getLegalMoves(getCurrentState(), getRole()); Move selection = moves.get(0); if (moves.size() > 1) { int[] moveTotalPoints = new int[moves.size()]; int[] moveTotalAttempts = new int[moves.size()]; // ... for (int i = 0; true; i = (i+1) % moves.size()) { if (System.currentTimeMillis() > finishBy) break; int theScore = performDepthChargeFromMove(theMachine.getInitialState(), moves.get(i)); moveTotalPoints[i] += theScore; moveTotalAttempts[i] += 1; } // Compute the expected score for each move. double[] moveExpectedPoints = new double[moves.size()]; for (int i = 0; i < moves.size(); i++) { moveExpectedPoints[i] = (double)moveTotalPoints[i] / moveTotalAttempts[i]; System.out.println("Move [" + moves.get(i) + "] = " + moveExpectedPoints[i] + ", with " + moveTotalAttempts[i] + " attempts."); } // Find the move with the best expected score. int bestMove = 0; double bestMoveScore = moveExpectedPoints[0]; for (int i = 1; i < moves.size(); i++) { if (moveExpectedPoints[i] > bestMoveScore) { bestMoveScore = moveExpectedPoints[i]; bestMove = i; } } selection = moves.get(bestMove); } long stop = System.currentTimeMillis(); notifyObservers(new ReflexMoveSelectionEvent(moves, selection, stop - start)); return selection; } private int[] depth = new int[1]; int performDepthChargeFromMove(MachineState theState, Move myMove) { StateMachine theMachine = getStateMachine(); try { MachineState finalState = theMachine.performDepthCharge(theMachine.getRandomNextState(theState, getRole(), myMove), depth); return theMachine.getGoal(finalState, getRole()); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * Uses a CachedProverStateMachine */ @Override public StateMachine getInitialStateMachine() { return new CachedProverStateMachine(); } @Override public String getName() { return "SimpleMonteCarlo"; } @Override public DetailPanel getDetailPanel() { return new ReflexDetailPanel(); } }