我正在开发一个在Java中使用不同的AI和ML技术的Tic Tac Toe版本,但是我在减慢模拟方面遇到了一些问题。 基本上我希望看到游戏好像它是由两个普通玩家玩的,而现在我一跑完就得到了最终的游戏状态。
这是我的代码:
Board.java
package com.nicolagheza.tictactoe;
public class Board {
// package access
Cell[][] cells; // 2D array of ROWS-by-COLS Cell instances
/** Constructor to initialize the game board */
public Board() {
cells = new Cell[GameMain.ROWS][GameMain.COLS]; // allocate the array
for (int row = 0; row < GameMain.ROWS; row++) {
for (int col = 0; col < GameMain.COLS; col++) {
cells[row][col] = new Cell(row, col); // allocate element of array
}
}
}
/** Initialize (or re-initialize) the game board */
public void init() {
for (int row = 0; row < GameMain.ROWS; row++) {
for (int col = 0; col < GameMain.COLS; col++) {
cells[row][col].clear(); // clear the cell content
}
}
}
/** Return true if it is a draw (i.e., no more EMPTY cell) */
public boolean isDraw() {
for (int row = 0; row < GameMain.ROWS; row++) {
for (int col = 0; col < GameMain.COLS; col++) {
if (cells[row][col].content == Seed.EMPTY) {
return false; // an empty seed found, not a draw, exit
}
}
}
return true; // no empty cell, it's a draw
}
/** Return true if the player with "seed" has won after placing at (seedRow, seedCol) */
public boolean hasWon(Seed seed, int seedRow, int seedCol) {
return (cells[seedRow][0].content == seed // 3-in-the-row
&& cells[seedRow][1].content == seed
&& cells[seedRow][2].content == seed
|| cells[0][seedCol].content == seed // 3-in-the-column
&& cells[1][seedCol].content == seed
&& cells[2][seedCol].content == seed
|| seedRow == seedCol // 3-in-the-diagonal
&& cells[0][0].content == seed
&& cells[1][1].content == seed
&& cells[2][2].content == seed
|| seedRow + seedCol == 2 // 3-in-the-opposite-diagonal
&& cells[0][2].content == seed
&& cells[1][1].content == seed
&& cells[2][0].content == seed);
}
}
GameState.java
package com.nicolagheza.tictactoe;
public enum GameState {
PLAYING, DRAW, CROSS_WON, NOUGHT_WON
}
GameMain.java
package com.nicolagheza.tictactoe;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class GameMain extends JPanel{
// Named-constants for the game board
public static final int ROWS = 3; // ROWS by COLS cells
public static final int COLS = 3;
public static final String TITLE = "Tic Tac Toe";
// Name-constants for the various dimensions used for graphics drawing
public static final int CELL_SIZE = 100; // cell width and height (square)
public static final int CANVAS_WIDTH = CELL_SIZE * COLS; // the drawing canvas
public static final int CANVAS_HEIGHT = CELL_SIZE * ROWS;
public static final int GRID_WIDTH = 8; // Grid-line's width
public static final int GRID_WIDHT_HALF = GRID_WIDTH / 2; // Grid-line's half-width
// Symbols (cross/nought) are displayed inside a cell, with padding from border
public static final int CELL_PADDING = CELL_SIZE / 6;
public static final int SYMBOL_SIZE = CELL_SIZE - CELL_PADDING * 2;
public static final int SYMBOL_STROKE_WIDTH = 8; // pen's stroke width
private Board board; // the game board
private BoardView boardView;
private AIPlayer aiPlayer1;
private AIPlayer aiPlayer2;
private GameState currentState; // the current state of the game
private Seed currentPlayer; // the current player
private JLabel statusBar; // for displaying status message
/** Constructor to setup the UI and game components */
public GameMain() {
// This JPanel fires MouseEvent
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
// Get the row and column clicked
int rowSelected = mouseY / CELL_SIZE;
int colSelected = mouseX / CELL_SIZE;
if (currentState == GameState.PLAYING) {
if (rowSelected >= 0 && rowSelected < ROWS
&& colSelected >= 0 && colSelected < COLS
&& board.cells[rowSelected][colSelected].content == Seed.EMPTY) {
board.cells[rowSelected][colSelected].content = currentPlayer; // move
updateGame(currentPlayer, rowSelected, colSelected); // update currentState
}
} else { // game over
initGame();
}
// Refresh the drawing canvas
repaint();
currentPlayer = (currentPlayer == Seed.CROSS) ? Seed.NOUGHT : Seed.CROSS;
}
});
// Setup the status bar (JLabel) to display status message
statusBar = new JLabel(" ");
statusBar.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD, 14));
statusBar.setBorder(BorderFactory.createEmptyBorder(2, 5, 4, 5));
statusBar.setOpaque(true);
statusBar.setBackground(Color.LIGHT_GRAY);
setLayout(new BorderLayout());
add(statusBar, BorderLayout.SOUTH);
setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT + 30));
board = new Board(); // allocate the game-board
boardView = new BoardView(board.cells);
initGame();
initAI();
}
private void initAI() {
aiPlayer1 = new AIPlayerRuleBased(board);
aiPlayer1.setSeed(Seed.CROSS);
aiPlayer2 = new AIPlayerTableLookup(board);
aiPlayer2.setSeed(Seed.NOUGHT);
}
/** Initialize the game-board contents and the current-state */
public void initGame() {
for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
board.cells[row][col].content = Seed.EMPTY; // all cells empty
}
}
currentState = GameState.PLAYING; // ready to play
currentPlayer = Seed.CROSS; // cross plays first
}
public void makeAIMove(AIPlayer player) {
int[] move = player.move();
if (move != null) {
System.out.println("Player " + player.mySeed + " row: " + move[0] + " col: " + move[1]);
board.cells[move[0]][move[1]].content = player.mySeed;
updateGame(currentPlayer, move[0], move[1]);
repaint();
currentPlayer = (currentPlayer == Seed.CROSS) ? Seed.NOUGHT : Seed.CROSS;
}
}
/** Update the currentState after the player with "theSeed" has placed on (row, col) */
public void updateGame(Seed theSeed, int row, int col) {
if(board.hasWon(theSeed, row, col)) { // check for win
currentState = (theSeed == Seed.CROSS) ? GameState.CROSS_WON : GameState.NOUGHT_WON;
} else if (board.isDraw()) { // check for draw
currentState = GameState.DRAW;
}
// Otherwise, no change to current state (PLAYING).
}
/** Custom painting codes on this JPanel */
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // fill background
setBackground(Color.WHITE); // set its background color
boardView.paint(g); // ask the game board to paint itself
// Print status-ba message
if (currentState == GameState.PLAYING) {
statusBar.setForeground(Color.BLACK);
if (currentPlayer == Seed.CROSS) {
statusBar.setText("X's Turn");
} else {
statusBar.setText("O's Turn");
}
} else if (currentState == GameState.DRAW) {
statusBar.setForeground(Color.RED);
statusBar.setText("It's a Draw! Click to play again.");
} else if (currentState == GameState.CROSS_WON) {
statusBar.setForeground(Color.RED);
statusBar.setText("'X' Won! Click to play again.");
} else if (currentState == GameState.NOUGHT_WON) {
statusBar.setForeground(Color.RED);
statusBar.setText("'O' Won! Click to play again.");
}
}
public void getNextState() {
if (currentPlayer == aiPlayer1.mySeed) {
makeAIMove(aiPlayer1);
}
if (currentPlayer == aiPlayer2.mySeed) {
makeAIMove(aiPlayer2);
}
}
/** The entry "main" method */
public static void main(String args[]) {
// Run GUI construction codes in Event-Dispatching thread for thread safety
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame(TITLE);
// Set the content-pane of the JFrame to an instance of main JPanel
GameMain game = new GameMain();
frame.setContentPane(game);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
while (game.currentState == GameState.PLAYING) {
game.getNextState();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
}
AIPlayer.java
package com.nicolagheza.tictactoe;
public abstract class AIPlayer {
protected int ROWS = GameMain.ROWS; // number of rows
protected int COLS = GameMain.COLS; // number of cols
protected Cell[][] cells; // the board's ROWs-by-COLs array of Cells
protected Seed mySeed; // computer's seed
protected Seed oppSeed; // opponent's seed
/** Constructor with reference to game board */
public AIPlayer(Board board) {
cells = board.cells;
}
/** Set/change the seed used by computer and opponent */
public void setSeed(Seed seed) {
this.mySeed = seed;
oppSeed = (mySeed == Seed.CROSS) ? Seed.NOUGHT : Seed.CROSS;
}
public abstract int[] move();
}
AIPlayerRuleBased.java
package com.nicolagheza.tictactoe;
import java.util.ArrayList;
import java.util.List;
public class AIPlayerRuleBased extends AIPlayer {
/**
* Constructor with reference to game board
*
* @param board
*/
public AIPlayerRuleBased(Board board) {
super(board);
}
private List<int[]> generatePossibleMoves() {
List<int[]> nextMoves = new ArrayList<int[]>();
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
if (cells[row][col].content == Seed.EMPTY)
nextMoves.add(new int[] {row, col});
}
}
return nextMoves;
}
@Override
public int[] move() {
List<int[]> nextPossibleMoves = generatePossibleMoves();
// Rule 1: If I have a winning move, take it.
for (int[] nextMove : nextPossibleMoves) {
// Try this move
cells[nextMove[0]][nextMove[1]].content = mySeed;
if (hasWon(mySeed)) {
cells[nextMove[0]][nextMove[1]].content = Seed.EMPTY; // Undo move
return nextMove;
}
cells[nextMove[0]][nextMove[1]].content = Seed.EMPTY; // Undo move
}
// Rule 2: If the opponent has a winning move, block it
for (int[] nextMove: nextPossibleMoves) {
// Try this move
cells[nextMove[0]][nextMove[1]].content = oppSeed;
if (hasWon(oppSeed)) {
cells[nextMove[0]][nextMove[1]].content = Seed.EMPTY; // Undo move
return nextMove;
}
cells[nextMove[0]][nextMove[1]].content = Seed.EMPTY; // Undo move
}
// Moves {row, col} in order of preferences. {0,0} at top-left corner
int[][] preferredMoves = {
{1,1}, {0,0}, {0,2}, {2,0}, {2,2},
{0,1}, {1,0}, {1,2}, {2,1}};
for (int[] move : preferredMoves) {
if (cells[move[0]][move[1]].content == Seed.EMPTY) {
return move;
}
}
assert false : "No empty cell?!";
return null;
}
private int[] winningPatterns = {
0b111000000, 0b000111000, 0b000000111, // rows
0b100100100, 0b010010010, 0b001001001, // cols
0b100010001, 0b001010100 // diagonals
};
/** Returns true if thePlayer wins */
private boolean hasWon(Seed thePlayer) {
int pattern = 0b000000000; // 9-bit pattern for the 9 cells
for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
if (cells[row][col].content == thePlayer) {
pattern |= (1 << (row * COLS + col));
}
}
}
for (int winningPattern : winningPatterns) {
if ((pattern & winningPattern) == winningPattern) return true;
}
return false;
}
}
我尝试使用thread.sleep,但它无法正常工作。
答案 0 :(得分:0)
(代表OP发布)。
我使用Javax.swing.Timer解决了我的问题。以下是我如何做以防万一其他人感兴趣的事情:
new javax.swing.Timer(TIMER_DELAY, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (game.currentState != GameState.PLAYING)
return;
game.getNextState();
}
}).start();