Skip to content

Method: simulateMove(IKopplung, State, Move)

1: package de.fhdw.gaming.ipspiel23.gst.strategies.impl;
2:
3: import java.util.Collection;
4: import java.util.Optional;
5:
6: import de.fhdw.gaming.core.domain.GameException;
7: import de.fhdw.gaming.core.domain.Move;
8: import de.fhdw.gaming.core.domain.Player;
9: import de.fhdw.gaming.core.domain.State;
10: import de.fhdw.gaming.ipspiel23.gst.domain.IKopplung;
11: import de.fhdw.gaming.ipspiel23.gst.strategies.domain.IGstKopplungsMiniMaxStrategy;
12:
13: /**
14: * An implementation of the IGstKopplungsMiniMaxStrategy interface using the
15: * NegaMax algorithm with alpha beta pruning and Time based Iterative Deepening.
16: *
17: * @param <P> The type of player in the game.
18: * @param <S> The type of state in the game.
19: * @author borkowitz
20: */
21: public class GstKopplungNegaMax<P extends Player<P>, S extends State<P, S>>
22: implements IGstKopplungsMiniMaxStrategy<P, S> {
23:
24: /**
25: * Calculates the best move for the current player of a Game in a given Game state using the NegaMax algorithm.
26: * Too complex, but not easily simplifyable. Warning suppressed.
27: *
28: * @param kopplung The game object representing the specific game being played.
29: * @param state The current state of the game.
30: * @param maximumComputationTime The maximum computation time allowed for calculating the move.
31: * @return An optional containing the calculated best move, or empty if no move is available.
32: */
33: @SuppressWarnings({ "checkstyle:CyclomaticComplexity", "PMD.CyclomaticComplexity", "PMD.CognitiveComplexity" })
34: @Override
35: public Optional<Move<P, S>> calculateBestMove(final IKopplung<P, S> kopplung, final S state, // NOPMD by Dave on
36: // 26.06.23, 22:57
37: final int maximumComputationTime) {
38: Optional<Move<P, S>> bestMove = Optional.empty();
39: try {
40:
41: final Long startTime = System.currentTimeMillis();
42: final Optional<Collection<Move<P, S>>> possiblesMovesOptional = kopplung.getPossibleMoves(state);
43:
44: if (possiblesMovesOptional.isPresent() && possiblesMovesOptional.get().size() > 0) {
45:
46: for (int i = 0;; i++) {
47: Integer bestMoveScore = Integer.MIN_VALUE;
48:
49:
50: Optional<Move<P, S>> iterationBestMove = Optional.empty();
51: Integer iterationBestMoveScore = Integer.MIN_VALUE;
52:
53: if ((System.currentTimeMillis() - startTime) > (maximumComputationTime / 2)) {
54: break;
55: }
56:
57: for (final Move<P, S> move : possiblesMovesOptional.get()) {
58:
59: final S copiedState = state.deepCopy();
60: final P currentPlayer = kopplung.getCurrentPlayer(copiedState).get();
61: move.applyTo(copiedState, currentPlayer);
62: copiedState.nextTurn();
63:
64: final Integer moveScore = -this.negamax(kopplung, copiedState, i, Integer.MIN_VALUE,
65: Integer.MAX_VALUE);
66:
67: // System.out.println(copiedState);
68: // System.out.println(move);
69: // System.out.println(moveScore);
70: // System.out.println("");
71:
72: // final Integer moveScore = simulateEvaluateMove(kopplung, state, i, move);
73:
74: if (moveScore > iterationBestMoveScore) {
75:
76: iterationBestMove = Optional.of(move);
77: iterationBestMoveScore = moveScore;
78: }
79: }
80: // System.out.println("");
81: // System.out.println("");
82: // System.out.println("________________");
83: if (iterationBestMoveScore > bestMoveScore) {
84: bestMove = iterationBestMove;
85: bestMoveScore = iterationBestMoveScore;
86: }
87: }
88: }
89: return bestMove;
90: } catch (final Exception e) {
91: e.printStackTrace();
92: return bestMove;
93: }
94:
95: }
96:
97: /**
98: * Extracted.
99: *
100: * @param kopplung
101: * @param state
102: * @param move
103: * @return
104: * @throws GameException
105: */
106: private S simulateMove(final IKopplung<P, S> kopplung, final S state, final Move<P, S> move) throws GameException {
107: final S copiedState = state.deepCopy();
108: final P currentPlayer = kopplung.getCurrentPlayer(copiedState).get();
109: move.applyTo(copiedState, currentPlayer);
110: copiedState.nextTurn();
111: return copiedState;
112: }
113:
114: /**
115: * Performs the NegaMax algorithm to evaluate the score of a Game state.
116: * Too complex, but not easily simplifyable. Warning suppressed.
117: *
118: * @param kopplung The game object representing the specific game being played.
119: * @param state The current state of the game.
120: * @param depth The current depth of the algorithm.
121: * @param alpha The alpha value for alpha-beta pruning.
122: * @param beta The beta value for alpha-beta pruning.
123: * @return The evaluated score of the state.
124: * @throws Exception
125: */
126: @SuppressWarnings({ "checkstyle:CyclomaticComplexity", "PMD.CyclomaticComplexity", "PMD.CognitiveComplexity" })
127:
128: private Integer negamax(final IKopplung<P, S> kopplung, final S state, final Integer depth, final Integer alpha,
129: final Integer beta) throws GameException {
130:
131: final Optional<Collection<Move<P, S>>> possiblesMovesOptional = kopplung.getPossibleMoves(state);
132:
133: if (depth == 0 || kopplung.getIsGameOver(state).get()) {
134:
135: return kopplung.evalState(state).get();
136: }
137:
138: if (possiblesMovesOptional.isPresent() && possiblesMovesOptional.get().size() > 0) {
139:
140: Integer score = Integer.MIN_VALUE;
141: Integer modAlpha = alpha;
142: for (final Move<P, S> move : possiblesMovesOptional.get()) {
143:
144: final S copiedState = this.simulateMove(kopplung, state, move);
145:
146: final Integer childScore = -this.negamax(kopplung, copiedState, depth - 1, -beta, -modAlpha);
147: if (childScore > score) {
148:
149: score = childScore - depth;
150: }
151: modAlpha = Math.max(modAlpha, score);
152: if (modAlpha >= beta) {
153:
154: break;
155: }
156:
157: }
158:
159: return score;
160: }
161:
162: throw new GameException("Something went wrong with Negamax");
163:
164: }
165:
166: }