Libgdx中的Connect4

时间:2015-04-04 01:41:24

标签: java libgdx minimax

好的,所以我一直在关注java中的Connect4这个教程,我试图修改它以适应我已经拥有的并适合libgdx。实施后,我有一些奇怪的问题。

问题1:在我第一步之后,计算机用他的芯片填满整个底行,然后第一步。

问题2:计算机没有显示任何A.I.并且只是从第一列和第一行开始,并在那里放置一个芯片。计算机将继续遵循此模式。

问题3:我的获胜检查员不再意识到我是否赢了比赛,但确实意识到计算机赢了。当我第一次设计游戏时,我开始让计算机随机放置芯片(用于测试),我的获胜检查器为计算机和我自己工作。

我关注的文章是:Connect4 in Java

这是我的代码。

ConnectFour.java

package com.comp452.tme31;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class ConnectFour extends ApplicationAdapter implements InputProcessor {

    // Create final ints for number of columns and rows in game.
    protected final static int COLUMNS = 7;
    protected final static int ROWS = 6;
    protected final static int TILESIZE = 64;

    // Create boolean to determine if player can take a turn.
    protected boolean playersTurn = true;

    // Create boolean for gameover.
    protected boolean gameOver = false;

    // Create boolean for winners.
    private boolean winner = false;

    // Sprite batch for texture drawing.
    SpriteBatch batch;
    // Create textures to represent board and player pieces.
    Texture drawingTile, empty, player, computer;

    // Create 2D array to hold game board.
    private final static int[][] gameBoard = new int[COLUMNS][ROWS];

    public static int[][] getGameBoard() {
        return gameBoard;
    }

    // Create variables to display status message.
    BitmapFont mainStatusDisplay;
    public static String mainStatusString;
    public static String winningString;

    // Create and set max depth for tree search
    private final int MAX_DEPTH = 4;

    // Create win, loss and nothing for zero sum game.
    private final float WIN = 1f;
    private final float LOSE = -1f;
    private final float TIE = 0f;

    @Override
    public void create () {
        batch = new SpriteBatch();
        empty = new Texture("empty.jpg");
        player = new Texture("player.jpg");
        computer = new Texture("computer.jpg");

        Gdx.input.setInputProcessor(this);

        // Initialize display for status messages.
        mainStatusDisplay = new BitmapFont();
        mainStatusString = "Player's Turn";
        winningString = "";
    }

    @Override
    public void render () {
        update();
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        batch.begin();
        drawBoard();
        mainStatusDisplay.setColor(Color.YELLOW);
        mainStatusDisplay.draw(batch, mainStatusString, 32, 416);
        batch.end();
    }

    public void drawBoard() {

        for (int i = 0; i < COLUMNS; i ++) {
            for (int j = 0; j < ROWS; j++) {
                if (gameBoard[i][j] == 0) {
                    drawingTile = empty; 
                }
                else if (gameBoard[i][j] == 1) {
                    drawingTile = player;
                }

                else if (gameBoard[i][j] == 2) {
                    drawingTile = computer;
                }
                batch.draw(drawingTile, i * 64, j * 64);
            }
        }
    }

    // Method update handles updates to game logic.
    public void update() {
        // If it's gameover, end the game.
        if (gameOver) {
            // Set players turn to true to prevent computer from taking another turn.
            playersTurn = true;
            // Set status message to winning message;
            mainStatusString = winningString;
            // Disable input processor to prevent player from taking another turn.
            Gdx.input.setInputProcessor(null);
        }        

        // If it's not players turn, call computersTurn.
        else if (!playersTurn) {
            mainStatusString = "Computer's Turn";
            computersTurn();
        }

        if (checkForWin(1) && checkForWin(2)) {
            gameOver = true;
        }
    }

    public void computersTurn() {
        double maxScore = 2. * Integer.MIN_VALUE;
        int xValue = 0;

        // Search the gameboard and find the best move.
        for (int x = 0; x < COLUMNS; x++) {
            // If x column is a value move...
            if (canMove(x)) {
                // Set score of move from function.
                double score = moveScore(x);

                // If score is greater than max score...
                if (score > maxScore) {
                    // Set score to max score and xValue to column.
                    maxScore = score;
                    xValue = x;
                    // If the score is a win, break from loop.
                    if (score == WIN) {
                        break;
                    }
                }
            }
        }
        // Set the piece for player at column as x.
        setPiece(2, xValue);

        // Set players turn and string status.
        playersTurn = true;
        mainStatusString = "Player's Turn";
    }

    // Method moveScore determines the value of a move and returns it.
    public double moveScore(int xValue) {
        // Set the piece in place.
        setPiece(2, xValue);
        // Get the score and check it's value with alpha beta pruning.
        double score = alphaBetaPrune(MAX_DEPTH, Integer.MIN_VALUE, Integer.MAX_VALUE, 1);
        // Remove the piece.
        takeAwayPiece(xValue);

        return score;
    }

    public double alphaBetaPrune(int depth, double alpha, double beta, int whoPlayed) {
        winner = checkForWin(1) || checkForWin(2);
        // If we've reached the max depth of the tree or there is a winner...
        if (depth == 0 || winner) {
            double score;
            // If there is a winner...
            if (winner) {
                // If player is the winner...
                if (checkForWin(1)) {
                    // Set a losing score (Computer does not want player to win).
                    score = LOSE;
                }
                // Else this is a win for the computer...
                else {
                    // Set score to a win.
                    score = WIN;
                }
            }
            // Otherwise there is no winner...
            else {
                // Set score to TIE (0).
                score = TIE;
            }

            // Return score and remove depth level.
            return score / (MAX_DEPTH - depth +1);
        }

        // If computer is making the move...
        if (whoPlayed == 2) {
            // Iterate through gameboard.
            for (int x = 0; x < COLUMNS; x++) {
                // Check and see if next move can be made.
                if (canMove(x)) {
                    // Make move for computer to x.
                    setPiece(2, x);
                    // Set alpha equal to return from recursion step minus one depth level.
                    alpha = Math.max(alpha, alphaBetaPrune(depth - 1, alpha, beta, 1));
                    // Remove piece.
                    takeAwayPiece(x);
                    // Check returned alpha against beta and break from loop if beta is less than alpha.
                    if (beta <= alpha) {
                        break;
                    }
                }
            }
            // We're here if alpha is larger and we didn't break from loop.
            return alpha;
        }
        // Else if player is making move...
        else {
            // Iterate through gameboard.
            for (int x = 0; x < COLUMNS; x++) {
                // Check and see if next move can be made.
                if (canMove(x)) {
                    // Make move for player to x.
                    setPiece(1, x);
                    // Set beta equal to return from recursion step minus one depth level for beta.
                    beta = Math.min(beta, alphaBetaPrune(depth - 1, alpha, beta, 2));
                    // Remove piece.
                    takeAwayPiece(x);
                    // Check returned alpha against beta and break from loop if beta is less than alpha.
                    if (beta <= alpha) {
                        break;
                    }
                }
            }
            // We're here if alpha is larger than beta and we didn't break from loop.
            return beta;
        }
    }

    // Method setPiece takes two int values as parameters and places a piece on the game board.
    public void setPiece(int whoPlayed, int xValue) {
        // For loop to iterate through each row.
        for (int i = 0; i < ROWS; i++) {
            // If row is empty...
            if (gameBoard[xValue][i] == 0) {
                // Place piece on the board.
                gameBoard[xValue][i] = whoPlayed;
                break;
            }
        }
    }

    // Method takeAwayPiece takes two int values as parameters and removes a piece from the board.
    public void takeAwayPiece(int xValue) {
        // For loop to iterate through each row.
        for (int i = ROWS - 1; i > 0; i--) {
            // If row contains a piece..
            if (gameBoard[xValue][i] != 0) {
                // Remove piece.
                gameBoard[xValue][i] = 0;
                break;
            }
        }
    }

    // Method to determine if a move is valid
    public boolean canMove(int xValue) {
        // If the top spot in the given column is 0, return true.
        return (gameBoard[xValue][ROWS-1] == 0);
    }

    // Method checkForWin takes a flag and checks to see if that player has won the game.
    public boolean checkForWin(int whoPlayed) {
        // Create counter to check for 4 in a row.
        int win = 0;
        // Iterate through gameboard and count pieces in a row.
        for (int y = 0; y < ROWS; y++) {
            for (int x = 0; x < COLUMNS; x++) {
                // If piece is player who is checking, increment counter.
                if (gameBoard[x][y] == whoPlayed) {
                    win++;
                }
                // Not in a row, set counter to 0.
                else {
                    win = 0;
                }
                if (win == 4) {
                    break;
                }
            }
            // If win counter is 4, winner.
            if (win == 4) {
                winningString = "Horizontal Win for Player " + whoPlayed;
                return true;
            }
            // Else, reset win counter and check next column.
            else {
                win = 0;
            }
        }

        // Iterate through gameboard and count pieces in a column.
        for (int x = 0; x < COLUMNS; x++) {
            for (int y = 0; y < ROWS; y++) {
                // If piece is player who is checking, increment counter.
                if (gameBoard[x][y] == whoPlayed) {
                    win++;
                }
                // Not in a row, set counter to 0.
                else {
                    win = 0;
                }
                if (win == 4) {
                    break;
                }
            }
            // If win counter is 4, player won.
            if (win == 4) {
                winningString = "Vertical Win for Player " + whoPlayed;
                return true;
            }
            // Else, reset win counter and check next column.
            else {
                win = 0;
            }
        }

        // Iterate through gameboard and count pieces in a diagonal row, left to right.
        for (int x = 0; x < 3; x++) {
            for (int y = 0; y < 2; y++) {
                // If piece is player who is checking, check next piece diagonally.
                if (gameBoard[x][y] == whoPlayed) {
                    // Then check next diagonal piece.
                    if (gameBoard[x+1][y+1] == whoPlayed) {
                        // Then check next diagonal piece.
                        if (gameBoard[x+2][y+2] == whoPlayed) {
                            // Then check last diagonal piece.
                            if (gameBoard[x+3][y+3] == whoPlayed) {
                                // Set winning message to player won and set gameover flag.
                                winningString = "Diagonal Win (LR) for Player " + whoPlayed;
                                // Exit function.
                                return true;
                            }
                        }
                    }
                }
            }
        }

        // Iterate through gameboard and count pieces in a diagonal row, right to left.
        for (int x = 3; x < COLUMNS; x++) {
            for (int y = 0; y < 2; y++) {
                // If piece is player who is checking, check next piece diagonally.
                if (gameBoard[x][y] == whoPlayed) {
                    // Then check next diagonal piece.
                    if (gameBoard[x-1][y+1] == whoPlayed) {
                        // Then check next diagonal piece.
                        if (gameBoard[x-2][y+2] == whoPlayed) {
                            // Then check last diagonal piece.
                            if (gameBoard[x-3][y+3] == whoPlayed) {
                                // Set winning message to player won and set gameover flag.
                                winningString = "Diagonal Win (RL) for Player " + whoPlayed;
                                // Exit function.
                                return true;
                            }
                        }
                    }
                }
            }
        }

        // Iterate through gameboard and if no 0 slots remain, game is a tie.
        for (int x = 0; x < COLUMNS; x++) {
            for (int y = 0; y < ROWS; y++) {
                // If a 0 slot remains, return.
                if (gameBoard[x][y] == 0) {
                    return false;
                }
            }
        }

        // If we're here, then there was no winners and no slots left.
        winningString = "Tie Game";
        return false;
    }

    @Override
    public boolean touchDown(int x, int y, int pointer, int button) {
        if (playersTurn) {
            if (button == Input.Buttons.LEFT) {
                if (canMove(x / TILESIZE)) {
                    setPiece(1, x / TILESIZE);
                    playersTurn = false;
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public boolean touchUp(int i, int i1, int i2, int i3) {
        return false;
    }

    @Override
    public boolean touchDragged(int i, int i1, int i2) {
        return false;
    }

    @Override
    public boolean mouseMoved(int i, int i1) {
        return false;
    }

    @Override
    public boolean scrolled(int i) {
        return false;
    }
    @Override
    public boolean keyDown(int i) {
        return false;
    }

    @Override
    public boolean keyUp(int i) {
        return false;
    }

    @Override
    public boolean keyTyped(char c) {
        return false;
    }
}

谢谢!

1 个答案:

答案 0 :(得分:0)

因此,在第一次修复之后,很容易缩小问题的另一个问题。在我的takeAwayPiece方法中,它没有删除底行上的任何内容,所以当A.I计算它的下一步时,它放在底部的测试片没有工作。

因此在takeAwayPiece方法中,我将其更改为以下

  // Method takeAwayPiece takes two int values as parameters and removes a piece from the board.
    public void takeAwayPiece(int xValue) {
        // For loop to iterate through each row.
        for (int i = ROWS; i > 0; i--) {
            System.out.println(i);
            // If row contains a piece..
            if (gameBoard[xValue][i-1] != 0) {
                // Remove piece.
                //System.out.println("Piece at column " + xValue + " " + i);
                gameBoard[xValue][i-1] = 0;
                break;
            }
        }
    }

您可能会注意到,不同之处在于我开始=到ROWS而不是ROWS -1然后我正在检查x-1而不是x(之前我只是尝试将条件从i> 0更改为i = 0但那有一些疯狂的奇怪结果。)