Ask User to Play Again Java
Java Graphics Programming
Case Written report on Tic-Tac-Toe & Assignment
Click on the Image to run the Applet Demo (tested on FireFox/Windows but)
Let's Start with a 2-Player Console Non-OO Tic-Tac-Toe
Allow us first with a two-player console (non-graphics) version of Tic-Tac-Toe, where histrion 'X'
and player 'O'
enter their moves successively, equally shown below:
Role player 'X', enter your motility (row[1-3] column[1-three]): 2 2 | | ----------- | 10 | ----------- | | Histrion 'O', enter your move (row[1-3] column[ane-3]): 1 ane O | | ----------- | X | ----------- | | Player 'Ten', enter your motility (row[1-3] column[1-three]): 1 three O | | X ----------- | 10 | ----------- | | Actor 'O', enter your move (row[ane-3] cavalcade[1-3]): 3 1 O | | Ten ----------- | X | ----------- O | | Player 'X', enter your move (row[1-3] cavalcade[1-iii]): two 2 This move at (2,2) is not valid. Endeavor over again... Thespian 'X', enter your motion (row[one-iii] column[ane-3]): 2 3 O | | X ----------- | X | X ----------- O | | Histrion 'O', enter your move (row[1-3] column[1-3]): two one O | | X ----------- O | X | 10 ----------- O | | Role player 'O' won!
TTTCosnoleNonOO2P.java
ane ii 3 4 five 6 7 8 9 10 xi 12 13 14 xv 16 17 18 19 20 21 22 23 24 25 26 27 28 29 thirty 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 seventy 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | import coffee.util.Scanner; public class TTTConsoleNonOO2P { public static final int EMPTY = 0; public static terminal int Cross = 1; public static terminal int NOUGHT = 2; public static final int PLAYING = 0; public static final int DRAW = one; public static last int CROSS_WON = 2; public static final int NOUGHT_WON = 3; public static concluding int ROWS = 3, COLS = 3; public static int[][] board = new int[ROWS][COLS]; public static int currentState; public static int currentPlayer; public static int currntRow, currentCol; public static Scanner in = new Scanner(System.in); public static void master(String[] args) { initGame(); practice { playerMove(currentPlayer); updateGame(currentPlayer, currntRow, currentCol); printBoard(); if (currentState == CROSS_WON) { Organisation.out.println("'X' won! Bye!"); } else if (currentState == NOUGHT_WON) { System.out.println("'O' won! Bye!"); } else if (currentState == DRAW) { Organization.out.println("It's a Draw! Bye!"); } currentPlayer = (currentPlayer == CROSS) ? NOUGHT : Cantankerous; } 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 = false; practice { if (theSeed == Cantankerous) { System.out.print("Role player 'X', enter your motility (row[1-3] column[1-3]): "); } else { System.out.impress("Histrion 'O', enter your move (row[1-3] cavalcade[1-3]): "); } int row = in.nextInt() - one; int col = in.nextInt() - i; if (row >= 0 && row < ROWS && col >= 0 && col < COLS && board[row][col] == EMPTY) { currntRow = row; currentCol = col; board[currntRow][currentCol] = theSeed; validInput = true; } else { System.out.println("This move at (" + (row + one) + "," + (col + 1) + ") is not valid. Effort 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 (lath[row][col] == EMPTY) { return false; } } } return truthful; } public static boolean hasWon(int theSeed, int currentRow, int currentCol) { render (lath[currentRow][0] == theSeed && lath[currentRow][1] == theSeed && board[currentRow][two] == theSeed || board[0][currentCol] == theSeed && board[1][currentCol] == theSeed && lath[2][currentCol] == theSeed || currentRow == currentCol && lath[0][0] == theSeed && board[1][1] == theSeed && board[two][2] == theSeed || currentRow + currentCol == two && board[0][2] == theSeed && board[1][ane] == theSeed && lath[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 - i) { System.out.impress("|"); } } Organisation.out.println(); if (row != ROWS - 1) { Organisation.out.println("-----------"); } } System.out.println(); } public static void printCell(int content) { switch (content) { instance EMPTY: System.out.impress(" "); break; case NOUGHT: Organization.out.impress(" O "); break; case Cross: System.out.print(" X "); suspension; } } } |
Dissecting the Program
Non-OO programs (like C programs) are organized in methods, which admission 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 every bit a state machine. Depending on the current-state and the player's motility, the game goes into the next-state. In this example, I apply a variable currentState
to continue runway 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 divers, which will be chosen afterward every motion 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 telephone call printCell()
to print each of the 9 cells. This seems fiddling hither, but will be useful in the object-oriented design to split the board and cells into split classes.
[TODO] more explanation
Endeavor: Prompt the user whether to play again later gameover.
do { initGame(); ...... ...... System.out.print("Play again (y/northward)? "); char ans = in.adjacent().charAt(0); if (ans != 'y' && ans != 'Y') { Organisation.out.println("Bye!"); Arrangement.go out(0); } } while (truthful);
A Console OO Tic-Tac-Toe
Let united states catechumen the before non-OO version of Tic-Tac-Toe to object-oriented. The OO version of this unproblematic Tic-Tac-Toe is more complex than the non-OO version, considering Tic-Tac-Toe is a rather unproblematic application. Simply OO pattern is a necessity to build a complex awarding.
Enumerations State and Seed
In our before version, we used int
named-constants to represent the diverse game states, every bit follows:
public static terminal int PLAYING = 0; public static last int Depict = 1; public static final int CROSS_WON = 2; public static terminal 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 form for storing an enumeration (listing) of items. In our case, we can define an enumeration called GameState
as follows:
1 2 3 4 5 half dozen | public enum GameState { PLAYING, Depict, CROSS_WON, NOUGHT_WON } |
To reference an item in an enum
, use enumName.itemName
(e.m., 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 similar creating an instance of a course
) and assign a value into information technology. We shall now declare the variable currentState
as an case of GameState
, which can take the value of GameState.PLAYING
, GameState.DRAW
, GameState.CROSS_WON
, and GameState.NOUGHT_WON
.
private GameState currentState; currentState = GameState.PLAYING;
Take annotation that yous tin can only assign a value defined in the enumeration (such as GameState.PLAYING
, GameState.DRAW
), and Not an arbitrary int
value in the before example. Enum is SAFE!
We shall besides create an enum
called Seed
for the various seeds and cell contents.
one ii 3 4 v vi | 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 currentPlayer; currentPlayer = Seed.CROSS; private Seed content; content = Seed.EMPTY;
In cursory, an enum
is simply a special class with a list of named-constants. Just enum is safe, compared with proper noun-constants.
Classes Board and Cell
Next, let'south design the OO classes needed for our Tic-Tac-Toe game. Each class shall maintain its ain attributes and operations (variables and methods), and it tin pigment itself in a graphics program.
We brainstorm with 2 classes, a class Prison cell
for each private prison cell of the game lath, and a class Lath
for the 3x3 game board.
The Jail cell
class has an case variable called content
(with bundle access), of the blazon 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 Jail cell
tin paint()
itself and has its own operations such as clear()
.
The Board
grade composes of 9 Cell
instances, arranged in an 3×iii assortment called cells
(with parcel admission), of the type Cell[][]
. A Board
tin can paint()
itself, and supports its own operations such equally checking the status of the current board (isDraw()
, hasWon()
).
Cell.java
ane ii 3 iv 5 6 7 8 ix x 11 12 thirteen 14 fifteen 16 17 18 xix 20 21 22 23 24 25 26 27 28 29 30 | public class Cell { Seed content; int row, col; public Prison cell(int row, int col) { this.row = row; this.col = col; clear(); } public void articulate() { content = Seed.EMPTY; } public void paint() { switch (content) { case CROSS: Organisation.out.impress(" X "); break; example NOUGHT: System.out.print(" O "); break; example EMPTY: System.out.impress(" "); pause; } } } |
Board.java
1 2 3 4 5 6 7 8 9 x eleven 12 13 xiv 15 sixteen 17 18 nineteen xx 21 22 23 24 25 26 27 28 29 thirty 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 fifty 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 lxx 71 72 73 74 75 76 | public class Board { public static final int ROWS = iii; public static last int COLS = 3; Cell[][] cells; int currentRow, currentCol; public Board() { cells = new Jail 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) { return false; } } } return true; } 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[two][currentCol].content == theSeed || currentRow == currentCol && cells[0][0].content == theSeed && cells[i][i].content == theSeed && cells[2][2].content == theSeed || currentRow + currentCol == 2 && cells[0][2].content == theSeed && cells[1][ane].content == theSeed && cells[ii][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, allow'southward write a principal grade called GameMain
to pull all the pieces together. GameMain
acts every bit the overall controller for the game.
GameMain.coffee
1 2 3 four 5 6 7 8 nine 10 11 12 xiii 14 fifteen 16 17 xviii xix 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 xl 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 lxx 71 72 73 74 75 76 77 78 79 80 81 82 83 | import coffee.util.Scanner; public form GameMain { private Board lath; private GameState currentState; private Seed currentPlayer; private static Scanner in = new Scanner(Organisation.in); public GameMain() { board = new Lath(); initGame(); practise { playerMove(currentPlayer); board.paint(); updateGame(currentPlayer); if (currentState == GameState.CROSS_WON) { System.out.println("'Ten' 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 = false; do { if (theSeed == Seed.Cross) { Organisation.out.impress("Player 'X', enter your move (row[1-3] column[1-3]): "); } else { Arrangement.out.print("Player 'O', enter your move (row[1-3] cavalcade[ane-three]): "); } int row = in.nextInt() - 1; int col = in.nextInt() - 1; if (row >= 0 && row < Board.ROWS && col >= 0 && col < Board.COLS && lath.cells[row][col].content == Seed.EMPTY) { lath.cells[row][col].content = theSeed; lath.currentRow = row; board.currentCol = col; validInput = true; } else { Organization.out.println("This move at (" + (row + 1) + "," + (col + one) + ") 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 aforementioned codes, just are organized differently. The organization in OO enables you lot to design and develop complex system.
A Graphics Simple-OO Tic-Tac-Toe
Permit'south rewrite the "console" version into a "graphics" version - a Java Swing application, as illustrated. In this initial blueprint, nosotros do non separate the prison cell and board into dedicated classes, but include them in the master class. Nosotros 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 condition-bar (a JLabel
) is placed at the Southward
(PAGE_END
).
The form diagram is as follows:
TTTGraphics2P.java
1 2 three 4 v half-dozen seven 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 l 51 52 53 54 55 56 57 58 59 lx 61 62 63 64 65 66 67 68 69 lxx 71 72 73 74 75 76 77 78 79 lxxx 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | import java.awt.*; import java.awt.event.*; import javax.swing.*; @SuppressWarnings("series") public class TTTGraphics2P extends JFrame { public static final int ROWS = iii; public static final int COLS = 3; public static last int CELL_SIZE = 100; public static last int CANVAS_WIDTH = CELL_SIZE * COLS; public static concluding int CANVAS_HEIGHT = CELL_SIZE * ROWS; public static concluding int GRID_WIDTH = 8; public static final int GRID_WIDHT_HALF = GRID_WIDTH / two; 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 } individual GameState currentState; public enum Seed { EMPTY, Cross, NOUGHT } private Seed currentPlayer; private Seed[][] board ; individual DrawCanvas sail; private JLabel statusBar; public TTTGraphics2P() { canvas = new DrawCanvas(); sail.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT)); canvas.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent eastward) { int mouseX = eastward.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 && lath[rowSelected][colSelected] == Seed.EMPTY) { board[rowSelected][colSelected] = currentPlayer; updateGame(currentPlayer, rowSelected, colSelected); currentPlayer = (currentPlayer == Seed.Cross) ? Seed.NOUGHT : Seed.CROSS; } } else { initGame(); } repaint(); } }); statusBar = new JLabel(" "); statusBar.setFont(new Font(Font.DIALOG_INPUT, Font.Assuming, 15)); statusBar.setBorder(BorderFactory.createEmptyBorder(2, v, 4, 5)); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); cp.add(canvas, BorderLayout.Centre); cp.add(statusBar, BorderLayout.PAGE_END); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); 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) { lath[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) { return false; } } } return true; } public boolean hasWon(Seed theSeed, int rowSelected, int colSelected) { return (board[rowSelected][0] == theSeed && board[rowSelected][1] == theSeed && board[rowSelected][two] == theSeed || board[0][colSelected] == theSeed && board[1][colSelected] == theSeed && lath[two][colSelected] == theSeed || rowSelected == colSelected && board[0][0] == theSeed && board[1][1] == theSeed && board[2][2] == theSeed || rowSelected + colSelected == 2 && board[0][ii] == theSeed && board[1][1] == theSeed && board[2][0] == theSeed); } class DrawCanvas extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); setBackground(Color.WHITE); one thousand.setColor(Color.LIGHT_GRAY); for (int row = ane; row < ROWS; ++row) { thou.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.Cantankerous) { g2d.setColor(Color.Cerise); 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 (lath[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'due south Turn"); } else { statusBar.setText("O's Plow"); } } else if (currentState == GameState.DRAW) { statusBar.setForeground(Color.Red); statusBar.setText("It's a Draw! Click to play once more."); } else if (currentState == GameState.CROSS_WON) { statusBar.setForeground(Color.Cherry); statusBar.setText("'Ten' 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() { @Override public void run() { new TTTGraphics2P(); } }); } } |
Dissecting the Plan
[TODO]
Game Programming Assignment
Yous can use the above Tic-tac-toe as a template to develop board games such as Connect-4 and Othello.
Connect-4
Click on the prototype to run the demo (in applet).
Wiki "Connect-4" to understand the rules of the game.
To write a Connect-Four game, let's kickoff from Tic-Tac-Toe's "Graphics Version". Do the following changes on "TTTGraphics2P.java
":
- Change constants
ROWS
to 6 andCOLS
to 7. Run the program. Y'all shall see a six×7 filigree. Try clicking on the cells, "cross" and "nought" shall exist displayed alternately. - Change the
mouseClicked()
result-handler to position the seed at the "bottom" row of the column clicked, instead of on the the cell clicked. Yous demand to check that there is empty cell on that column.if (colSelected >= 0 && colSelected < COLS) { for (int row = ROWS -1; row >= 0; row--) { if (board[row][colSelected] == Seed.EMPTY) { lath[row][colSelected] = currentPlayer; updateGame(currentPlayer, row, colSelected); currentPlayer = (currentPlayer == Seed.Cantankerous) ? Seed.NOUGHT : Seed.CROSS; break; } } }
- Alter 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) { ++count; if (count == 4) return truthful; } else { count = 0; } } ...... return faux; }
That's all!
Side by side,
- Tidy up the names (In Eclipse, Refactor ⇒ Rename).
- Tidy up the display (using red and yellow discs, instead of cross and nought).
- Add more features. For instance, audio outcome; or buttons to control the game.
- Re-design your classes (Read the "Graphics Advanced-OO Tic-Tac-Toe").
- Improve your display (due east.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 higher up Tic-Tac-Toe ("TTTGraphics2P.java
"):
- Change
ROWS
andCOLS
to eight. Run the programme. You shall see a 8×eight filigree. Try clicking on the cells, "cantankerous" 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)
- afterward the player with "theSeed
" has placed on(rowSelected, colSelected)
. If in that location is no more empty space, the game is over. Decide the winner by counting the numbers of blackness and white seeds.
HINTS:public void updateGame(Seed mySeed, int rowSelected, int colSelected) { Seed opponentSeed = (mySeed == Seed.Blackness) ? Seed.WHITE : Seed.Black; int col, row; col = colSelected + i;
while (col < COLS - 1 && board[rowSelected][col] == opponentSeed) { ++col; } if (col <= COLS - 1 && lath[rowSelected][col] == mySeed) { for (int colFlip = colSelected + 1; colFlip <= col - ane; ++colFlip) { board[rowSelected][colFlip] = mySeed; } } ...... ...... - Remove
isDraw()
andhasWon()
.
Side by side,
- Tidy upwards the names (Refactor ⇒ Rename).
- Tidy upwardly the display (using black and white discs, instead of cross and nought).
- Add more features. For instance, audio effect; or buttons to command the game.
- Re-design your classes (Read the "Graphics Advanced-OO Tic-Tac-Toe").
- Improve your brandish (e.chiliad., using images, animation etc).
Sudoku
Y'all could wiki "Sudoku" to sympathize the rules of the game.
Sudoku's graphics does not involve custom drawing (such equally drawing lines or circles). Hence, the to a higher place Tic-Tac-Toe graphics instance is non really applicative. You lot tin 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×9GridLayout
. Create 9×9JTextField
s and add together to the content-pane. You need to set up 2 ix×ix arrays. Aneint[ix][9]
to shop the numbers (i-9, or 0 if empty). Some otherJTextField[9][9]
to practice the brandish (impress blank if the number is 0). - Initialize the game by reading in an input puzzle with blank cells, and populate the
int[ix][9]
andJTextField[9][9]
arrays. Fix the non-empty cells to non-editable.
For example,
one 2 3 4 5 6 seven 8 9 10 xi 12 13 14 fifteen 16 17 xviii 19 20 21 22 23 24 25 26 27 28 29 xxx 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 sixty 61 62 63 64 65 66 67 68 69 lxx 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | import coffee.awt.*; import coffee.awt.event.*; import javax.swing.*; public course SudokuDisplayOnly extends JFrame { public static final int ROWS = 9; public static final int COLS = 9; public static terminal int CELL_SIZE = threescore; public static terminal int CANVAS_WIDTH = CELL_SIZE * COLS; public static final int CANVAS_HEIGHT = CELL_SIZE * ROWS; individual int[][] cells; individual JTextField[][] tfCells; individual int[][] puzzle = {{five, 3, iv, six, 7, eight, 9, 1, 2}, {6, vii, two, i, 9, 5, 3, 4, 8}, {1, 9, 8, iii, 4, two, v, 6, 7}, {8, five, 9, 7, 6, i, 4, ii, three}, {4, ii, 6, 8, 5, three, 7, ix, i}, {7, 1, iii, 9, 2, 4, 8, five, 6}, {9, 6, 1, v, 3, seven, 2, 8, 4}, {2, eight, 7, four, i, ix, 6, 3, 5}, {3, 4, five, 2, 8, 6, 1, seven, 9}}; private boolean[][] mask = {{false, false, false, false, false, true, false, fake, false}, {false, false, false, fake, false, false, false, fake, truthful}, {fake, false, truthful, imitation, false, fake, false, false, false}, {truthful, imitation, simulated, false, false, false, false, false, fake}, {false, false, false, true, false, false, false, faux, false}, {false, false, false, false, simulated, fake, truthful, false, fake}, {false, false, false, false, false, false, false, true, false}, {simulated, truthful, false, false, false, imitation, false, false, false}, {faux, imitation, 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.Xanthous); } else { cells[row][col] = number; 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); pack(); setTitle("Sudoku"); setVisible(true); } public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { @Override 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 apply ane listener to listen to all the 9×nine JTextField
due south. In order to ascertain the JTextField
that has fired the ActionEvent
. You could utilize the upshot.getSource()
method to retrieve the source object that has fired the event and compare with all the 9×9 JTextField
s:
@Override public void actionPerformed(ActionEvent evt) { int rowSelected = -i; int colSelected = -1; JTextField source = (JTextField)e.getSource(); boolean constitute = false; for (int row = 0; row < ROWS && !found; ++row) { for (int col = 0; col < COLS && !constitute; ++col) { if (tfCells[row][col] == source) { rowSelected = row; colSelected = col; found = true; } } } ......
More:
- Validate the input (prove invalid input in a different color or show it in "X") and bank check for puzzle solved.
- Refine your brandish. Apply 3x3
JPanel
s inGridLayout
. EachJPanel
has 3x3JTextField
due south inGridLayout
likewise. In this mode, yous can command the border of theJPanel
south viasetBorder()
. - Add more than features (east.one thousand., sound issue, buttons for controlling the game, disengage).
- Improve the game, eastward.g., difficulty level (easy, medium, difficult), hints and cheats, etc.
- Re-design your classes.
- Amend brandish (eastward.one thousand., images and blitheness).
Triggering JTextField
's ActionEvent
involves hitting the "enter" cardinal. That is, without striking the enter key, the number is not captured by actionPerformed()
, although information technology may appear on the text field. Endeavour using the KeyEvent
, which is fired after every cardinal stroke.
Mine Sweeper
Similar to Sudoku, the graphics for Mine Sweeper does non involve custom drawings. For the bones version with 10x10 cells, construct a 10x10 JButton
array and arranged in GridLayout
. Study the sample code in Sudoku to create the brandish.
In Mine Sweeper, you demand to response to left-mouse click and right-mouse-click differently. Hence, instead of listening to the ActionEvent
, yous shall listen to the MouseEvent
with mouse-clicked handler then equally to response to the left-click and correct-click. Since ActionEvent
is not used, you probably can use 10x10 JLabel
instead of JButton
, every bit JLabel
tin can also trigger mouse-consequence.
MasterMind
[TODO]
Checker
[TODO]
A Graphics Avant-garde-OO Tic-Tac-Toe - Separating the Board and Jail cell Classes
A More Versatile Swing Program Template
Before I proceed, I take modified the Swing plan template to make the codes more versatile (the same codes can be run as a standalone application, every bit well as Applet and Coffee Web-Start application).
- The principal class extends from
JPanel
instead ofJFrame
. - In the entry
master()
method, an instance ofJFrame
is constructed, and its content-pane is set to an instance of the main form. TheJFrame
is then prepare visible.
one 2 3 4 v 6 7 8 9 ten 11 12 13 14 15 16 17 18 nineteen 20 21 22 23 24 25 26 27 28 29 thirty 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 l 51 52 53 54 55 56 57 58 | import coffee.awt.*; import coffee.awt.event.*; import javax.swing.*; @SuppressWarnings("serial") public class SwingTemplateJPanel extends JPanel { public static concluding int CANVAS_WIDTH = 640; public static last int CANVAS_HEIGHT = 480; public static final String Championship = "...Title..."; public SwingTemplateJPanel() { setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT)); } @Override public void paintComponent(Graphics g) { super.paintComponent(m); 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 every bit a Standalone Awarding
The above template has a primary()
method, which can exist run as a standalone application, a web-commencement 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 tin can provide another grade to run the master class as an applet equally follows:
1 two 3 4 five 6 7 8 9 x eleven 12 13 fourteen 15 16 17 eighteen 19 20 21 22 23 24 | import javax.swing.*; @SuppressWarnings("serial") public class SwingTemplateJApplet extends JApplet { @Override public void init() { try { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { setContentPane(new SwingTemplateJPanel()); } }); } take hold of (Exception east) { e.printStackTrace(); } } } |
You demand to provide an HMTL file to run the applet in production. (For testing, You could run your applet directly nether Eclipse/NetBeans using the then-called "appletviewer" without an HTML file.) For example,
1 two three 4 5 vi 7 8 9 10 eleven | <html> <head> <title>An .... Applet</title> </caput> <body> <h1>Heading ...</h1> <applet code="SwingTemplateJApplet.class" width="640" height="480" alt="Fault Loading Applet?!"> Your browser does not seem to support <APPLET> tag! </applet> </torso> </html> |
Graphics with OO Design
In a good OO design, each class shall exist encapsulated, shall have its own attributes and operations (variables and methods), and responsible for painting itself in a graphics programme.
The class diagram is every bit follows:
Enumeration Seed.java
1 2 three 4 5 half dozen | public enum Seed { EMPTY, CROSS, NOUGHT } |
Enumeration State.java
1 2 3 4 5 vi | public enum State { PLAYING, Depict, CROSS_WON, NOUGHT_WON } |
Grade Cell.java
ane 2 3 4 5 6 seven eight ix 10 11 12 13 14 fifteen 16 17 eighteen nineteen 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | import java.awt.*; public class Cell { Seed content; int row, col; public Cell(int row, int col) { this.row = row; this.col = col; clear(); } public void clear() { content = Seed.EMPTY; } public void paint(Graphics thou) { Graphics2D g2d = (Graphics2D)thou; 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(Colour.RED); int x2 = (col + i) * 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.Blueish); g2d.drawOval(x1, y1, GameMain.SYMBOL_SIZE, GameMain.SYMBOL_SIZE); } } } |
Class Board.java
1 2 3 4 5 6 7 8 9 ten xi 12 13 14 xv 16 17 18 19 twenty 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | import java.awt.*; public class Lath { Cell[][] cells; public Board() { cells = new Jail 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) { return false; } } } render truthful; } public boolean hasWon(Seed seed, int seedRow, int seedCol) { return (cells[seedRow][0].content == seed && cells[seedRow][one].content == seed && cells[seedRow][2].content == seed || cells[0][seedCol].content == seed && cells[1][seedCol].content == seed && cells[ii][seedCol].content == seed || seedRow == seedCol && cells[0][0].content == seed && cells[one][i].content == seed && cells[2][2].content == seed || seedRow + seedCol == 2 && cells[0][2].content == seed && cells[ane][1].content == seed && cells[2][0].content == seed); } public void pigment(Graphics thousand) { 1000.setColor(Color.GRAY); for (int row = 1; row < GameMain.ROWS; ++row) { yard.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 - one, 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
1 two 3 4 5 6 7 8 9 10 11 12 xiii fourteen xv 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 twoscore 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 lxx 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | import java.awt.*; import java.awt.outcome.*; import javax.swing.*; @SuppressWarnings("series") public class GameMain extends JPanel { public static terminal int ROWS = 3; public static final int COLS = iii; public static final Cord TITLE = "Tic Tac Toe"; public static concluding int CELL_SIZE = 100; public static final int CANVAS_WIDTH = CELL_SIZE * COLS; public static terminal int CANVAS_HEIGHT = CELL_SIZE * ROWS; public static concluding int GRID_WIDTH = 8; public static final int GRID_WIDHT_HALF = GRID_WIDTH / ii; public static concluding int CELL_PADDING = CELL_SIZE / 6; public static final int SYMBOL_SIZE = CELL_SIZE - CELL_PADDING * 2; public static terminal int SYMBOL_STROKE_WIDTH = viii; private Lath lath; private GameState currentState; private Seed currentPlayer; individual JLabel statusBar; public GameMain() { this.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { int mouseX = eastward.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 = currentPlayer; updateGame(currentPlayer, rowSelected, colSelected); currentPlayer = (currentPlayer == Seed.CROSS) ? Seed.NOUGHT : Seed.CROSS; } } else { 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 + xxx)); 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.Cantankerous; } 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; } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); setBackground(Color.WHITE); board.paint(g); if (currentState == GameState.PLAYING) { statusBar.setForeground(Colour.BLACK); if (currentPlayer == Seed.CROSS) { statusBar.setText("X's Plow"); } else { statusBar.setText("O's Turn"); } } else if (currentState == GameState.DRAW) { statusBar.setForeground(Colour.Cherry-red); statusBar.setText("It'southward a Draw! Click to play again."); } else if (currentState == GameState.CROSS_WON) { statusBar.setForeground(Color.Cerise); statusBar.setText("'X' Won! Click to play over again."); } else if (currentState == GameState.NOUGHT_WON) { statusBar.setForeground(Color.RED); statusBar.setText("'O' Won! Click to play over 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(zero); frame.setVisible(truthful); } }); } } |
Running as a Standalone Program
Merely run the form containing the entry main()
method.
Deploying an Application via a JAR file
To deploy an awarding containing many classes, you have to pack (i.e., jar) all classes and resources into a unmarried file, with a manifest that specifies the main class (containing the entry primary()
method).
For example:
- via the Eclipse's "Consign" pick: Right-click on the project ⇒ Consign ⇒ Java ⇒ JAR file ⇒ Next ⇒ Specify the JAR filename ⇒ Side by side ⇒ Adjacent ⇒ Select "Generate the manifest file" ⇒ Browse to select the main class "
GameMain
" ⇒ Stop. - via the "
jar
" command.
First, create a manifest file called "tictactoe.mf
", equally follow:Manifest-Version: 1.0 Main-Class: GameMain
Adjacent, event a "jar
" command (class CMD beat out) where options'c'
for create,'m'
for manifest,'f'
for output jar filename, and'5'
for verbose:> jar cmfv tictactoe.mf tictactoe.jar *.class
You can run the program from a JAR file direct (without unpacking the JAR file) by:
- In Windows' Explorer, right-click on the JAR file ⇒ Open with ⇒ Java Platform SE Binary; 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, yous could utilize 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.coffee
Provide a chief class (says AppletMain.java
) for the applet that extends javax.swing.JApplet
:
i 2 3 iv v vi vii 8 9 10 11 12 13 14 15 16 17 18 19 xx 21 22 23 | import javax.swing.*; @SuppressWarnings("serial") public class AppletMain extends JApplet { @Override public void init() { attempt { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { setContentPane(new GameMain()); } }); } grab (Exception e) { e.printStackTrace(); } } } |
TicTacToe.html
Provide an HMTL file (says "TicTacToe.html
") that embeds the "AppletMain.class
":
i 2 3 iv 5 6 seven viii 9 x 11 | <html> <caput> <title>Tic Tac Toe</title> </head> <trunk> <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! </applet> </torso> </html> |
tictactoe.jar
To deploy an applet which contains more than one classes, yous demand to pack all the classes and resources into a JAR file (e.thousand., via Eclipse's "Consign" option or "jar
" command described earlier), but you demand non use a manifest (for specify a main class every bit applet does not need a main()
method). And so, employ 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! </applet>
Adding Audio Event
How to Play an Audio File
SoundTest.java
1 2 3 4 v 6 7 8 9 10 11 12 xiii 14 xv sixteen 17 18 xix 20 21 22 23 24 25 26 27 28 29 thirty 31 32 33 34 35 36 37 38 39 twoscore 41 42 43 44 45 46 47 48 49 fifty 51 52 53 54 55 56 57 58 59 threescore 61 62 63 64 | import coffee.awt.*; import java.awt.outcome.*; import java.internet.URL; import javax.sound.sampled.*; import javax.swing.*; @SuppressWarnings("serial") public class SoundTest extends JFrame { individual Cord fileGameOver = "gameover.wav"; private Clip soundClipGameOver; public SoundTest() { endeavour { URL url = this.getClass().getClassLoader().getResource(fileGameOver); if (url == zero) { System.err.println("Couldn't find file: " + fileGameOver); } else { 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() { @Override public void actionPerformed(ActionEvent e) { if (soundClipGameOver.isRunning()) soundClipGameOver.stop(); soundClipGameOver.setFramePosition(0); soundClipGameOver.get-go(); } }); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setTitle("Test Sound"); setSize(200, 100); setVisible(truthful); } public static void master(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new SoundTest(); } }); } } |
- Prepare a sound file called "
gameover.wav
" (attempt googling) and place it under under your projection root (or "bin
") directory. - If you receive error message "Couldn't find file: xxx". Try moving your file around your project (such every bit nether the "bin" sub-directory).
- JDK supports only a sampled audio format, "
.wav
", ".au
", and ".aiff
". It does non support ".mp3
" (You will get an error message "Audio Format not supported: 30". 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 demand to test the sound effect nether a Swing application, instead of placing all the codes nether the
main()
. This is becausemain()
exits before the sound gets a chance to play.
Adding Audio Consequence to the Tic-Tac-Toe Graphics Version
Two sound clips were used in the demo: ane for the move ("movement.wav
") and the other for game-over ("gameover.wav
"). (Google to discover some interesting "wav
" files.)
Include the following codes at the advisable locations:
import coffee.io.IOException; import java.internet.URL; import javax.sound.sampled.*; ...... String fileMove = "sounds/motion.wav"; Cord fileGameOver = "sounds/gameover.wav"; Clip soundClipMove; Clip soundClipGameOver; ...... endeavour {
URL url = this.getClass().getClassLoader().getResource(fileGameOver);
if (url == nil) {
System.err.println("Couldn't find file: " + fileGameOver);
} else {
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);
} else {
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
soundClipMove = AudioSystem.getClip();
soundClipMove.open(audioIn);
}
} catch (UnsupportedAudioFileException e) {
Organisation.err.println("Sound Format non supported!");
} catch (Exception due east) {
e.printStackTrace();
}
...... if (currentState == GameState.PLAYING) { if (soundClipMove.isRunning()) soundClipMove.stop(); soundClipMove.setFramePosition(0); soundClipMove.start(); } else { 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 "Drawing Images" of "Custom Graphics".
Animation
Read "Animation" of "Custom Graphics".
Fast Matching of Winning Patterns with Bit-Masks (Advanced)
Reference: Arthur van Hoff's Tic Tac Toe Applet Demo (nether the JDK demo "applets" binder).
A much more efficient method for matching with a winning pattern in a Tic-tac-toe is to apply a 9-flake binary number (stored as an int
or brusque
type) to denote the placement of the seeds, and use fleck operations to perform the matching.
The following table summaries all the bit-wise operations, which are efficient and fast.
Operator | Clarification | Usage | Instance |
---|---|---|---|
& | Fleck-wise AND | expr1 & expr2 | 0b0110 0001 & Ob1110 0000 gives 0b0110 0000 |
| | Scrap-wise OR | expr1 | expr2 | 0b0110 0001 | Ob0000 1000 gives 0b0110 1001 |
! | Bit-wise Not | !expr | ^0b0110 0001 gives 0b1001 1110 |
^ | Bit-wise XOR | expr1 ^ expr2 | 0b0110 0001 ^ Ob0000 0001 gives 0b0110 1001 |
<< | Left-shift and padded with zeros | operand << number | 0b0000 0001 << iv gives 0b0001 0000 |
>> | Right-shift and padded with the "sign-bit" (Signed-extended right-shift) | operand >> number | 0b1000 0001 >> two gives 0b1110 0000 |
>>> | Right-shift and padded with zeros (Unsigned-extended right-shift) | operand >>> number | 0b1000 0001 >>> two gives 0b0010 0000 |
We can continue the 8 winning patterns in an int
array every bit follows:
int[] winningPatterns = { 0x1c0, 0x038, 0x007, 0x124, 0x092, 0x049, 0x111, 0x054};
Annotation: JDK one.7 supports binary literals beginning with prefix "0b
". Pre-JDK 1.vii does not support binary literals simply supports hexadecimal literals beginning with prefix "0x
". Eclipse IDE supports JDK ane.7 only after Eclipse 3.seven.2. Hence, try 0b...
but fall back to 0x...
if compilation fails.
Nosotros ascertain 2 placement binary patterns for the cross and nought respectively.
int crossPattern; int noughtPattern; int bitPosition = rowSelected * ROWS + colSelected; if (currentPlayer == Seed.Cross) { crossPattern = crossPattern | (0x1 << bitPosition); } else { 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 'i'
bit at the proper position. It is and then fleck-OR with the existing pattern to include the new chip, without modifying the existing bits. For example, suppose rowSelect = ii
and colSelected = 0
, then bitPosition = 6
. (0x1 << bitPosition)
gives 0b 001 000 000
.
To lucifer with the winning patterns:
public boolean hasWon(Seed theSeed) { int playerPattern = (theSeed == Seed.CROSS) ? crossPattern : noughtPattern; for (int aWinningPattern : winningPatterns) { if ((aWinningPattern & playerPattern) == aWinningPattern) { return true; } } render fake; }
(aWinningPattern & playerPattern)
masks out all the bits in the playerPattern
except those having 1'southward 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 equally it involves only comparison with 8 integers (plus 8 efficient chip-AND operations).
Other Modes of Operation
WebStart Awarding
[TODO]
Playing Over the Cyberspace
[TODO]
Playing Confronting Computer with AI (Advanced)
Read "Case Study on Tic-Tac-Toe Part 2: With AI".
REFERENCES & Resource
- JDK Applets demo "TicTacToe" (under JDK demo applets folder).
Source: https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaGame_TicTacToe.html
0 Response to "Ask User to Play Again Java"
Post a Comment