lt,s_thethe swingg

请把下面的作文缩短到8句话The playground seems to shine with warmness as it pulled me closer to it.I looked at it,the warm sunlight shining on the monkey bars,the slides,the sandbox and ofcourse the swing.I walked closer to the swing and sat down.The same warmness and feeling came back.The feeling of being able to fly away from Earth,as if gravity cannot pull you down.I sat and swung lightly.Looking out at the playgrounds,memmories came back to me.The innocent laughter I had as a kid,the adorable smile.I sighed and thought,"Sometimes I wish times could be as simple as easy as they were before." Then I looked back at the playground and decided to walk away
The playground seems to shine with warmness as it pulled me closer to it.I looked at it,the warm sunlight shining on the monkey bars,the slides,the sandbox and ofcourse the swing.I walked closer to the swing and sat down.The same warmness and feeling came back.The feeling of being able to fly away from Earth,as if gravity cannot pull you down.I sat and swung lightly.Looking out at the playgrounds,memmories came back to me.The innocent laughter I had as
为您推荐:
其他类似问题
扫描下载二维码TABLE OF CONTENTS
Click on the Image to run the Applet Demo (tested on FireFox/Windows only)
Let's Start with a 2-Player Console Non-OO Tic-Tac-Toe
Let us start with a 2-player console (non-graphics) version of Tic-Tac-Toe, where player 'X' and player 'O' enter their moves successively, as shown below:
Player 'X', enter your move (row[1-3] column[1-3]): 2 2
-----------
-----------
Player 'O', enter your move (row[1-3] column[1-3]): 1 1
-----------
-----------
Player 'X', enter your move (row[1-3] column[1-3]): 1 3
-----------
-----------
Player 'O', enter your move (row[1-3] column[1-3]): 3 1
-----------
-----------
Player 'X', enter your move (row[1-3] column[1-3]): 2 2
This move at (2,2) is not valid. Try again...
Player 'X', enter your move (row[1-3] column[1-3]): 2 3
-----------
-----------
Player 'O', enter your move (row[1-3] column[1-3]): 2 1
-----------
-----------
Player 'O' won!
TTTCosnoleNonOO2P.java
import java.util.S
public class TTTConsoleNonOO2P {
public static final int EMPTY = 0;
public static final int CROSS = 1;
public static final int NOUGHT = 2;
public static final int PLAYING = 0;
public static final int DRAW = 1;
public static final int CROSS_WON = 2;
public static final int NOUGHT_WON = 3;
public static final int ROWS = 3, COLS = 3;
public static int[][] board = new int[ROWS][COLS];
public static int currentS
public static int currentP
public static int currntRow, currentC
public static Scanner in = new Scanner(System.in);
public static void main(String[] args) {
initGame();
playerMove(currentPlayer);
updateGame(currentPlayer, currntRow, currentCol);
printBoard();
if (currentState == CROSS_WON) {
System.out.println(&'X' won! Bye!&);
} else if (currentState == NOUGHT_WON) {
System.out.println(&'O' won! Bye!&);
} else if (currentState == DRAW) {
System.out.println(&It's a Draw! Bye!&);
currentPlayer = (currentPlayer == CROSS) ? NOUGHT : CROSS;
} while (currentState == PLAYING);
public static void initGame() {
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
board[row][col] = EMPTY;
currentState = PLAYING;
currentPlayer = CROSS;
public static void playerMove(int theSeed) {
boolean validInput =
if (theSeed == CROSS) {
System.out.print(&Player 'X', enter your move (row[1-3] column[1-3]): &);
System.out.print(&Player 'O', enter your move (row[1-3] column[1-3]): &);
int row = in.nextInt() - 1;
int col = in.nextInt() - 1;
if (row &= 0 && row & ROWS && col &= 0 && col & COLS && board[row][col] == EMPTY) {
currntRow =
currentCol =
board[currntRow][currentCol] = theS
validInput =
System.out.println(&This move at (& + (row + 1) + &,& + (col + 1)
+ &) is not valid. Try again...&);
} while (!validInput);
public static void updateGame(int theSeed, int currentRow, int currentCol) {
if (hasWon(theSeed, currentRow, currentCol)) {
currentState = (theSeed == CROSS) ? CROSS_WON : NOUGHT_WON;
} else if (isDraw()) {
currentState = DRAW;
public static boolean isDraw() {
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
if (board[row][col] == EMPTY) {
public static boolean hasWon(int theSeed, int currentRow, int currentCol) {
return (board[currentRow][0] == theSeed
&& board[currentRow][1] == theSeed
&& board[currentRow][2] == theSeed
|| board[0][currentCol] == theSeed
&& board[1][currentCol] == theSeed
&& board[2][currentCol] == theSeed
|| currentRow == currentCol
&& board[0][0] == theSeed
&& board[1][1] == theSeed
&& board[2][2] == theSeed
|| currentRow + currentCol == 2
&& board[0][2] == theSeed
&& board[1][1] == theSeed
&& board[2][0] == theSeed);
public static void printBoard() {
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
printCell(board[row][col]);
if (col != COLS - 1) {
System.out.print(&|&);
System.out.println();
if (row != ROWS - 1) {
System.out.println(&-----------&);
System.out.println();
public static void printCell(int content) {
switch (content) {
case EMPTY:
System.out.print(&
case NOUGHT: System.out.print(& O &);
case CROSS:
System.out.print(& X &);
Dissecting the Program
Non-OO programs (like C programs) are organized in methods, which access common global variables. (OO programs are organized in classes.) All the variables/methods shall be declared static (belong to the class instead of instances). The program starts at the main() method. No instance will be created.
A board game (such as Tic-tac-toe) is typically programmed as a state machine. Depending on the current-state and the player's move, the game goes into the next-state. In this example, I use a variable currentState to keep track of the current-state of the game, and define named-constants to denote the various
states of the game (PLAYING,
DRAW, CROSS_WON, and NOUGHT_WON).
A method called updateGame() is defined, which will be called after every move to update this currentState, by checking
the status of the game-board.
Two methods are defined for printing the game board, printBoard() and printCell(). The printBoard() shall call printCell() to
print each of the 9 cells.
This seems
trivial here, but will be useful in the object-oriented design to separate the
board and cells into separate classes.
[TODO] more explanation
Prompt the user whether to play again after gameover.
initGame();
System.out.print(&Play again (y/n)? &);
char ans = in.next().charAt(0);
if (ans != 'y' && ans != 'Y') {
System.out.println(&Bye!&);
System.exit(0);
} while (true);
A Console OO Tic-Tac-Toe
Let us convert the earlier non-OO version of Tic-Tac-Toe to object-oriented. The OO version of this simple Tic-Tac-Toe is more complex than the non-OO version, because Tic-Tac-Toe is a rather simple application. But OO design is a necessity to build a complex application.
Enumerations State and Seed
In our earlier version, we used int named-constants to represent the various game states, as follows:
public static final int PLAYING = 0;
public static final int DRAW = 1;
public static final int CROSS_WON = 2;
public static final int NOUGHT_WON = 3;
public static int currentState = PLAYING;
This approach of using int named-constants is better than using number in the programming statements, but it is not ideal. This is because you may inadvertently assign an int value outside the valid range to the variable currentState. For example,
currentState = 99;
JDK 1.5 introduces a new feature called enumeration, which is a special class for storing an enumeration (list) of items. In our case, we can define an enumeration called GameState as follows:
public enum GameState {
PLAYING, DRAW, CROSS_WON, NOUGHT_WON
To reference an item in an enum, use enumName.itemName (e.g., GameState.PLAYING and
GameState.DRAW), just like referencing static variables of a class (e.g., Math.PI).
You can create an instance for an enum (just like creating an instance of a class) and assign a value into it.
We shall now declare the variable currentState as an instance of GameState, which can take the value of GameState.PLAYING, GameState.DRAW, GameState.CROSS_WON, and GameState.NOUGHT_WON.
private GameState currentS
currentState = GameState.PLAYING;
Take note that you can only assign a value defined in the enumeration (such as GameState.PLAYING, GameState.DRAW), and NOT an arbitrary int value in the earlier example. Enum is SAFE!
We shall also create an enum called Seed for the various seeds and cell contents.
public enum Seed {
EMPTY, CROSS, NOUGHT
Again, you need to use Seed.EMPTY, Seed.CROSS, Seed.NOUGHT to refer to these values, just like any public static variable.
We shall declare the variables currentPlayer and content as instances of enum Seed.
private Seed currentP
currentPlayer = Seed.CROSS;
content = Seed.EMPTY;
In brief, an enum is just a special class with a list of named-constants. But enum is safe, compared with name-constants.
Classes Board and Cell
Next, let's design the OO classes needed for our Tic-Tac-Toe game.
Each class shall maintain its own attributes and operations (variables and methods), and it can paint itself in a graphics program.
We begin with two classes, a class Cell for each individual cell of the game board, and a class Board for the 3x3 game board.
The Cell class has an instance variable called content (with package access), of the type enum Seed. You can only assign a value from the enum's constants, such as Seed.EMPTY, Seed.CROSS, and Seed.NOUGHT, into content. A Cell can paint() itself and has its own operations such as clear().
The Board class composes of nine Cell instances, arranged in an 3&3 array called cells (with package access), of the type Cell[][].
A Board can paint() itself, and supports its own operations such as checking the status of the current board (isDraw(), hasWon()).
public class Cell {
public Cell(int row, int col) {
this.row =
this.col =
public void clear() {
content = Seed.EMPTY;
public void paint() {
switch (content) {
case CROSS:
System.out.print(& X &);
case NOUGHT: System.out.print(& O &);
case EMPTY:
System.out.print(&
Board.java
public class Board {
public static final int ROWS = 3;
public static final int COLS = 3;
int currentRow, currentC
public Board() {
cells = new Cell[ROWS][COLS];
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
cells[row][col] = new Cell(row, col);
public void init() {
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
cells[row][col].clear();
public boolean isDraw() {
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
if (cells[row][col].content == Seed.EMPTY) {
public boolean hasWon(Seed theSeed) {
return (cells[currentRow][0].content == theSeed
&& cells[currentRow][1].content == theSeed
&& cells[currentRow][2].content == theSeed
|| cells[0][currentCol].content == theSeed
&& cells[1][currentCol].content == theSeed
&& cells[2][currentCol].content == theSeed
|| currentRow == currentCol
&& cells[0][0].content == theSeed
&& cells[1][1].content == theSeed
&& cells[2][2].content == theSeed
|| currentRow + currentCol == 2
&& cells[0][2].content == theSeed
&& cells[1][1].content == theSeed
&& cells[2][0].content == theSeed);
public void paint() {
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
cells[row][col].paint();
if (col & COLS - 1) System.out.print(&|&);
System.out.println();
if (row & ROWS - 1) {
System.out.println(&-----------&);
Class GameMain
Finally, let's write a main class called GameMain to pull all the pieces together.
GameMain acts as the overall controller for the game.
GameMain.java
import java.util.S
public class GameMain {
private GameState currentS
private Seed currentP
private static Scanner in = new Scanner(System.in);
public GameMain() {
board = new Board();
initGame();
playerMove(currentPlayer);
board.paint();
updateGame(currentPlayer);
if (currentState == GameState.CROSS_WON) {
System.out.println(&'X' won! Bye!&);
} else if (currentState == GameState.NOUGHT_WON) {
System.out.println(&'O' won! Bye!&);
} else if (currentState == GameState.DRAW) {
System.out.println(&It's Draw! Bye!&);
currentPlayer = (currentPlayer == Seed.CROSS) ? Seed.NOUGHT : Seed.CROSS;
} while (currentState == GameState.PLAYING);
public void initGame() {
board.init();
currentPlayer = Seed.CROSS;
currentState = GameState.PLAYING;
public void playerMove(Seed theSeed) {
boolean validInput =
if (theSeed == Seed.CROSS) {
System.out.print(&Player 'X', enter your move (row[1-3] column[1-3]): &);
System.out.print(&Player 'O', enter your move (row[1-3] column[1-3]): &);
int row = in.nextInt() - 1;
int col = in.nextInt() - 1;
if (row &= 0 && row & Board.ROWS && col &= 0 && col & Board.COLS
&& board.cells[row][col].content == Seed.EMPTY) {
board.cells[row][col].content = theS
board.currentRow =
board.currentCol =
validInput =
System.out.println(&This move at (& + (row + 1) + &,& + (col + 1)
+ &) is not valid. Try again...&);
} while (!validInput);
public void updateGame(Seed theSeed) {
if (board.hasWon(theSeed)) {
currentState = (theSeed == Seed.CROSS) ? GameState.CROSS_WON : GameState.NOUGHT_WON;
} else if (board.isDraw()) {
currentState = GameState.DRAW;
public static void main(String[] args) {
new GameMain();
Take note that the OO-version and the non-OO version have the same codes, but are organized differently. The organization in OO enables you to design and develop complex system.
A Graphics Simple-OO Tic-Tac-Toe
Let's rewrite the &console& version into a &graphics& version - a Java Swing application, as illustrated. In this initial design, we do not separate the cell and board into dedicated classes, but include them in the main class. We used an inner class DrawCanvas (that extends JPanel) to do the custom drawing, and an anonymous inner class for MouseListener.
The content-pane (of the top-level container JFrame) is set to BorderLayout. The DrawCanvas (JPanel) is placed at the CENTER; while a status-bar (a JLabel) is placed at the SOUTH (PAGE_END).
The class diagram is as follows:
TTTGraphics2P.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
@SuppressWarnings(&serial&)
public class TTTGraphics2P extends JFrame {
public static final int ROWS = 3;
public static final int COLS = 3;
public static final int CELL_SIZE = 100;
public static final int CANVAS_WIDTH = CELL_SIZE * COLS;
public static final int CANVAS_HEIGHT = CELL_SIZE * ROWS;
public static final int GRID_WIDTH = 8;
public static final int GRID_WIDHT_HALF = GRID_WIDTH / 2;
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;
public enum GameState {
PLAYING, DRAW, CROSS_WON, NOUGHT_WON
private GameState currentS
public enum Seed {
EMPTY, CROSS, NOUGHT
private Seed currentP
private Seed[][]
private DrawC
private JLabel statusB
public TTTGraphics2P() {
canvas = new DrawCanvas();
canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
canvas.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
int rowSelected = mouseY / CELL_SIZE;
int colSelected = mouseX / CELL_SIZE;
if (currentState == GameState.PLAYING) {
if (rowSelected &= 0 && rowSelected & ROWS && colSelected &= 0
&& colSelected & COLS && board[rowSelected][colSelected] == Seed.EMPTY) {
board[rowSelected][colSelected] = currentP
updateGame(currentPlayer, rowSelected, colSelected);
currentPlayer = (currentPlayer == Seed.CROSS) ? Seed.NOUGHT : Seed.CROSS;
initGame();
repaint();
statusBar = new JLabel(&
statusBar.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD, 15));
statusBar.setBorder(BorderFactory.createEmptyBorder(2, 5, 4, 5));
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(canvas, BorderLayout.CENTER);
cp.add(statusBar, BorderLayout.PAGE_END);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(&Tic Tac Toe&);
setVisible(true);
board = new Seed[ROWS][COLS];
initGame();
public void initGame() {
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
board[row][col] = Seed.EMPTY;
currentState = GameState.PLAYING;
currentPlayer = Seed.CROSS;
public void updateGame(Seed theSeed, int rowSelected, int colSelected) {
if (hasWon(theSeed, rowSelected, colSelected)) {
currentState = (theSeed == Seed.CROSS) ? GameState.CROSS_WON : GameState.NOUGHT_WON;
} else if (isDraw()) {
currentState = GameState.DRAW;
public boolean isDraw() {
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
if (board[row][col] == Seed.EMPTY) {
public boolean hasWon(Seed theSeed, int rowSelected, int colSelected) {
return (board[rowSelected][0] == theSeed
&& board[rowSelected][1] == theSeed
&& board[rowSelected][2] == theSeed
|| board[0][colSelected] == theSeed
&& board[1][colSelected] == theSeed
&& board[2][colSelected] == theSeed
|| rowSelected == colSelected
&& board[0][0] == theSeed
&& board[1][1] == theSeed
&& board[2][2] == theSeed
|| rowSelected + colSelected == 2
&& board[0][2] == theSeed
&& board[1][1] == theSeed
&& board[2][0] == theSeed);
class DrawCanvas extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.WHITE);
g.setColor(Color.LIGHT_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);
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke(SYMBOL_STROKE_WIDTH, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND));
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
int x1 = col * CELL_SIZE + CELL_PADDING;
int y1 = row * CELL_SIZE + CELL_PADDING;
if (board[row][col] == Seed.CROSS) {
g2d.setColor(Color.RED);
int x2 = (col + 1) * CELL_SIZE - CELL_PADDING;
int y2 = (row + 1) * CELL_SIZE - CELL_PADDING;
g2d.drawLine(x1, y1, x2, y2);
g2d.drawLine(x2, y1, x1, y2);
} else if (board[row][col] == Seed.NOUGHT) {
g2d.setColor(Color.BLUE);
g2d.drawOval(x1, y1, SYMBOL_SIZE, SYMBOL_SIZE);
if (currentState == GameState.PLAYING) {
statusBar.setForeground(Color.BLACK);
if (currentPlayer == Seed.CROSS) {
statusBar.setText(&X's Turn&);
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 static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TTTGraphics2P();
Dissecting the Program
Game Programming Assignment
You can use the above Tic-tac-toe as a template to develop board games such as Connect-4 and Othello.
Connect-Four
Click on the image to run the demo (in applet).
Wiki &Connect-4& to understand the rules of the game.
To write a Connect-Four game, let's start from Tic-Tac-Toe's &&. Do the following changes on &TTTGraphics2P.java&:
Change constants ROWS to 6 and COLS to 7. Run the program. You shall see a 6&7 grid. Try clicking on the cells, &cross& and &nought& shall be displayed alternately.
Modify the mouseClicked() event-handler to position the seed at the &bottom& row of the column clicked, instead of on the the cell clicked. You need to check that there is empty cell on that column.
if (colSelected >= 0 && colSelected = 0; row--) {
if (board[row][colSelected] == Seed.EMPTY) {
board[row][colSelected] = currentP
updateGame(currentPlayer, row, colSelected);
currentPlayer = (currentPlayer == Seed.CROSS) ? Seed.NOUGHT : Seed.CROSS;
Modify the hasWon() method to check for 4-in-a-line (along row, column, diagonal or opposite-diagonal).
public boolean hasWon(Seed theSeed, int rowSelected, int colSelected) {
int count = 0;
for (int col = 0; col & COLS; ++col) {
if (board[rowSelected][col] == theSeed) {
if (count == 4)
count = 0;
That's all!
Tidy up the names (In Eclipse, Refactor &rA Rename).
Tidy up the display (using red and yellow discs, instead of cross and nought).
Add more features. For example, or buttons to control the game.
Re-design your classes (Read the &&).
Improve your display (e.g., using images, animation etc).
Othello (Reversi)
Click on the image to run my demo (in applet).
Wiki &Othello& or &Reversi& to understand the rules of the game.
Modify the above Tic-Tac-Toe (&TTTGraphics2P.java&):
Change ROWS and COLS to 8. Run the program. You shall see a 8&8 grid. Try clicking on the cells, &cross& and &nought& shall be displayed alternately.
Modify the updateGame(Seed theSeed, int rowSelected, int colSelect) to flip the opponent's seeds along the row, column, diagonal and opposite diagonal - centered at (rowSelected, colSelected) - after the player with &theSeed& has placed on (rowSelected, colSelected). If there is no more empty space, the game is over. Decide the winner by counting the numbers of black and white seeds.
public void updateGame(Seed mySeed, int rowSelected, int colSelected) {
Seed opponentSeed = (mySeed == Seed.BLACK) ? Seed.WHITE : Seed.BLACK;
col = colSelected + 1;
while (col & COLS - 1 && board[rowSelected][col] == opponentSeed) {
if (col &= COLS - 1 && board[rowSelected][col] == mySeed) {
for (int colFlip = colSelected + 1; colFlip &= col - 1; ++colFlip) {
board[rowSelected][colFlip] = myS
Remove isDraw() and hasWon().
Tidy up the names (Refactor &rA Rename).
Tidy up the display (using black and white discs, instead of cross and nought).
Add more features. For example, or buttons to control the game.
Re-design your classes (Read the &&).
Improve your display (e.g., using images, animation etc).
You could wiki &Sudoku& to understand the rules of the game.
Sudoku's graphics does not involve custom drawing (such as drawing lines or circles). Hence, the above Tic-Tac-Toe graphics example is not really applicable. You can simply use a 9x9 JTextFields arranged in a 9x9 GridLayout - the GUI codes is simple!
The steps for producing the display are:
Set the JFrame's content-pane to 9&9 GridLayout. Create 9&9 JTextFields and add to the content-pane. You need to set up two 9&9 arrays. One int[9][9] to store the numbers (1-9, or 0 if empty). Another JTextField[9][9] to do the display (print blank if the number is 0).
Initialize the game by reading in an input puzzle with blank cells, and populate the int[9][9] and JTextField[9][9] arrays. Set the non-empty cells to non-editable.
For example,
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SudokuDisplayOnly extends JFrame {
public static final int ROWS = 9;
public static final int COLS = 9;
public static final int CELL_SIZE = 60;
public static final int CANVAS_WIDTH = CELL_SIZE * COLS;
public static final int CANVAS_HEIGHT = CELL_SIZE * ROWS;
private int[][]
private JTextField[][] tfC
private int[][] puzzle =
{{5, 3, 4, 6, 7, 8, 9, 1, 2},
{6, 7, 2, 1, 9, 5, 3, 4, 8},
{1, 9, 8, 3, 4, 2, 5, 6, 7},
{8, 5, 9, 7, 6, 1, 4, 2, 3},
{4, 2, 6, 8, 5, 3, 7, 9, 1},
{7, 1, 3, 9, 2, 4, 8, 5, 6},
{9, 6, 1, 5, 3, 7, 2, 8, 4},
{2, 8, 7, 4, 1, 9, 6, 3, 5},
{3, 4, 5, 2, 8, 6, 1, 7, 9}};
private boolean[][] mask =
{{false, false, false, false, false, true, false, false, false},
{false, false, false, false, false, false, false, false, true},
{false, false, true, false, false, false, false, false, false},
{true, false, false, false, false, false, false, false, false},
{false, false, false, true, false, false, false, false, false},
{false, false, false, false, false, false, true, false, false},
{false, false, false, false, false, false, false, true, false},
{false, true, false, false, false, false, false, false, false},
{false, false, false, false, true, false, false, false, false}};
public SudokuDisplayOnly() {
Container cp = getContentPane();
cp.setLayout(new GridLayout(ROWS, COLS));
cells = new int[ROWS][COLS];
tfCells = new JTextField[ROWS][COLS];
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
tfCells[row][col] = new JTextField();
cp.add(tfCells[row][col]);
int number = puzzle[row][col];
if (mask[row][col]) {
cells[row][col] = 0;
tfCells[row][col].setText(&&);
tfCells[row][col].setEditable(true);
tfCells[row][col].setBackground(Color.YELLOW);
cells[row][col] =
tfCells[row][col].setText(number + &&);
tfCells[row][col].setEditable(false);
tfCells[row][col].setHorizontalAlignment(JTextField.CENTER);
tfCells[row][col].setFont(new Font(&Monospaced&, Font.BOLD, 20));
cp.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(&Sudoku&);
setVisible(true);
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SudokuDisplayOnly();
Some useful methods of JTextField are:
setBackground(Color c)
setForeground(Color c)
setFont(Font f)
setHorizontalAlignment(int align);
Next, write the event handler actionPerformed() for the ActionEvent fired by the JTextField. You may use one listener to listen to all the 9&9 JTextFields. In order to ascertain the JTextField that has fired the ActionEvent. You could use the event.getSource() method to retrieve the source object that has fired the event and compare with all the 9&9 JTextFields:
public void actionPerformed(ActionEvent evt) {
int rowSelected = -1;
int colSelected = -1;
JTextField source = (JTextField)e.getSource();
boolean found =
for (int row = 0; row & ROWS && ! ++row) {
for (int col = 0; col & COLS && ! ++col) {
if (tfCells[row][col] == source) {
rowSelected =
colSelected =
Validate the input (show invalid input in a different color or show it in &X&) and check for puzzle solved.
Refine your display. Use 3x3 JPanels in GridLayout. Each JPanel has 3x3 JTextFields in GridLayout too. In this way, you can control the border of the JPanels via setBorder().
Add more features (e.g., sound effect, buttons for controlling the game, undo).
Improve the game, e.g., difficulty level (easy, medium, hard), hints and cheats, etc.
Re-design your classes.
Improve display (e.g., images and animation).
Triggering JTextField's ActionEvent involves hitting the &enter& key. That is, without hitting the enter key, the number is not captured by actionPerformed(), although it may appear on the text field. Try using the KeyEvent, which is fired after every key stroke.
Mine Sweeper
Similar to Sudoku, the graphics for Mine Sweeper does not involve custom drawings. For the basic version with 10x10 cells, construct a 10x10 JButton
array and arranged in GridLayout. Study the sample code in Sudoku to create the display.
In Mine Sweeper, you need to response to left-mouse click and right-mouse-click differently. Hence, instead of listening to the ActionEvent, you shall listen to the MouseEvent with mouse-clicked handler so as to response to the left-click and right-click. Since ActionEvent is not used, you probably can use 10x10 JLabel instead of JButton, as JLabel can also trigger mouse-event.
MasterMind
A Graphics Advanced-OO Tic-Tac-Toe - Separating the Board and Cell Classes
A More Versatile Swing Program Template
Before I proceed, I have modified the Swing program template to make the codes more versatile (the same codes can be run as a standalone application, as well as Applet and Java Web-Start application).
The main class extends from JPanel instead of JFrame.
In the entry main() method, an instance of JFrame is constructed, and its content-pane is set to an instance of the main class. The JFrame is then set visible.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
@SuppressWarnings(&serial&)
public class SwingTemplateJPanel extends JPanel {
public static final int CANVAS_WIDTH = 640;
public static final int CANVAS_HEIGHT = 480;
public static final String TITLE = &...Title...&;
public SwingTemplateJPanel() {
setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.BLACK);
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame(TITLE);
frame.setContentPane(new SwingTemplateJPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Running as a Standalone Application
The above template has a main() method, which can be run as a standalone application, a web-start application, or from a JAR file.
Running as an Applet
An Swing Applet extends javax.swing.JApplet, intead of javax.swing.JFrame. An applet starts at init(), instead of main(). We can provide another class to run the main class as an applet as follows:
import javax.swing.*;
@SuppressWarnings(&serial&)
public class SwingTemplateJApplet extends JApplet {
public void init() {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
setContentPane(new SwingTemplateJPanel());
} catch (Exception e) {
e.printStackTrace();
You need to provide an HMTL file to run the applet in production. (For testing, You could run your applet directly under Eclipse/NetBeans using the so-called &appletviewer& without an HTML file.) For example,
&title&An .... Applet&/title&
&h1&Heading ...&/h1&
&applet code=&SwingTemplateJApplet.class& width=&640& height=&480& alt=&Error Loading Applet?!&&
Your browser does not seem to support &APPLET& tag!
Graphics with OO Design
In a good OO design, each class shall be encapsulated, shall have its own attributes and operations (variables and methods), and responsible for painting itself in a graphics program.
The class diagram is as follows:
Enumeration Seed.java
public enum Seed {
EMPTY, CROSS, NOUGHT
Enumeration State.java
public enum State {
PLAYING, DRAW, CROSS_WON, NOUGHT_WON
Class Cell.java
import java.awt.*;
public class Cell {
public Cell(int row, int col) {
this.row =
this.col =
public void clear() {
content = Seed.EMPTY;
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke(GameMain.SYMBOL_STROKE_WIDTH,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
int x1 = col * GameMain.CELL_SIZE + GameMain.CELL_PADDING;
int y1 = row * GameMain.CELL_SIZE + GameMain.CELL_PADDING;
if (content == Seed.CROSS) {
g2d.setColor(Color.RED);
int x2 = (col + 1) * GameMain.CELL_SIZE - GameMain.CELL_PADDING;
int y2 = (row + 1) * GameMain.CELL_SIZE - GameMain.CELL_PADDING;
g2d.drawLine(x1, y1, x2, y2);
g2d.drawLine(x2, y1, x1, y2);
} else if (content == Seed.NOUGHT) {
g2d.setColor(Color.BLUE);
g2d.drawOval(x1, y1, GameMain.SYMBOL_SIZE, GameMain.SYMBOL_SIZE);
Class Board.java
import java.awt.*;
public class Board {
public Board() {
cells = new Cell[GameMain.ROWS][GameMain.COLS];
for (int row = 0; row & GameMain.ROWS; ++row) {
for (int col = 0; col & GameMain.COLS; ++col) {
cells[row][col] = new Cell(row, col);
public void init() {
for (int row = 0; row & GameMain.ROWS; ++row) {
for (int col = 0; col & GameMain.COLS; ++col) {
cells[row][col].clear();
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) {
public boolean hasWon(Seed seed, int seedRow, int seedCol) {
return (cells[seedRow][0].content == seed
&& cells[seedRow][1].content == seed
&& cells[seedRow][2].content == seed
|| cells[0][seedCol].content == seed
&& cells[1][seedCol].content == seed
&& cells[2][seedCol].content == seed
|| seedRow == seedCol
&& cells[0][0].content == seed
&& cells[1][1].content == seed
&& cells[2][2].content == seed
|| seedRow + seedCol == 2
&& cells[0][2].content == seed
&& cells[1][1].content == seed
&& cells[2][0].content == seed);
public void paint(Graphics g) {
g.setColor(Color.GRAY);
for (int row = 1; row & GameMain.ROWS; ++row) {
g.fillRoundRect(0, GameMain.CELL_SIZE * row - GameMain.GRID_WIDHT_HALF,
GameMain.CANVAS_WIDTH - 1, GameMain.GRID_WIDTH,
GameMain.GRID_WIDTH, GameMain.GRID_WIDTH);
for (int col = 1; col & GameMain.COLS; ++col) {
g.fillRoundRect(GameMain.CELL_SIZE * col - GameMain.GRID_WIDHT_HALF, 0,
GameMain.GRID_WIDTH, GameMain.CANVAS_HEIGHT - 1,
GameMain.GRID_WIDTH, GameMain.GRID_WIDTH);
for (int row = 0; row & GameMain.ROWS; ++row) {
for (int col = 0; col & GameMain.COLS; ++col) {
cells[row][col].paint(g);
Classes GameMain.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
@SuppressWarnings(&serial&)
public class GameMain extends JPanel {
public static final int ROWS = 3;
public static final int COLS = 3;
public static final String TITLE = &Tic Tac Toe&;
public static final int CELL_SIZE = 100;
public static final int CANVAS_WIDTH = CELL_SIZE * COLS;
public static final int CANVAS_HEIGHT = CELL_SIZE * ROWS;
public static final int GRID_WIDTH = 8;
public static final int GRID_WIDHT_HALF = GRID_WIDTH / 2;
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;
private GameState currentS
private Seed currentP
private JLabel statusB
public GameMain() {
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
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 = currentP
updateGame(currentPlayer, rowSelected, colSelected);
currentPlayer = (currentPlayer == Seed.CROSS) ? Seed.NOUGHT : Seed.CROSS;
initGame();
repaint();
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);
setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT + 30));
board = new Board();
initGame();
public void initGame() {
for (int row = 0; row & ROWS; ++row) {
for (int col = 0; col & COLS; ++col) {
board.cells[row][col].content = Seed.EMPTY;
currentState = GameState.PLAYING;
currentPlayer = Seed.CROSS;
public void updateGame(Seed theSeed, int row, int col) {
if (board.hasWon(theSeed, row, col)) {
currentState = (theSeed == Seed.CROSS) ? GameState.CROSS_WON : GameState.NOUGHT_WON;
} else if (board.isDraw()) {
currentState = GameState.DRAW;
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.WHITE);
board.paint(g);
if (currentState == GameState.PLAYING) {
statusBar.setForeground(Color.BLACK);
if (currentPlayer == Seed.CROSS) {
statusBar.setText(&X's Turn&);
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 static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame(TITLE);
frame.setContentPane(new GameMain());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Running as a Standalone Program
Simply run the class containing the entry main() method.
Deploying an Application via a JAR file
To deploy an application containing many classes, you have to pack (i.e., jar) all classes and resources into a single file, with a manifest that specifies the main class (containing the entry main() method).
For example:
via the Eclipse's &Export& option: Right-click on the project &rA Export &rA Java &rA JAR file &rA Next &rA Specify the JAR filename &rA Next &rA Next &rA Select &Generate the manifest file& &rA Browse to select the main class &GameMain& &rA Finish.
via the &jar& command.
First, create a manifest file called &tictactoe.mf&, as follow:
Manifest-Version: 1.0
Main-Class: GameMain
Next, issue a &jar& command (form CMD shell) where options 'c' for create, 'm' for manifest, 'f' for output jar filename, and 'v' for verbose:
& jar cmfv tictactoe.mf tictactoe.jar *.class
You can run the program from a JAR file directly (without unpacking the JAR file) by:
In Windows' Explorer, right-click on the JAR file &rA Open with &rA Java Platform SE B or
From the CMD shell, run java.exe with -jar option, i.e., &java -jar JarFileName.jar&.
Note: JAR file uses the ZIP algorithm. In other words, you could use WinZIP/WinRAR to open and extract the contents of a JAR file.
Running as an Applet
Click the image to run the demo applet:
AppletMain.java
Provide a main class (says AppletMain.java) for the applet that extends javax.swing.JApplet:
import javax.swing.*;
@SuppressWarnings(&serial&)
public class AppletMain extends JApplet {
public void init() {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
setContentPane(new GameMain());
} catch (Exception e) {
e.printStackTrace();
TicTacToe.html
Provide an HMTL file (says &TicTacToe.html&) that embeds the &AppletMain.class&:
&title&Tic Tac Toe&/title&
&h1&Tic Tac Toe&/h1&
&applet code=&AppletMain.class& width=&300& height=&330& alt=&Error Loading Applet?!&&
Your browser does not seem to support &APPLET& tag!
tictactoe.jar
To deploy an applet which contains more than one classes, you need to pack all the classes and resources into a JAR file (e.g., via Eclipse's &Export& option or &jar& command described earlier), but you need not use a manifest (for specify a main class as applet does not need a main() method). Then, use the following &applet& tag with an &archive& attribute to specify the JAR filename:
&applet code=&AppletMain.class&
archive=&JarFileName.jar&
width=&300& height=&300&
alt=&Error Loading Applet?!& &
Your browser does not seem to support &APPLET& tag!
Adding Sound Effect
How to Play an Audio File
SoundTest.java
import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import javax.sound.sampled.*;
import javax.swing.*;
@SuppressWarnings(&serial&)
public class SoundTest extends JFrame {
private String fileGameOver = &gameover.wav&;
private Clip soundClipGameO
public SoundTest() {
URL url = this.getClass().getClassLoader().getResource(fileGameOver);
if (url == null) {
System.err.println(&Couldn't find file: & + fileGameOver);
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
soundClipGameOver = AudioSystem.getClip();
soundClipGameOver.open(audioIn);
} catch (UnsupportedAudioFileException e) {
System.err.println(&Audio Format not supported: & + fileGameOver);
} catch (Exception e) {
e.printStackTrace();
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
JButton btn = new JButton(&Play Sound&);
cp.add(btn);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (soundClipGameOver.isRunning()) soundClipGameOver.stop();
soundClipGameOver.setFramePosition(0);
soundClipGameOver.start();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(&Test Sound&);
setSize(200, 100);
setVisible(true);
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SoundTest();
Prepare a sound file called &gameover.wav& (try googling) and place it under under your project root (or &bin&) directory.
If you receive error message &Couldn't find file: xxx&. Try moving your file around your project (such as under the &bin& sub-directory).
JDK supports only a sampled audio format, &.wav&, &.au&, and &.aiff&. It does not support &.mp3& (You will get an error message &Audio Format not supported: xxx&. There used to be a &Java Media Framework (JMF)& that supports MP3.
You may try to download the pronunciations for the words &game& and &over&, and join them into a &wav& file.
We need to test the sound effect under a Swing application, instead of placing all the codes under the main(). This is because main() exits before the sound gets a chance to play.
Adding Sound Effect to the Tic-Tac-Toe Graphics Version
Two sound clips were used in the demo: one for the move (&move.wav&) and the other for game-over (&gameover.wav&). (Google to find some interesting &wav& files.)
Include the following codes at the appropriate locations:
import java.io.IOE
import java.net.URL;
import javax.sound.sampled.*;
String fileMove = &sounds/move.wav&;
String fileGameOver = &sounds/gameover.wav&;
Clip soundClipM
Clip soundClipGameO
URL url = this.getClass().getClassLoader().getResource(fileGameOver);
if (url == null) {
System.err.println(&Couldn't find file: & + fileGameOver);
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
soundClipGameOver = AudioSystem.getClip();
soundClipGameOver.open(audioIn);
url = this.getClass().getClassLoader().getResource(fileMove);
if (url == null) {
System.err.println(&Couldn't find file: & + fileMove);
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
soundClipMove = AudioSystem.getClip();
soundClipMove.open(audioIn);
}} catch (UnsupportedAudioFileException e) {
System.err.println(&Audio Format not supported!&);} catch (Exception e) {
e.printStackTrace();}
if (currentState == GameState.PLAYING) {
if (soundClipMove.isRunning()) soundClipMove.stop();
soundClipMove.setFramePosition(0);
soundClipMove.start();
if (soundClipGameOver.isRunning()) soundClipGameOver.stop();
soundClipGameOver.setFramePosition(0);
soundClipGameOver.start();
You may consider using different sound files for &win&, &loss&, &draw&, &valid_move& and &invalid_move&.
Using Images
Read && of &Custom Graphics&.
Read && of &Custom Graphics&.
Fast Matching of Winning Patterns with Bit-Masks (Advanced)
Arthur van Hoff's Tic Tac Toe Applet Demo (under the JDK demo &applets& folder).
A much more efficient method for matching with a winning pattern in a Tic-tac-toe is to use a 9-bit binary number (stored as an int or short type) to denote the placement of the seeds, and use bit operations to perform the matching.
The following table summaries all the bit-wise operations, which are efficient and fast.
Description
Bit-wise AND
expr1 & expr2
0b & Ob gives 0b
Bit-wise OR
expr1 | expr2
0b | Ob gives 0b
Bit-wise NOT
^0b gives 0b
Bit-wise XOR
expr1 ^ expr2
0b ^ Ob gives 0b
Left-shift and padded with zeros
operand && number
0b && 4 gives 0b
Right-shift and padded with the &sign-bit&
(Signed-extended right-shift)
operand && number
0b && 2 gives 0b
Right-shift and padded with zeros
(Unsigned-extended right-shift)
operand &&& number
0b &&& 2 gives 0b
We can keep the 8 winning patterns in an int array as follows:
int[] winningPatterns = {
Note: JDK 1.7 supports binary literals beginning with prefix &0b&. Pre-JDK 1.7 does not support binary literals but supports hexadecimal literals beginning with prefix &0x&. Eclipse IDE supports JDK 1.7 only after Eclipse 3.7.2. Hence, try 0b... but fall back to 0x... if compilation fails.
We define two placement binary patterns for the cross and nought respectively.
int crossP
int noughtP
int bitPosition = rowSelected * ROWS + colS
if (currentPlayer == Seed.CROSS) {
crossPattern = crossPattern | (0x1 && bitPosition);
noughtPattern = noughtPattern | (0x1 && bitPosition);
(0x1 && bitPosition) shifts a binary 0b 000 000 001 to the left by the bitPosition number of bits, so as to place a '1' bit at the proper position. It is then bit-OR with the existing pattern to include the new bit, without modifying the existing bits. For example, suppose rowSelect = 2 and colSelected = 0, then bitPosition = 6. (0x1 && bitPosition) gives 0b 001 000 000.
To match with the winning patterns:
public boolean hasWon(Seed theSeed) {
int playerPattern = (theSeed == Seed.CROSS) ? crossPattern : noughtP
for (int aWinningPattern : winningPatterns) {
if ((aWinningPattern & playerPattern) == aWinningPattern) {
(aWinningPattern & playerPattern) masks out all the bits in the playerPattern except those having 1's in aWinningPattern. For example, suppose that playerPattern = 0b111 000 101, it matches the aWinningPattern = 0b111 000 000. This is because (playerPattern & aWinningPattern) returns 0b111 000 000, which is the same the the aWinningPattern.
This code is very much more efficient as it involves only comparison with 8 integers (plus 8 efficient bit-AND operations).
Other Modes of Operation
WebStart Application
Playing Over the Net
Playing Against Computer with AI (Advanced)
REFERENCES & RESOURCES
JDK Applets demo &TicTacToe& (under JDK demo applets folder).

我要回帖

更多关于 swing for the fences 的文章

 

随机推荐