Skip to content

Content of file GstKopplungNegaMax.java

package de.fhdw.gaming.ipspiel23.gst.strategies.impl;

import java.util.Collection;
import java.util.Optional;

import de.fhdw.gaming.core.domain.GameException;
import de.fhdw.gaming.core.domain.Move;
import de.fhdw.gaming.core.domain.Player;
import de.fhdw.gaming.core.domain.State;
import de.fhdw.gaming.ipspiel23.gst.domain.IKopplung;
import de.fhdw.gaming.ipspiel23.gst.strategies.domain.IGstKopplungsMiniMaxStrategy;

/**
 * An implementation of the IGstKopplungsMiniMaxStrategy interface using the
 * NegaMax algorithm with alpha beta pruning and Time based Iterative Deepening.
 *
 * @param <P> The type of player in the game.
 * @param <S> The type of state in the game.
 * @author borkowitz
 */
public class GstKopplungNegaMax<P extends Player<P>, S extends State<P, S>>
        implements IGstKopplungsMiniMaxStrategy<P, S> {

    /**
     * Calculates the best move for the current player of a Game in a given Game state using the NegaMax algorithm.
     * Too complex, but not easily simplifyable. Warning suppressed.
     *
     * @param kopplung               The game object representing the specific game being played.
     * @param state                  The current state of the game.
     * @param maximumComputationTime The maximum computation time allowed for calculating the move.
     * @return An optional containing the calculated best move, or empty if no move is available.
     */
    @SuppressWarnings({ "checkstyle:CyclomaticComplexity", "PMD.CyclomaticComplexity", "PMD.CognitiveComplexity" })
    @Override
    public Optional<Move<P, S>> calculateBestMove(final IKopplung<P, S> kopplung, final S state, // NOPMD by Dave on
                                                                                                 // 26.06.23, 22:57
            final int maximumComputationTime) {
        Optional<Move<P, S>> bestMove = Optional.empty();
        try {

            final Long startTime = System.currentTimeMillis();
            final Optional<Collection<Move<P, S>>> possiblesMovesOptional = kopplung.getPossibleMoves(state);

            if (possiblesMovesOptional.isPresent() && possiblesMovesOptional.get().size() > 0) {

                for (int i = 0;; i++) {
                    Integer bestMoveScore = Integer.MIN_VALUE;
                

                    Optional<Move<P, S>> iterationBestMove = Optional.empty();
                    Integer iterationBestMoveScore = Integer.MIN_VALUE;

                    if ((System.currentTimeMillis() - startTime) > (maximumComputationTime / 2)) {
                        break;
                    }

                    for (final Move<P, S> move : possiblesMovesOptional.get()) {

                        final S copiedState = state.deepCopy();
                        final P currentPlayer = kopplung.getCurrentPlayer(copiedState).get();
                        move.applyTo(copiedState, currentPlayer);
                        copiedState.nextTurn();

                        final Integer moveScore = -this.negamax(kopplung, copiedState, i, Integer.MIN_VALUE,
                                Integer.MAX_VALUE);

//                        System.out.println(copiedState);
//                        System.out.println(move);
//                        System.out.println(moveScore);
//                        System.out.println("");

                        // final Integer moveScore = simulateEvaluateMove(kopplung, state, i, move);

                        if (moveScore > iterationBestMoveScore) {

                            iterationBestMove = Optional.of(move);
                            iterationBestMoveScore = moveScore;
                        }
                    }
//                    System.out.println("");
//                    System.out.println("");
//                    System.out.println("________________");
                    if (iterationBestMoveScore > bestMoveScore) {
                        bestMove = iterationBestMove;
                        bestMoveScore = iterationBestMoveScore;
The value assigned to variable 'bestMoveScore' is never used.
Reports assignments to variables that are never used before the variable is overwritten, or goes out of scope. Unused assignments are those for which 1. The variable is never read after the assignment, or 2. The assigned value is always overwritten by other assignments before the next read of the variable. The rule doesn't consider assignments to fields except for those of `this` in a constructor, or static fields of the current class in static initializers. The rule may be suppressed with the standard `@SuppressWarnings("unused")` tag. The rule subsumes {% rule "UnusedLocalVariable" %}, and {% rule "UnusedFormalParameter" %}. Those violations are filtered out by default, in case you already have enabled those rules, but may be enabled with the property `reportUnusedVariables`. Variables whose name starts with `ignored` or `unused` are filtered out, as is standard practice for exceptions. Limitations: * The rule currently cannot know which method calls throw exceptions, or which exceptions they throw. In the body of a try block, every method or constructor call is assumed to throw. This may cause false-negatives. The only other language construct that is assumed to throw is the `throw` statement, in particular, things like `assert` statements, or NullPointerExceptions on dereference are ignored. * The rule cannot resolve assignments across constructors, when they're called with the special `this(...)` syntax. This may cause false-negatives. Both of those limitations may be partly relaxed in PMD 7.
    
        
            
            class A {
                // this field initializer is redundant,
                // it is always overwritten in the constructor
                int f = 1;

                A(int f) {
                    this.f = f;
                }
            }
        
        
    
See PMD documentation.
} } } return bestMove; } catch (final Exception e) { e.printStackTrace(); return bestMove; } } /** * Extracted. * * @param kopplung * @param state * @param move * @return * @throws GameException */ private S simulateMove(final IKopplung<P, S> kopplung, final S state, final Move<P, S> move) throws GameException { final S copiedState = state.deepCopy(); final P currentPlayer = kopplung.getCurrentPlayer(copiedState).get(); move.applyTo(copiedState, currentPlayer); copiedState.nextTurn(); return copiedState; } /** * Performs the NegaMax algorithm to evaluate the score of a Game state. * Too complex, but not easily simplifyable. Warning suppressed. * * @param kopplung The game object representing the specific game being played. * @param state The current state of the game. * @param depth The current depth of the algorithm. * @param alpha The alpha value for alpha-beta pruning. * @param beta The beta value for alpha-beta pruning. * @return The evaluated score of the state. * @throws Exception */ @SuppressWarnings({ "checkstyle:CyclomaticComplexity", "PMD.CyclomaticComplexity", "PMD.CognitiveComplexity" }) private Integer negamax(final IKopplung<P, S> kopplung, final S state, final Integer depth, final Integer alpha, final Integer beta) throws GameException { final Optional<Collection<Move<P, S>>> possiblesMovesOptional = kopplung.getPossibleMoves(state); if (depth == 0 || kopplung.getIsGameOver(state).get()) { return kopplung.evalState(state).get(); } if (possiblesMovesOptional.isPresent() && possiblesMovesOptional.get().size() > 0) { Integer score = Integer.MIN_VALUE; Integer modAlpha = alpha; for (final Move<P, S> move : possiblesMovesOptional.get()) { final S copiedState = this.simulateMove(kopplung, state, move); final Integer childScore = -this.negamax(kopplung, copiedState, depth - 1, -beta, -modAlpha); if (childScore > score) { score = childScore - depth; } modAlpha = Math.max(modAlpha, score); if (modAlpha >= beta) { break; } } return score; } throw new GameException("Something went wrong with Negamax"); } }