Tic Tac Toe X Code Play Again
Let'due south Kickoff with a 2-Role player Console Non-OO Tic-Tac-Toe
Let the states showtime with a ii-actor console (non-graphics) version of Tic-Tac-Toe, where player 'X'
and player 'O'
enter their moves successively, as shown below:
Player '10', enter your movement (row[1-three] cavalcade[one-3]): ii 2 | | ----------- | Ten | ----------- | | Player 'O', enter your move (row[1-iii] column[1-3]): ane 1 O | | ----------- | X | ----------- | | Player 'X', enter your move (row[1-3] column[1-3]): 1 iii O | | X ----------- | X | ----------- | | Actor 'O', enter your motion (row[1-iii] column[1-three]): iii 1 O | | X ----------- | X | ----------- O | | Histrion '10', enter your move (row[1-iii] cavalcade[1-three]): 2 ii This movement at (2,2) is not valid. Try over again... Thespian 'X', enter your move (row[1-3] cavalcade[one-iii]): 2 3 O | | 10 ----------- | X | X ----------- O | | Player 'O', enter your motion (row[1-3] column[1-3]): two i O | | 10 ----------- O | X | X ----------- O | | Histrion 'O' won!
TTTConsoleNonOO.coffee
1 2 iii four 5 six 7 8 ix 10 11 12 13 xiv 15 16 17 18 nineteen xx 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 threescore 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 xc 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 | import java.util.Scanner; public class TTTConsoleNonOO { public static final int Cross = 0; public static final int NOUGHT = 1; public static final int NO_SEED = 2; public static last int ROWS = 3, COLS = 3; public static int[][] lath = new int[ROWS][COLS]; public static int currentPlayer; public static final int PLAYING = 0; public static final int Describe = one; public static concluding int CROSS_WON = 2; public static final int NOUGHT_WON = 3; public static int currentState; public static Scanner in = new Scanner(Organization.in); public static void main(String[] args) { initGame(); do { currentPlayerMove(); printBoard(); if (currentState == CROSS_WON) { System.out.println("'X' won!\nBye!"); } else if (currentState == NOUGHT_WON) { System.out.println("'O' won!\nBye!"); } else if (currentState == DRAW) { System.out.println("Information technology's a Describe!\nBye!"); } 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] = NO_SEED; } } currentPlayer = Cantankerous; currentState = PLAYING; } public static void currentPlayerMove() { boolean validInput = imitation; practice { if (currentPlayer == CROSS) { Arrangement.out.print("Histrion 'Ten', enter your movement (row[i-iii] column[ane-iii]): "); } else { Organization.out.print("Player 'O', enter your move (row[1-3] column[1-3]): "); } int row = in.nextInt() - i; int col = in.nextInt() - 1; if (row >= 0 && row < ROWS && col >= 0 && col < COLS && board[row][col] == NO_SEED) { currentState = moveAndGetState(currentPlayer, row, col); validInput = true; } else { Organisation.out.println("This move at (" + (row + ane) + "," + (col + i) + ") is not valid. Try again..."); } } while (!validInput); } public static int moveAndGetState(int histrion, int selectedRow, int selectedCol) { board[selectedRow][selectedCol] = histrion; if (board[selectedRow][0] == player && lath[selectedRow][1] == player && board[selectedRow][two] == player || board[0][selectedCol] == role player && board[one][selectedCol] == player && board[2][selectedCol] == player || selectedRow == selectedCol && board[0][0] == thespian && board[ane][1] == histrion && board[2][2] == player || selectedRow + selectedCol == 2 && board[0][two] == player && lath[1][one] == player && lath[two][0] == player) { return (thespian == Cantankerous) ? CROSS_WON : NOUGHT_WON; } else { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { if (lath[row][col] == NO_SEED) { render PLAYING; } } } return Depict; } } 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 - ane) { System.out.print("|"); } } Arrangement.out.println(); if (row != ROWS - i) { Organization.out.println("-----------"); } } System.out.println(); } public static void printCell(int content) { switch (content) { case CROSS: System.out.print(" 10 "); break; example NOUGHT: Organisation.out.impress(" O "); intermission; case NO_SEED: Organization.out.print(" "); break; } } } |
Dissecting the Plan
Non-OO programs (like C programs) are organized in methods (or functions), which access common global variables. In non-OO Java, all the variables/methods shall exist alleged static
(i.due east., they belong to the class instead of instances). The plan starts at the principal()
method. No instances are created.
A lath game (such equally Tic-tac-toe) is typically programmed equally a state machine. Depending on the current-country and the player's motility, the game goes into the next-state. In this example, I use a variable currentState
to keep track of the current-country of the game, and define named constants to denote the diverse states of the game (PLAYING
, Depict
, CROSS_WON
, and NOUGHT_WON
). A method chosen moveAndGetState()
is defined, which volition be called to compute the new game state.
Ii 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 volition be useful in the object-oriented design to separate the board and cells into separate classes.
[TODO] more caption
TRY: Prompt the user whether to play over again later on gameover.
exercise { initGame(); ...... ...... Organization.out.print("Play again (y/n)? "); char ans = in.next().charAt(0); if (ans != 'y' && ans != 'Y') { System.out.println("Good day!"); System.exit(0); } } while (true);
A Console OO Tic-Tac-Toe
Let u.s.a. catechumen the earlier non-OO version of Tic-Tac-Toe to object-oriented. The OO version of this simple Tic-Tac-Toe is more circuitous than the not-OO version, considering Tic-Tac-Toe is a rather simple awarding. But OO pattern is a necessity to build a complex application.
Enumerations State
In our earlier version, nosotros used int
named-constants to represent the various game states, every bit follows:
public static final int PLAYING = 0; public static concluding int Draw = 1; public static concluding int CROSS_WON = two; public static final int NOUGHT_WON = 3; public static int currentState = PLAYING;
This arroyo of using int
named-constants is ameliorate than using number in the programming statements, but it is not ideal. This is considering yous may inadvertently assign an int
value exterior the valid range to the variable currentState
. For example,
currentState = 99;
JDK 1.five introduces a new feature called enumeration, which is a special class for storing an enumeration (list) of items. In our case, we tin can ascertain an enumeration called State
equally follows:
1 2 three 4 five six | public enum State { PLAYING, Describe, CROSS_WON, NOUGHT_WON } |
To reference an item in an enum
, use enumName.itemName
(e.chiliad., Land.PLAYING
and State.Describe
), but like referencing static
variables of a class (e.g., Math.PI
).
You lot can create an case for an enum
(just like creating an case of a class
) and assign a value into information technology. Nosotros shall at present declare the variable currentState
as an instance of State
, which can accept the value of Land.PLAYING
, State.DRAW
, State.CROSS_WON
, and State.NOUGHT_WON
.
State currentState; currentState = Gtate.PLAYING;
Accept note that you can simply assign a value defined in the enumeration (such as Country.PLAYING
, Land.Describe
), and NOT an arbitrary int
value in the earlier instance. In other words, enum is SAFE!
Enumerations Seed
We shall also create an enum
called Seed
every bit follows.
1 ii 3 4 5 vi 7 8 9 10 xi 12 13 14 15 16 17 18 19 xx 21 22 23 24 25 | public enum Seed { Cross("10"), NOUGHT("O"), NO_SEED(" "); private String icon; private Seed(Cord icon) { this.icon = icon; } public String getIcon() { render icon; } } |
This enum is used in 2 properties:
- Player: uses values
CROSS
andNOUGHT
. - Cell Content: uses values
CROSS
,NOUGHT
, andNO_SEED
.
(Ideally, nosotros should create 2 enums with inheritance, but enum inheritance is not supported in Java.)
Again, y'all need to utilize Seed.NO_SEED
, Seed.Cross
, Seed.NOUGHT
to refer to these values, just similar any public
static
final
constants.
We likewise attach an icon to each of the enum items, past defining a private variable, a private constructor and a public getter (as in a regular class). We can get the icon via enumItem.getIcon()
.
We shall declare the variables currentPlayer
and content
as instances of enum Thespian
and CellContent
.
private Seed currentPlayer; currentPlayer = Seed.CROSS; individual Seed content; content = Seed.NO_SEED;
In cursory, an enum
is just a special class with a listing of named-constants. Just 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 brainstorm with 2 classes, a class Cell
for each private prison cell of the game board, and a class Board
for the 3x3 game board.
The Cell
class has an example variable called content
(with packet access), of the type enum
Seed
. You can but assign a value from the enum
'southward constants, such as Seed.NO_SEED
, Seed.Cross
, and Seed.NOUGHT
, into content
. A Cell
can paint()
and init()
itself.
The Board
course composes of ix Prison cell
instances, bundled in an iii×3 array called cells
(with parcel access), of the type Cell[][]
. A Board
tin paint()
and init()
itself, and supports its own operations such equally moveAndGetState()
.
Prison cell.java
one two 3 four 5 6 7 eight 9 ten 11 12 13 14 15 16 17 xviii xix 20 21 22 23 24 25 26 27 | public form Cell { Seed content; int row, col; public Cell(int row, int col) { this.row = row; this.col = col; this.content = Seed.NO_SEED; } public void init() { this.content = Seed.NO_SEED; } public void paint() { Cord icon = this.content.getIcon(); System.out.print(icon); } } |
Board.java
1 ii three four 5 6 7 8 nine ten 11 12 13 14 15 xvi 17 xviii 19 xx 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 threescore 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 | public class Board { public static final int ROWS = three; public static concluding int COLS = 3; Prison cell[][] cells; public Lath() { cells = new Cell[ROWS][COLS]; for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { cells[row][col] = new Jail cell(row, col); } } } public void init() { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { cells[row][col].init(); } } } public Land moveAndGetState(Seed player, int selectedRow, int selectedCol) { cells[selectedRow][selectedCol].content = player; if (cells[selectedRow][0].content == player && cells[selectedRow][1].content == player && cells[selectedRow][2].content == player || cells[0][selectedCol].content == player && cells[i][selectedCol].content == role player && cells[2][selectedCol].content == player || selectedRow == selectedCol && cells[0][0].content == player && cells[1][ane].content == actor && cells[two][ii].content == player || selectedRow + selectedCol == 2 && cells[0][two].content == player && cells[ane][1].content == player && cells[2][0].content == player) { return (player == Seed.Cross) ? State.CROSS_WON : State.NOUGHT_WON; } else { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { if (cells[row][col].content == Seed.NO_SEED) { return Land.PLAYING; } } } return State.DRAW; } } public void paint() { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { System.out.print(" "); cells[row][col].paint(); Organisation.out.print(" "); if (col < COLS - 1) Organization.out.print("|"); } System.out.println(); if (row < ROWS - 1) { System.out.println("-----------"); } } Arrangement.out.println(); } } |
Class GameMain
Finally, let's write a main course called GameMain
to pull all the pieces together. GameMain
acts every bit the overall controller for the game.
GameMain.java
1 2 3 four 5 half-dozen 7 eight 9 10 xi 12 13 fourteen 15 xvi 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 seventy 71 72 | import java.util.Scanner; public form GameMain { private Board lath; individual Country currentState; private Seed currentPlayer; private static Scanner in = new Scanner(System.in); public void initGame() { board.init(); currentPlayer = Seed.Cantankerous; currentState = Country.PLAYING; } public void motility() { boolean validInput = false; exercise { Cord icon = currentPlayer.getIcon(); System.out.print("Player '" + icon + "', enter your move (row[1-3] column[1-iii]): "); int row = in.nextInt() - 1; int col = in.nextInt() - one; if (row >= 0 && row < Board.ROWS && col >= 0 && col < Board.COLS && board.cells[row][col].content == Seed.NO_SEED) { currentState = board.moveAndGetState(currentPlayer, row, col); validInput = true; } else { Arrangement.out.println("This motion at (" + (row + ane) + "," + (col + 1) + ") is not valid. Attempt again..."); } } while (!validInput); } public static void master(String[] args) { new GameMain(); } } |
OO vs non-OO
The OO-version and the non-OO version have the same codes, but are organized differently. The organization in OO enables you lot to design and develop complex arrangement.
A Graphical Tic-Tac-Toe with Elementary-OO
Let'southward rewrite the "panel" version into a "graphics" version - a Java Swing application, as illustrated. In this initial design, we practise non separate the prison cell and lath into defended classes, merely include them in the primary class. We used an inner class DrawCanvas
(that extends JPanel
) to do the custom drawing, and an bearding inner class for MouseListener
.
The content-pane (of the top-level container JFrame
) is set to BorderLayout
. The DrawCanvas
(JPanel
) is placed at the Heart
; while a status-bar (a JLabel
) is placed at the SOUTH
(PAGE_END
).
The class diagram is every bit follows:
TTTGraphics.java
1 ii 3 4 5 half dozen 7 8 9 10 xi 12 13 14 15 16 17 18 19 xx 21 22 23 24 25 26 27 28 29 thirty 31 32 33 34 35 36 37 38 39 forty 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 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 217 218 219 220 221 222 223 224 | import java.awt.*; import coffee.awt.event.*; import javax.swing.*; public grade TTTGraphics extends JFrame { private static final long serialVersionUID = 1L; public static final int ROWS = iii; public static final int COLS = three; public static final int CELL_SIZE = 120; public static final int CANVAS_WIDTH = CELL_SIZE * COLS; public static final int CANVAS_HEIGHT = CELL_SIZE * ROWS; public static terminal int GRID_WIDTH = 10; public static last int GRID_WIDHT_HALF = GRID_WIDTH / 2; public static concluding int CELL_PADDING = CELL_SIZE / 5; public static last int SYMBOL_SIZE = CELL_SIZE - CELL_PADDING * ii; public static final int SYMBOL_STROKE_WIDTH = 8; public static final Colour COLOR_BG = Color.WHITE; public static final Color COLOR_BG_STATUS = new Color(216, 216, 216); public static final Color COLOR_GRID = Color.LIGHT_GRAY; public static final Color COLOR_CROSS = new Color(211, 45, 65); public static final Color COLOR_NOUGHT = new Color(76, 181, 245); public static final Font FONT_STATUS = new Font("OCR A Extended", Font.PLAIN, 14); public enum State { PLAYING, Draw, CROSS_WON, NOUGHT_WON } individual State currentState; public enum Seed { CROSS, NOUGHT, NO_SEED } individual Seed currentPlayer; private Seed[][] lath; private DrawCanvas canvass; private JLabel statusBar; public TTTGraphics() { canvas = new DrawCanvas(); canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT)); sheet.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { int mouseX = e.getX(); int mouseY = eastward.getY(); int row = mouseY / CELL_SIZE; int col = mouseX / CELL_SIZE; if (currentState == Country.PLAYING) { if (row >= 0 && row < ROWS && col >= 0 && col < COLS && board[row][col] == Seed.NO_SEED) { currentState = moveAndGetState(currentPlayer, row, col); currentPlayer = (currentPlayer == Seed.Cross) ? Seed.NOUGHT : Seed.Cantankerous; } } else { initGame(); } repaint(); } }); statusBar = new JLabel(" "); statusBar.setFont(FONT_STATUS); statusBar.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 12)); statusBar.setOpaque(true); statusBar.setBackground(COLOR_BG_STATUS); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); cp.add(canvass, BorderLayout.Eye); cp.add(statusBar, BorderLayout.PAGE_END); lath = new Seed[ROWS][COLS]; initGame(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setTitle("Tic Tac Toe"); setVisible(truthful); } public void initGame() { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { board[row][col] = Seed.NO_SEED; } } currentPlayer = Seed.Cross; currentState = State.PLAYING; } public Land moveAndGetState(Seed player, int selectedRow, int selectedCol) { lath[selectedRow][selectedCol] = player; if (board[selectedRow][0] == player && board[selectedRow][1] == player && board[selectedRow][two] == role player || board[0][selectedCol] == player && board[1][selectedCol] == player && board[2][selectedCol] == player || selectedRow == selectedCol && lath[0][0] == thespian && lath[1][1] == histrion && lath[2][2] == thespian || selectedRow + selectedCol == two && board[0][2] == role player && lath[1][ane] == player && board[2][0] == player) { return (player == Seed.CROSS) ? Land.CROSS_WON : State.NOUGHT_WON; } else { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { if (board[row][col] == Seed.NO_SEED) { return State.PLAYING; } } } return State.DRAW; } } grade DrawCanvas extends JPanel { individual static final long serialVersionUID = 1L; @Override public void paintComponent(Graphics g) { super.paintComponent(one thousand); setBackground(COLOR_BG); k.setColor(COLOR_GRID); for (int row = one; 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)1000; 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_CROSS); 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_NOUGHT); g2d.drawOval(x1, y1, SYMBOL_SIZE, SYMBOL_SIZE); } } } if (currentState == State.PLAYING) { statusBar.setForeground(Color.BLACK); statusBar.setText((currentPlayer == Seed.Cantankerous) ? "Ten's Turn" : "O'due south Turn"); } else if (currentState == Country.DRAW) { statusBar.setForeground(Color.RED); statusBar.setText("Information technology'due south a Draw! Click to play once more"); } else if (currentState == Country.CROSS_WON) { statusBar.setForeground(Colour.Reddish); statusBar.setText("'10' Won! Click to play again"); } else if (currentState == State.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 TTTGraphics(); } }); } } |
Dissecting the Program
[TODO]
A Graphical Tic-Tac-Toe 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 program.
The grade diagram is equally follows:
Enumeration Seed.coffee
one 2 three 4 five six 7 viii 9 10 eleven | public enum Seed { CROSS, NOUGHT, NO_SEED } |
Enumeration State.java
1 ii 3 4 5 half-dozen | public enum Land { PLAYING, DRAW, CROSS_WON, NOUGHT_WON } |
Class Jail cell.coffee
1 2 3 iv 5 6 7 eight 9 x xi 12 13 14 15 xvi 17 18 xix twenty 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 | import java.awt.*; public class Jail cell { public static final int SIZE = 120; public static final int PADDING = SIZE / five; public static last int SEED_SIZE = SIZE - PADDING * 2; public static final int SEED_STROKE_WIDTH = 8; Seed content; int row, col; public Cell(int row, int col) { this.row = row; this.col = col; content = Seed.NO_SEED; } public void init() { content = Seed.NO_SEED; } public void pigment(Graphics g) { Graphics2D g2d = (Graphics2D)one thousand; g2d.setStroke(new BasicStroke(SEED_STROKE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); int x1 = col * SIZE + PADDING; int y1 = row * SIZE + PADDING; if (content == Seed.Cross) { g2d.setColor(GameMain.COLOR_CROSS); int x2 = (col + 1) * SIZE - PADDING; int y2 = (row + one) * SIZE - PADDING; g2d.drawLine(x1, y1, x2, y2); g2d.drawLine(x2, y1, x1, y2); } else if (content == Seed.NOUGHT) { g2d.setColor(GameMain.COLOR_NOUGHT); g2d.drawOval(x1, y1, SEED_SIZE, SEED_SIZE); } } } |
Course Board.java
ane 2 iii iv 5 6 seven 8 nine 10 xi 12 xiii 14 15 16 17 18 nineteen 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 fifty 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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | import coffee.awt.*; public class Board { public static final int ROWS = 3; public static concluding int COLS = 3; public static final int CANVAS_WIDTH = Cell.SIZE * COLS; public static final int CANVAS_HEIGHT = Jail cell.SIZE * ROWS; public static terminal int GRID_WIDTH = 8; public static final int GRID_WIDHT_HALF = GRID_WIDTH / 2; public static final Color COLOR_GRID = Colour.LIGHT_GRAY; public static final int Y_OFFSET = 1; Cell[][] cells; 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].init(); } } } public State moveAndGetState(Seed player, int selectedRow, int selectedCol) { cells[selectedRow][selectedCol].content = player; if (cells[selectedRow][0].content == actor && cells[selectedRow][i].content == player && cells[selectedRow][2].content == histrion || cells[0][selectedCol].content == player && cells[one][selectedCol].content == player && cells[2][selectedCol].content == thespian || selectedRow == selectedCol && cells[0][0].content == role player && cells[one][1].content == actor && cells[2][ii].content == player || selectedRow + selectedCol == 2 && cells[0][two].content == thespian && cells[one][ane].content == player && cells[2][0].content == player) { return (player == Seed.CROSS) ? Land.CROSS_WON : State.NOUGHT_WON; } else { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { if (cells[row][col].content == Seed.NO_SEED) { return Country.PLAYING; } } } return Country.DRAW; } } public void pigment(Graphics g) { thousand.setColor(COLOR_GRID); for (int row = i; row < ROWS; ++row) { g.fillRoundRect(0, Cell.SIZE * row - GRID_WIDHT_HALF, CANVAS_WIDTH - 1, GRID_WIDTH, GRID_WIDTH, GRID_WIDTH); } for (int col = ane; col < COLS; ++col) { chiliad.fillRoundRect(Cell.SIZE * col - GRID_WIDHT_HALF, 0 + Y_OFFSET, GRID_WIDTH, CANVAS_HEIGHT - 1, GRID_WIDTH, GRID_WIDTH); } for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { cells[row][col].paint(m); } } } } |
Classes GameMain.java
For better flexibility, we shall extend ths chief class from JPanel
(instead of JFrame
); classify a JFrame
in chief()
; and prepare this as the contentPane
of the JFrame
.
i 2 3 4 v 6 7 8 ix ten 11 12 13 14 15 sixteen 17 eighteen 19 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 50 51 52 53 54 55 56 57 58 59 threescore 61 62 63 64 65 66 67 68 69 70 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 | import java.awt.*; import coffee.awt.event.*; import javax.swing.*; public form GameMain extends JPanel { individual static concluding long serialVersionUID = 1L; public static final String Championship = "Tic Tac Toe"; public static final Color COLOR_BG = Color.WHITE; public static final Color COLOR_BG_STATUS = new Colour(216, 216, 216); public static terminal Color COLOR_CROSS = new Colour(239, 105, eighty); public static last Color COLOR_NOUGHT = new Color(64, 154, 225); public static final Font FONT_STATUS = new Font("OCR A Extended", Font.Manifestly, 14); private Board board; private State currentState; private Seed currentPlayer; private JLabel statusBar; public GameMain() { super.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { int mouseX = e.getX(); int mouseY = e.getY(); int row = mouseY / Prison cell.SIZE; int col = mouseX / Cell.SIZE; if (currentState == Land.PLAYING) { if (row >= 0 && row < Board.ROWS && col >= 0 && col < Lath.COLS && board.cells[row][col].content == Seed.NO_SEED) { currentState = board.moveAndGetState(currentPlayer, row, col); currentPlayer = (currentPlayer == Seed.Cross) ? Seed.NOUGHT : Seed.CROSS; } } else { initGame(); } repaint(); } }); statusBar = new JLabel(); statusBar.setFont(FONT_STATUS); statusBar.setBackground(COLOR_BG_STATUS); statusBar.setOpaque(true); statusBar.setPreferredSize(new Dimension(300, thirty)); statusBar.setHorizontalAlignment(JLabel.LEFT); statusBar.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 12)); super.setLayout(new BorderLayout()); super.add(statusBar, BorderLayout.PAGE_END); super.setPreferredSize(new Dimension(Board.CANVAS_WIDTH, Board.CANVAS_HEIGHT + 30)); super.setBorder(BorderFactory.createLineBorder(COLOR_BG_STATUS, ii, simulated)); lath = new Board(); initGame(); } public void initGame() { for (int row = 0; row < Board.ROWS; ++row) { for (int col = 0; col < Board.COLS; ++col) { lath.cells[row][col].content = Seed.NO_SEED; } } currentPlayer = Seed.CROSS; currentState = Land.PLAYING; } @Override public void paintComponent(Graphics g) { super.paintComponent(grand); setBackground(COLOR_BG); board.paint(g); if (currentState == Land.PLAYING) { statusBar.setForeground(Color.BLACK); statusBar.setText((currentPlayer == Seed.CROSS) ? "X's Turn" : "O's Turn"); } else if (currentState == Country.Depict) { statusBar.setForeground(Color.Ruby); statusBar.setText("It's a Draw! Click to play once again."); } else if (currentState == State.CROSS_WON) { statusBar.setForeground(Color.RED); statusBar.setText("'X' Won! Click to play again."); } else if (currentState == State.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(Championship); frame.setContentPane(new GameMain()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(cipher); frame.setVisible(true); } }); } } |
Dissecting the Program
[TODO]
Running as a Standalone Plan
Simply run the class containing the entry main()
method.
Deploying an Application via a JAR file
To deploy an awarding containing many classes, you have to pack (i.east., jar) all classes and resources into a single file, with a manifest that specifies the primary class (containing the entry main()
method).
For example:
- via the Eclipse's "Export" option: Right-click on the project ⇒ Export ⇒ Java ⇒ JAR file ⇒ Next ⇒ Specify the JAR filename ⇒ Next ⇒ Next ⇒ Select "Generate the manifest file" ⇒ Browse to select the main course "
GameMain
" ⇒ End. - via the "
jar
" command.
First, create a manifest file called "tictactoe.mf
", every bit follow:Manifest-Version: 1.0 Main-Class: GameMain
Side by side, result a "jar
" command (form CMD crush) 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 plan from a JAR file directly (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
coffee.exe
with-jar
pick, i.eastward.,> java -jar JarFilename.jar
Note: JAR file uses the ZIP algorithm. In other words, you could utilise WinZIP/7-Zip to open and excerpt the contents of a JAR file.
Running equally an Applet (Obsolete)
Notation: Applet is no longer supported on broswer! I nostalgically keep this department.
AppletMain.java
Provide a principal course (says AppletMain.java
) for the applet that extends javax.swing.JApplet
:
1 2 3 iv 5 6 7 viii nine ten xi 12 13 14 xv sixteen 17 xviii xix xx 21 22 23 | import javax.swing.*; @SuppressWarnings("serial") public form AppletMain extends JApplet { @Override public void init() { try { SwingUtilities.invokeAndWait(new Runnable() { @Override 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
":
1 2 three 4 v six 7 8 9 10 11 | <html> <head> <title>Tic Tac Toe</title> </head> <trunk> <h1>Tic Tac Toe</h1> <applet code="AppletMain.class" width="300" height="330" alt="Mistake Loading Applet?!"> Your browser does not seem to back up <APPLET> tag! </applet> </body> </html> |
tictactoe.jar
To deploy an applet which contains more than than ane classes, you need to pack all the classes and resource into a JAR file (e.k., via Eclipse'due south "Consign" option or "jar
" command described earlier), but you need not use a manifest (for specify a principal class as applet does non need a chief()
method). So, apply the following <applet>
tag with an "annal
" attribute to specify the JAR filename:
<applet code="AppletMain.grade" archive="JarFileName.jar" width="300" superlative="300" alt="Error Loading Applet?!" > Your browser does not seem to back up <APPLET> tag! </applet>
A Graphical Tic-Tac-Toe with Sound Upshot and Images
Read "Using Images and Audio".
Where to proceed the paradigm and audio files?
For Eclipse, create folders called "images" and "audio" under "src", and re-create/paste the files at that place. These folders/files will exist duplicated under the "bin" for execution.
Enumeration State.java
1 2 3 iv 5 6 vii 8 nine 10 xi 12 13 fourteen fifteen xvi 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 50 51 52 53 54 55 | import java.net.URL; import javax.sound.sampled.*; public enum Country { PLAYING("sound/eatfood.wav"), Draw("sound/explode.wav"), CROSS_WON("audio/die.wav"), NOUGHT_WON("audio/die.wav"); private Clip soundClip = cypher; individual Land(Cord audioFilename) { if (audioFilename != null) { effort { URL url = this.getClass().getClassLoader().getResource(audioFilename); if (url != null) { AudioInputStream audioIn = AudioSystem.getAudioInputStream(url); soundClip = AudioSystem.getClip(); soundClip.open(audioIn); } else { System.err.println("Couldn't find file: " + audioFilename); } } take hold of (UnsupportedAudioFileException due east) { System.err.println("Audio Format not supported: " + audioFilename); } take hold of (Exception e) { due east.printStackTrace(); } } } public static boolean mute = false; public void playSoundClip() { if (!mute) { if (soundClip.isRunning()) soundClip.finish(); soundClip.setFramePosition(0); soundClip.showtime(); } } } |
Enumeration Seed.java
one 2 iii 4 5 6 seven eight 9 10 11 12 xiii 14 15 16 17 18 nineteen 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 50 51 | import java.awt.Image; import java.net.URL; import javax.swing.ImageIcon; public enum Seed { Cross("10", "images/cross.gif"), NOUGHT("O", "images/not.gif"), NO_SEED(" ", aught); private Cord displayName; private Paradigm img = null; private Seed(Cord proper noun, Cord imageFilename) { this.displayName = name; if (imageFilename != nothing) { URL imgURL = getClass().getClassLoader().getResource(imageFilename); ImageIcon icon = null; if (imgURL != null) { icon = new ImageIcon(imgURL); } else { Organization.err.println("Couldn't find file " + imageFilename); } img = icon.getImage(); } } public String getDisplayName() { return displayName; } public Image getImage() { return img; } } |
Cell.coffee
ane 2 3 iv five 6 7 8 9 10 11 12 13 fourteen fifteen 16 17 18 19 xx 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import java.awt.*; public class Prison cell { public static final int SIZE = 120; public static final int PADDING = SIZE / 5; public static final int SEED_SIZE = SIZE - PADDING * two; Seed content; int row, col; public Cell(int row, int col) { this.row = row; this.col = col; content = Seed.NO_SEED; } public void init() { content = Seed.NO_SEED; } public void paint(Graphics thousand) { int x1 = col * SIZE + PADDING; int y1 = row * SIZE + PADDING; if (content == Seed.Cantankerous || content == Seed.NOUGHT) { g.drawImage(content.getImage(), x1, y1, SEED_SIZE, SEED_SIZE, aught); } } } |
Board.coffee
i 2 three 4 5 6 7 viii nine 10 11 12 13 14 15 16 17 18 xix xx 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 sixty 61 62 63 64 65 66 67 68 69 70 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 | import java.awt.*; public class Lath { public static last int ROWS = 3; public static final int COLS = 3; public static last int CANVAS_WIDTH = Cell.SIZE * COLS; public static final int CANVAS_HEIGHT = Cell.SIZE * ROWS; public static last int GRID_WIDTH = 8; public static final int GRID_WIDHT_HALF = GRID_WIDTH / two; public static final Color COLOR_GRID = Color.LIGHT_GRAY; public static final int Y_OFFSET = 1; Cell[][] cells; 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 Jail cell(row, col); } } } public void init() { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { cells[row][col].init(); } } } public Country moveAndGetState(Seed player, int selectedRow, int selectedCol) { cells[selectedRow][selectedCol].content = thespian; if (cells[selectedRow][0].content == player && cells[selectedRow][ane].content == player && cells[selectedRow][2].content == actor || cells[0][selectedCol].content == player && cells[one][selectedCol].content == player && cells[2][selectedCol].content == player || selectedRow == selectedCol && cells[0][0].content == player && cells[1][one].content == player && cells[2][2].content == thespian || selectedRow + selectedCol == two && cells[0][2].content == player && cells[i][1].content == histrion && cells[2][0].content == player) { render (thespian == Seed.Cantankerous) ? Land.CROSS_WON : Country.NOUGHT_WON; } else { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { if (cells[row][col].content == Seed.NO_SEED) { return State.PLAYING; } } } render State.Draw; } } public void paint(Graphics g) { chiliad.setColor(COLOR_GRID); for (int row = 1; row < ROWS; ++row) { g.fillRoundRect(0, Jail cell.SIZE * row - GRID_WIDHT_HALF, CANVAS_WIDTH - 1, GRID_WIDTH, GRID_WIDTH, GRID_WIDTH); } for (int col = 1; col < COLS; ++col) { thousand.fillRoundRect(Cell.SIZE * col - GRID_WIDHT_HALF, 0+Y_OFFSET, GRID_WIDTH, CANVAS_HEIGHT - i, GRID_WIDTH, GRID_WIDTH); } for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { cells[row][col].paint(g); } } } } |
GameMain.coffee
1 2 3 4 5 half-dozen 7 8 9 ten eleven 12 xiii fourteen 15 16 17 eighteen 19 xx 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 sixty 61 62 63 64 65 66 67 68 69 70 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 | import java.awt.*; import java.awt.upshot.*; import javax.swing.*; public class GameMain extends JPanel { private static terminal long serialVersionUID = 1L; public static final String TITLE = "Tic Tac Toe"; public static final Color COLOR_BG = Colour.WHITE; public static terminal Colour COLOR_BG_STATUS = new Colour(216, 216, 216); public static final Color COLOR_CROSS = new Color(239, 105, eighty); public static terminal Colour COLOR_NOUGHT = new Color(64, 154, 225); public static terminal Font FONT_STATUS = new Font("OCR A Extended", Font.Apparently, 14); private Lath board; private State currentState; private Seed currentPlayer; private JLabel statusBar; public GameMain() { super.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { int mouseX = e.getX(); int mouseY = east.getY(); int row = mouseY / Cell.SIZE; int col = mouseX / Cell.SIZE; if (currentState == Land.PLAYING) { if (row >= 0 && row < Board.ROWS && col >= 0 && col < Board.COLS && board.cells[row][col].content == Seed.NO_SEED) { currentState = board.moveAndGetState(currentPlayer, row, col); currentState.playSoundClip(); currentPlayer = (currentPlayer == Seed.CROSS) ? Seed.NOUGHT : Seed.CROSS; } } else { initGame(); } repaint(); } }); statusBar = new JLabel(" "); statusBar.setBackground(COLOR_BG_STATUS); statusBar.setFont(FONT_STATUS); statusBar.setPreferredSize(new Dimension(300, 30)); statusBar.setBorder(BorderFactory.createEmptyBorder(5, x, 5, 12)); statusBar.setOpaque(true); statusBar.setHorizontalAlignment(JLabel.LEFT); super.setLayout(new BorderLayout()); super.add together(statusBar, BorderLayout.PAGE_END); super.setPreferredSize(new Dimension(Board.CANVAS_WIDTH, Lath.CANVAS_HEIGHT + 30)); super.setBorder(BorderFactory.createLineBorder(COLOR_BG_STATUS, 2, simulated)); board = new Lath(); initGame(); } public void initGame() { for (int row = 0; row < Board.ROWS; ++row) { for (int col = 0; col < Board.COLS; ++col) { board.cells[row][col].content = Seed.NO_SEED; } } currentPlayer = Seed.CROSS; currentState = State.PLAYING; } @Override public void paintComponent(Graphics yard) { super.paintComponent(g); setBackground(COLOR_BG); board.paint(yard); String playerName = currentPlayer.getDisplayName(); if (currentState == State.PLAYING) { statusBar.setForeground(Color.BLACK); statusBar.setText(playerName + "'due south Turn"); } else if (currentState == Country.DRAW) { statusBar.setForeground(Color.Reddish); statusBar.setText("It's a Draw! Click to play again."); } else if (currentState == State.CROSS_WON) { statusBar.setForeground(Color.RED); statusBar.setText("'Ten' Won! Click to play over again."); } else if (currentState == Land.NOUGHT_WON) { statusBar.setForeground(Color.Carmine); 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(truthful); } }); } } |
Dissecting the program
[TODO]
Game Programming Assignment
You can use the higher up Tic-tac-toe as a template to develop lath games such as Connect-4 and Othello.
Connect-4
Wiki "Connect-four" to understand the rules of the game.
To write a Connect-Iv game, allow'southward starting time from Tic-Tac-Toe's "Graphics Version". Do the post-obit changes on "TTTGraphics.java
":
- Change constants
ROWS
to 6 andCOLS
to 7. Run the programme. Y'all shall see a 6×vii filigree. Try clicking on the cells, "cross" and "nought" shall exist displayed alternately. - Modify the
mouseClicked()
outcome-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 jail cell on that cavalcade.if (colSelected >= 0 && colSelected < COLS) { for (int row = ROWS -1; row >= 0; row--) { if (lath[row][colSelected] == Seed.EMPTY) { board[row][colSelected] = currentPlayer; updateGame(currentPlayer, row, colSelected); currentPlayer = (currentPlayer == Seed.Cantankerous) ? Seed.NOUGHT : Seed.Cross; break; } } }
- Change the
hasWon()
method to check for four-in-a-line (forth 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 (lath[rowSelected][col] == theSeed) { ++count; if (count == iv) return truthful; } else { count = 0; } } ...... render fake; }
That's all!
Next,
- Tidy up the names (In Eclipse, Refactor ⇒ Rename).
- Tidy up the display (using ruby and yellowish discs, instead of cross and nought).
- Add more features. For case, sound outcome; or buttons to control the game.
- Re-design your classes (Read the "Graphics Avant-garde-OO Tic-Tac-Toe").
- Improve your display (e.one thousand., using images, animation etc).
Othello (Reversi)
Wiki "Othello" or "Reversi" to understand the rules of the game.
Modify the above Tic-Tac-Toe ("TTTGraphics.java
"):
- Change
ROWS
andCOLS
to 8. Run the program. You shall meet a 8×8 grid. Endeavor 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)
- after the player with "theSeed
" has placed on(rowSelected, colSelected)
. If there is no more than 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.Blackness; int col, row; col = colSelected + i;
while (col < COLS - 1 && board[rowSelected][col] == opponentSeed) { ++col; } if (col <= COLS - 1 && board[rowSelected][col] == mySeed) { for (int colFlip = colSelected + 1; colFlip <= col - 1; ++colFlip) { board[rowSelected][colFlip] = mySeed; } } ...... ...... - Remove
isDraw()
andhasWon()
.
Next,
- Tidy up the names (Refactor ⇒ Rename).
- Tidy up the display (using black and white discs, instead of cross and nought).
- Add more features. For example, sound consequence; or buttons to command the game.
- Re-design your classes (Read the "Graphics Advanced-OO Tic-Tac-Toe").
- Ameliorate your display (e.g., using images, animation etc).
Sudoku
Encounter the "Sudoku" commodity.
Mine Sweeper
Run across the "Mine Sweeper" article.
MasterMind
[TODO]
Checker
[TODO]
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" folder).
A much more than efficient method for matching with a winning pattern in a Tic-tac-toe is to use a 9-flake binary number (stored as an int
or short
type) to denote the placement of the seeds, and use fleck operations to perform the matching.
The post-obit tabular array summaries all the bit-wise operations, which are efficient and fast.
Operator | Clarification | Usage | Example |
---|---|---|---|
& | Flake-wise AND | expr1 & expr2 | 0b0110 0001 & Ob1110 0000 gives 0b0110 0000 |
| | Bit-wise OR | expr1 | expr2 | 0b0110 0001 | Ob0000 chiliad gives 0b0110 1001 |
! | Bit-wise NOT | !expr | ^0b0110 0001 gives 0b1001 1110 |
^ | Chip-wise XOR | expr1 ^ expr2 | 0b0110 0001 ^ Ob0000 0001 gives 0b0110 1001 |
<< | Left-shift and padded with zeros | operand << number | 0b0000 0001 << 4 gives 0b0001 0000 |
>> | Correct-shift and padded with the "sign-bit" (Signed-extended right-shift) | operand >> number | 0b1000 0001 >> 2 gives 0b1110 0000 |
>>> | Right-shift and padded with zeros (Unsigned-extended right-shift) | operand >>> number | 0b1000 0001 >>> ii gives 0b0010 0000 |
Nosotros can keep the 8 winning patterns in an int
array as follows:
int[] winningPatterns = { 0x1c0, 0x038, 0x007, 0x124, 0x092, 0x049, 0x111, 0x054};
Annotation: 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 but after Eclipse iii.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 crossPattern; int noughtPattern; int bitPosition = rowSelected * ROWS + colSelected; if (currentPlayer == Seed.Cantankerous) { 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 every bit to identify a '1'
bit at the proper position. It is and then bit-OR with the existing blueprint to include the new bit, without modifying the existing $.25. For instance, suppose rowSelect = 2
and colSelected = 0
, then bitPosition = vi
. (0x1 << bitPosition)
gives 0b 001 000 000
.
To lucifer with the winning patterns:
public boolean hasWon(Seed theSeed) { int playerPattern = (theSeed == Seed.Cantankerous) ? crossPattern : noughtPattern; for (int aWinningPattern : winningPatterns) { if ((aWinningPattern & playerPattern) == aWinningPattern) { return true; } } return false; }
(aWinningPattern & playerPattern)
masks out all the bits in the playerPattern
except those having i's in aWinningPattern
. For case, 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 than efficient as it involves only comparison with viii integers (plus 8 efficient bit-AND operations).
Other Modes of Operation
WebStart Application
[TODO]
Playing Over the Cyberspace
[TODO]
Playing Against Calculator with AI (Advanced)
Read "Case Report on Tic-Tac-Toe Part two: With AI".
REFERENCES & RESOURCES
- JDK Applets demo "TicTacToe" (under JDK demo applets binder).
Source: https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaGame_TicTacToe.html
Postar um comentário for "Tic Tac Toe X Code Play Again"