package net.demilich.metastone.game.behaviour; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.demilich.metastone.game.GameContext; import net.demilich.metastone.game.Player; import net.demilich.metastone.game.actions.ActionType; import net.demilich.metastone.game.actions.GameAction; import net.demilich.metastone.game.behaviour.heuristic.IGameStateHeuristic; import net.demilich.metastone.game.cards.Card; public class GreedyOptimizeTurn extends Behaviour { private final Logger logger = LoggerFactory.getLogger(GreedyOptimizeTurn.class); private final IGameStateHeuristic heuristic; private int assignedGC; private final HashMap<ActionType, Integer> evaluatedActions = new HashMap<ActionType, Integer>(); private final TranspositionTable table = new TranspositionTable(); public GreedyOptimizeTurn(IGameStateHeuristic heuristic) { this.heuristic = heuristic; } private double alphaBeta(GameContext context, int playerId, GameAction action, int depth) { GameContext simulation = context.clone(); simulation.getLogic().performGameAction(playerId, action); if (!evaluatedActions.containsKey(action.getActionType())) { evaluatedActions.put(action.getActionType(), 0); } evaluatedActions.put(action.getActionType(), evaluatedActions.get(action.getActionType()) + 1); if (depth == 0 || simulation.getActivePlayerId() != playerId || simulation.gameDecided()) { return heuristic.getScore(simulation, playerId); } List<GameAction> validActions = simulation.getValidActions(); double score = Float.NEGATIVE_INFINITY; if (table.known(simulation)) { return table.getScore(simulation); // logger.info("GameState is known, has score of {}", score); } else { for (GameAction gameAction : validActions) { score = Math.max(score, alphaBeta(simulation, playerId, gameAction, depth - 1)); if (score >= 10000) { break; } } table.save(simulation, score); } return score; } @Override public IBehaviour clone() { try { return new GreedyOptimizeTurn(heuristic.getClass().newInstance()); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } @Override public String getName() { return "Min-Max Turn"; } @Override public List<Card> mulligan(GameContext context, Player player, List<Card> cards) { List<Card> discardedCards = new ArrayList<Card>(); for (Card card : cards) { if (card.getBaseManaCost() >= 4) { discardedCards.add(card); } } return discardedCards; } @Override public GameAction requestAction(GameContext context, Player player, List<GameAction> validActions) { if (validActions.size() == 1) { heuristic.onActionSelected(context, player.getId()); return validActions.get(0); } // for now, do now evaluate battecry actions if (validActions.get(0).getActionType() == ActionType.BATTLECRY) { return validActions.get(context.getLogic().random(validActions.size())); } if (assignedGC != 0 && assignedGC != context.hashCode()) { logger.warn("AI behaviour was used in another context!"); } assignedGC = context.hashCode(); evaluatedActions.clear(); table.clear(); GameAction bestAction = validActions.get(0); double bestScore = Double.NEGATIVE_INFINITY; for (GameAction gameAction : validActions) { logger.debug("********************* SIMULATION STARTS *********************"); double score = alphaBeta(context, player.getId(), gameAction, 3); if (score > bestScore) { bestAction = gameAction; bestScore = score; } logger.debug("********************* SIMULATION ENDS, Action {} achieves score {}", gameAction, score); } int totalActionCount = 0; for (ActionType actionType : evaluatedActions.keySet()) { int count = evaluatedActions.get(actionType); logger.debug("{} actions of type {} have been evaluated this turn", count, actionType); totalActionCount += count; } logger.debug("{} actions in total have been evaluated this turn", totalActionCount); logger.debug("Selecting best action {} with score {}", bestAction, bestScore); heuristic.onActionSelected(context, player.getId()); return bestAction; } /*private double simulateAction(GameContext context, int playerId, GameAction action) { GameContext simulation = context.clone(); simulation.getLogic().performGameAction(playerId, action); if (!evaluatedActions.containsKey(action.getActionType())) { evaluatedActions.put(action.getActionType(), 0); } evaluatedActions.put(action.getActionType(), evaluatedActions.get(action.getActionType()) + 1); if (simulation.getActivePlayerId() != playerId || simulation.gameDecided()) { return heuristic.getScore(simulation, playerId); } List<GameAction> validActions = simulation.getValidActions(); if (validActions.size() == 0) { throw new RuntimeException("No more possible moves, last action was: " + action); } double bestScore = Integer.MIN_VALUE; for (GameAction gameAction : validActions) { bestScore = Math.max(bestScore, simulateAction(simulation, playerId, gameAction)); } return bestScore; }*/ }