如何在connect4

时间:2019-11-29 18:16:45

标签: minimax

我为connect4游戏制作了一个minmax算法,该算法非常不稳定。 在第一步中,它可以轻松地与用户抗争并预测用户何时可以获胜。 经过一些交流之后,它开始跌跌撞撞,在那里它无法直接赢取价值,也无法做出有助于玩家获胜的举动,我拍了张照片以显示AI wrong move

的样子

我看到了很多有关minmax算法的文档,并且不知道该怎么做。感谢您的帮助! :)

西蒙(Simon)

     * Find the best move to do
     * @param game game table
     * @param depth depth to be explored by the algorithm
     * @param turnsLeft number of turns before a drow
     * @param maximizingPlayer true if it's AI's turn
     * @return the column of the best move and its value
     */
    private static minimaxResult miniMax(Puissance4Struct game, int depth, int turnsLeft, boolean maximizingPlayer) {
        minimaxResult minimaxResult = new minimaxResult();

        if (game.verifWinner()) {
            //game.afficherPuissance4();
            if (game.IATurn()) {
                //System.out.println("Je vois une victoire pour l'ordinateur");
                minimaxResult.setValue(100000);
            } else {
                //System.out.println("Je vois une victoire pour le joueur");
                minimaxResult.setValue(-100000);
            }
            return minimaxResult;

        }

        if (depth == 0 || turnsLeft == 0) {

            minimaxResult.setValue(game.tableEvaluation());
            return minimaxResult;
        }


        if (maximizingPlayer) {

            int maxValue = -10000;
            int maxColon = 0;
            minimaxResult.setValue(maxValue);

            for (int col = 0; col < game.getWidthTable(); col++) {
                if (game.findIndex(col)) {
                    game.setToken(Puissance4Struct.getAIToken());
                    game.putToken();

                    minimaxResult = miniMax(game, depth-1, turnsLeft-1, false);

                    game.eraseTokenColon(col);

                    if (maxValue < minimaxResult.getValue()) {
                        maxValue = minimaxResult.getValue();
                        maxColon = col;
                    }
                }
            }
            minimaxResult.setColonne(maxColon);
            minimaxResult.setValue(maxValue);

        } else {
            int minValue = 100000;
            int minColonne = 0;
            minimaxResult.setValue(minValue);

            for (int col = 0; col < game.getWidthTable(); col++) {

                if (game.findIndex(col)) {
                    game.setToken(Puissance4Struct.getPlayerToken());
                    game.putToken();

                    minimaxResult = miniMax(game, depth-1, turnsLeft-1, true);

                    game.eraseTokenColon(col);

                    if (minValue > minimaxResult.getValue()) {
                        minValue = minimaxResult.getValue();
                        minColonne = col;
                    }
                }
            }
            minimaxResult.setColonne(minColonne);
            minimaxResult.setValue(minValue);
            game.eraseToken();
        }
        game.changeToken();
        return minimaxResult;
    }

如有需要,这是游戏类:

      public static void main(String[] args) {
        Puissance4Struct game = new Puissance4Struct();
        minimaxResult minimaxResult;
        int input;
        int numberMovesPlayed = 0;
        int maximumMoves =  game.getNombreCases();
        boolean winner = false;
        boolean AITurn = false;

       System.out.println("Do you want to play against an AI ? \n1: yes | 2: no");
        input = (int) Verif.Input(1, 2);

        if (input == 1) { // JCAI
            // Initialise the static token reference for player and AI
            Puissance4Struct.setJetonJoueur(game.getToken());
            game.changeToken();
            Puissance4Struct.setJetonAI(game.getToken());


            while (!winner && numberMovesPlayed < maximumMoves) {
                if (AITurn) {
                    game.setToken(Puissance4Struct.getAIToken());

                    minimaxResult = miniMax(game, 4, maximumMoves - numberMovesPlayed,  true);

                    game.setToken(Puissance4Struct.getAIToken());
                    game.findIndex(minimaxResult.getColonne());
                    game.putToken();
                    ++numberMovesPlayed;

                    if (numberMovesPlayed >= 7) {
                        winner = game.verifWinner();
                    }
                    AITurn = false;

                } else {
                    game.setToken(Puissance4Struct.getPlayerToken());
                    player(game);

                    game.putToken();
                    numberMovesPlayed++;

                    if (numberMovesPlayed >= 7) {
                        winner = game.verifWinner();
                    }
                    AITurn = true;
                }
            }
            game.printConnect4();

        } else { // JCJ

            while (!winner && numberMovesPlayed < maximumMoves) {
                player(game);

                numberMovesPlayed++;
                game.putToken();

                if (numberMovesPlayed >= 7) {
                    winner = game.verifWinner();
                }

                System.out.println();
                game.printConnect4();

            }

        }
        if (winner) {
            System.out.println("\nBravo au joueur " + game.getToken() + " pour avoir gagné en " + numberMovesPlayed + " tours !");
        } else {
            System.out.println("c'est un match nul. Quelle intensité !");
        }

    }

和游戏板类:

package puissance4;


class Puissance4Struct {
    private char [][] tableau = new char[7][7];
    private int[] indice = new int[2];
    private char jeton;
    private static char jetonAI;
    private static char jetonJoueur;

    Puissance4Struct() {
        jeton = 'X';
        initialisationTableau();
    }

    static char getPlayerToken() {
        return jetonJoueur;
    }

    static void setJetonJoueur(char jetonJoueur) {
        if (jetonJoueur == 'O' || jetonJoueur == 'X') {
            Puissance4Struct.jetonJoueur = jetonJoueur;
        }
    }

    int getNombreCases() {
        return tableau.length * tableau[0].length;
    }

    int getWidthTable() {
        return tableau[0].length;
    }

    /**
     * Place l'indice sur la première ligne possible d'une colonne choisie. Renvoie faux s'il n'y a aps de colonne possible.
     * @param colonne l'indice de la colonne.
     * @return <code>false</code> s'il n'y a pas de colonne possible.
     */
    boolean findIndex(int colonne) {
        int i = 0;

        while (i < tableau.length && tableau[i][colonne] == ' ') {
            i++;
        }

        if (i != 0) {
            indice[0] = i - 1;
            indice[1] = colonne;
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * Attribue des valeur à la variable indice
     * @param ligne ligne du puissance4
     * @param colonne colonne du puisance 4
     */
    private void setIndice(int ligne, int colonne) {
        indice[0] = ligne;
        indice[1] = colonne;
    }

    /**
     * Copie de setIndice, mais ne modifie pas la variable indice de l'objet
     * @param colonne l'indice de la colonne
     * @return retourne le nombre de ligne de la colonne passée en argument. (est-il nécessaire de retourner une valeur?
     * ('void' est peut-être plus approprié)
     */
    private int getIndiceLigne(int colonne) {
        int i = 0;
        while (i < tableau.length - 1 && tableau[i][colonne] == ' ') {
            i++;
        }

        return i;
    }


    char getToken() {
        return jeton;
    }

    void setToken(char jeton) {
        if (jeton == 'X' || jeton == 'O')
            this.jeton = jeton;
    }


    void changeToken() {
        if (jeton == 'O')
            jeton = 'X';
        else {
            jeton = 'O';
        }
    }


    private void initialisationTableau() {
        for (int i = 0; i < tableau.length; i++) {
            for (int j = 0; j < tableau[0].length; j++) {
                tableau[i][j] = ' ';
            }
        }
    }


    void printConnect4() {
        for (int i = 0; i < tableau[0].length; i++) // Affiche les nombres au dessus des colonnes
            if (i < 10)
                System.out.print("   "+ (i+1));
            else
                System.out.print("  "+ (i+1));
        System.out.println();

        for (char[] chars : tableau) { // Affichage du puissance 4
            for (int j = 0; j < tableau[0].length; j++) {
                System.out.print(" | " + chars[j]);
            }
            System.out.println(" |");
            System.out.print(" ");

            for (int k = 0; k < tableau[0].length; k++)
                System.out.print("----");

            System.out.println("-");
        }

    }

    void putToken() {
        tableau[indice[0]][indice[1]] = jeton;
    }


    /**
     * Procède aux vérifications permettant de savoir si le joueur a gagné.
     * @return <code>true</code> si le joueur a gagné.
     */
    boolean verifWinner() {
        // Le vectorValeur faisant à  chaque fois la vérification du vecteur opposé,
        // il n'est pas nécéssaire de mettre les 8 directions.

        for (int k = -1; k <= 1; k++) {
            if (vectorValeur( k, -1) >= 4)
                return true;
        }

        return vectorValeur(1, 0) >= 4;
    }

    /**
     * Donne le nombre de même jetons sur un sens de l'indice
     * @param vectorI direction verticale du vecteur donné
     * @param vectorJ direction horizontale du vecteur donné
     * @return le nombre de même jeton dans le sens donné
     */
    private int vectorValeur(int vectorI, int vectorJ) {
        int recurrence = 0;
        int i = indice[0];
        int j = indice[1];
        int edgeI = calculEdgePoint(vectorI, 'i'), edgeJ = calculEdgePoint(vectorJ, 'j');

        for (int k = 0; k < 2; k++) {
            do {
                i += vectorI;
                j += vectorJ;
                recurrence++;
            } while (i != edgeI && j != edgeJ && tableau[i][j] == jeton);

            if (k == 0) {
                i = indice[0];
                j = indice[1];
                vectorI = -vectorI; // Continue à  chercher dans la direction opposée, c'est la raison pour le For avec k
                vectorJ = -vectorJ;
                edgeI = calculEdgePoint(vectorI, 'i');
                edgeJ = calculEdgePoint(vectorJ, 'j');
            }

        }
        recurrence -= 1;

        return recurrence;
    }

    /**
     * @param vectorPoint donne la case de l'indice
     * @param indice définit si c'est un point i ou j
     * @return le point prime que i ou j ne devra pas dépasser
     */
    private int calculEdgePoint(int vectorPoint, char indice) { // Donne la valeur pour i ou j à ne pas dépasser
        if (vectorPoint == -1 || vectorPoint == 0) {
            return -1;
        } else if (indice == 'i') {
            return tableau.length;
        } else {
            return tableau[0].length;
        }
    }

    /**
     * Donne la valeur de la combinaison testée.
     * @return la valeur de la combinaison.
     */
    private int verifTailleCombinaison() {
        int valCombinaisons = 0;

        for (int k = -1; k <= 1; k = k + 2) {
            valCombinaisons += vectorValeur(k, -1);
        }

        valCombinaisons += vectorValeur(1, 0);

        return valCombinaisons;
    }

    /**
     * Indique si le tour actuel est celui de l'IA
     * @return <code>true</code> si le jeton utilisé correspond à celui de l'IA
     */
    boolean IATurn() {
        return getToken() == getAIToken();
    }

    /**
     * Donne une évaluation du tableau actuelle.
     * Elle est négative ou positive si le tableau est favorable pour l'humain ou l'IA respectivement
     * @return l'évaluation du tableau.
     */
    int tableEvaluation() {
        int baseI = indice[0], baseJ = indice[1];
        int score = 0;

        for (int ligne = 0; ligne < tableau.length; ligne++) {
            for (int colonne = 0; colonne < tableau[0].length; colonne++) {
                if (findIndex(colonne)) {
                    putToken();
                    score += verifTailleCombinaison();
                    eraseTokenColon(colonne);
                }
            }
        }

        changeToken();

        for (int col = 0; col < tableau.length; col++) {
            if (findIndex(col)) {
                putToken();
                score -= verifTailleCombinaison();
                eraseTokenColon(col);
            }
        }

        changeToken();

        setIndice(baseI, baseJ);

        return score;
    }

    /**
     * Efface le jeton de la colonne choisie
     * @param colonne la colonne choisie
     */
    void eraseTokenColon(int colonne) {
        int ligne = getIndiceLigne(colonne);
        tableau[ligne][colonne] = ' ';
    }


    /**
     * Efface le jeton actuellement sélectionné par l'indice
     */
    void eraseToken() {
        tableau[indice[0]][indice[1]] = ' ';
    }

    static char getAIToken() {
        return jetonAI;
    }

    static void setJetonAI(char jetonAI) {
        if (jetonAI == 'X' || jetonAI == 'O') {
            Puissance4Struct.jetonAI = jetonAI;
        }
    }

}

0 个答案:

没有答案