【发布时间】:2020-04-22 18:25:40
【问题描述】:
所以,我正在开展一个项目,我希望能够将相同的棋盘用于不同的游戏,只需更改列数和行数。到目前为止,我有四个班级和两个枚举,但我的问题是如何制作类似通用板的东西来玩井字游戏并连接四个?我会在下面添加我的课程。
Board.java
package Board;
import java.awt.*;
import Cells.Cell;
import Cells.Properties;
/**
* The Board class models the ROWS-by-COLS game-board.
*/
public class BoardTTT {
// 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 heigh (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
// package access
private Cell[][] cells; // composes of 2D array of ROWS-by-COLS Cell instances
/** Constructor to initialize the game board */
public BoardTTT() {
setCells(new Cell[ROWS][COLS]); // allocate the array
for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
getCells()[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 < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
getCells()[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 < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
if (getCells()[row][col].content == Properties.EMPTY) {
return false; // an empty Properties found, not a draw, exit
}
}
}
return true; // no empty cell, it's a draw
}
/** Return true if the player with "Properties" has won after placing at
(PropertiesRow, PropertiesCol) */
public boolean hasWon(Properties Properties, int PropertiesRow, int PropertiesCol) {
return (getCells()[PropertiesRow][0].content == Properties // 3-in-the-row
&& getCells()[PropertiesRow][1].content == Properties
&& getCells()[PropertiesRow][2].content == Properties
|| getCells()[0][PropertiesCol].content == Properties // 3-in-the-column
&& getCells()[1][PropertiesCol].content == Properties
&& getCells()[2][PropertiesCol].content == Properties
|| PropertiesRow == PropertiesCol // 3-in-the-diagonal
&& getCells()[0][0].content == Properties
&& getCells()[1][1].content == Properties
&& getCells()[2][2].content == Properties
|| PropertiesRow + PropertiesCol == 2 // 3-in-the-opposite-diagonal
&& getCells()[0][2].content == Properties
&& getCells()[1][1].content == Properties
&& getCells()[2][0].content == Properties);
}
/** Paint itself on the graphics canvas, given the Graphics context */
public void paint(Graphics g) {
// Draw the grid-lines
g.setColor(Color.GRAY);
for (int row = 1; row < ROWS; ++row) {
g.fillRoundRect(0, CELL_SIZE * row - GRID_WIDHT_HALF,
CANVAS_WIDTH - 1, GRID_WIDTH,
GRID_WIDTH, GRID_WIDTH);
}
for (int col = 1; col < COLS; ++col) {
g.fillRoundRect(CELL_SIZE * col - GRID_WIDHT_HALF, 0,
GRID_WIDTH, CANVAS_HEIGHT - 1,
GRID_WIDTH, GRID_WIDTH);
}
// Draw all the cells
for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
getCells()[row][col].paint(g); // ask the cell to paint itself
}
}
}
public Cell[][] getCells() {
return cells;
}
public void setCells(Cell[][] cells) {
this.cells = cells;
}
}
Cell.java
package Cells;
import java.awt.*;
import Board.BoardTTT;
/**
* The Cell class models each individual cell of the game BoardTTT.
*/
public class Cell {
// Package access
public Properties content; // content of this cell (Properties.EMPTY, Properties.CROSS, or Properties.NOUGHT)
int row, col; // row and column of this cell
/** Constructor to initialize this cell with the specified row and col */
public Cell(int row, int col) {
this.row = row;
this.col = col;
clear(); // clear content
}
/** Clear this cell's content to EMPTY */
public void clear() {
setContent(Properties.EMPTY);
}
/** Paint itself on the graphics canvas, given the Graphics context */
public void paint(Graphics g) {
// Use Graphics2D which allows us to set the pen's stroke
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke(BoardTTT.SYMBOL_STROKE_WIDTH,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); // Graphics2D only
// Draw the Properties if it is not empty
int x1 = col * BoardTTT.CELL_SIZE + BoardTTT.CELL_PADDING;
int y1 = row * BoardTTT.CELL_SIZE + BoardTTT.CELL_PADDING;
if (getContent() == Properties.CROSS) {
g2d.setColor(Color.RED);
int x2 = (col + 1) * BoardTTT.CELL_SIZE - BoardTTT.CELL_PADDING;
int y2 = (row + 1) * BoardTTT.CELL_SIZE - BoardTTT.CELL_PADDING;
g2d.drawLine(x1, y1, x2, y2);
g2d.drawLine(x2, y1, x1, y2);
} else if (getContent() == Properties.NOUGHT) {
g2d.setColor(Color.BLUE);
g2d.drawOval(x1, y1, BoardTTT.SYMBOL_SIZE, BoardTTT.SYMBOL_SIZE);
}
}
public Properties getContent() {
return content;
}
public void setContent(Properties content) {
this.content = content;
}
}
超级游戏
package Game;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import Board.BoardTTT;
import Cells.Properties;
import Cells.Cell;
/**
* Tic-Tac-Toe: Two-player Graphic version with better OO design.
* The Board and Cell classes are separated in their own classes.
*/
public class SuperGame extends JPanel{
public BoardTTT board; // the game board
public GameState currentState; // the current state of the game
public Properties currentPlayer; // the current player
public JLabel statusBar; // for displaying status message
/** Constructor to setup the UI and game components */
public SuperGame() {
// 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.PAGE_END); // same as SOUTH
setPreferredSize(new Dimension(BoardTTT.CANVAS_WIDTH, BoardTTT.CANVAS_HEIGHT + 30));
// account for statusBar in height
board = new BoardTTT(); // allocate the game-board
initGame(); // Initialize the game variables
}
/** Initialize the game-board contents and the current-state */
public void initGame() {
for (int row = 0; row < BoardTTT.ROWS; ++row) {
for (int col = 0; col < BoardTTT.COLS; ++col) {
board.getCells()[row][col].content = Properties.EMPTY; // all cells empty
}
}
currentState = GameState.PLAYING; // ready to play
currentPlayer = Properties.CROSS; // cross plays first
}
/** Update the currentState after the player with "theProperties" has placed on (row, col) */
public void updateGame(Properties theProperties, int row, int col) {
if (board.hasWon(theProperties, row, col)) { // check for win
currentState = (theProperties == Properties.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) { // invoke via repaint()
super.paintComponent(g); // fill background
setBackground(Color.WHITE); // set its background color
board.paint(g); // ask the game board to paint itself
// Print status-bar message
if (currentState == GameState.PLAYING) {
statusBar.setForeground(Color.BLACK);
if (currentPlayer == Properties.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.");
}
}
}
MainTTT.java
package Game;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import Board.BoardTTT;
import Cells.Properties;
import Cells.Cell;
/**
* Tic-Tac-Toe: Two-player Graphic version with better OO design.
* The Board and Cell classes are separated in their own classes.
*/
public class MainTTT extends SuperGame{
/** Constructor to setup the UI and game components */
public MainTTT() {
// This JPanel fires MouseEvent
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) { // mouse-clicked handler
int mouseX = e.getX();
int mouseY = e.getY();
// Get the row and column clicked
int rowSelected = mouseY / BoardTTT.CELL_SIZE;
int colSelected = mouseX / BoardTTT.CELL_SIZE;
if (currentState == GameState.PLAYING) {
if (rowSelected >= 0 && rowSelected < BoardTTT.ROWS
&& colSelected >= 0 && colSelected < BoardTTT.COLS
&& board.getCells()[rowSelected][colSelected].content == Properties.EMPTY) {
board.getCells()[rowSelected][colSelected].content = currentPlayer; // move
updateGame(currentPlayer, rowSelected, colSelected); // update currentState
// Switch player
currentPlayer = (currentPlayer == Properties.CROSS) ? Properties.NOUGHT : Properties.CROSS;
}
} else { // game over
initGame(); // restart the game
}
// Refresh the drawing canvas
repaint(); // Call-back paintComponent().
}
});
}
/** 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() {
public void run() {
JFrame frame = new JFrame(BoardTTT.TITLE);
// Set the content-pane of the JFrame to an instance of main JPanel
frame.setContentPane(new MainTTT());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null); // center the application window
frame.setVisible(true); // show it
}
});
}
}
Properties.java
package Cells;
public enum Properties {
EMPTY, CROSS, NOUGHT;
}
游戏状态
package Game;
public enum GameState {
PLAYING, DRAW, CROSS_WON, NOUGHT_WON
}
【问题讨论】:
-
如果您希望能够重用类并更改列数和行数,请不要将
COLS和ROWS指定为常量,而是将它们作为参数传递给构造函数。我真的很困惑为什么这不明显,特别是如果你足够先进,可以编写所有其他代码。
标签: java inheritance interface