diff --git a/src/main/java/com/small/tictactoe/App.java b/src/main/java/com/small/tictactoe/App.java index cf2fb3ebf6dae5cf53080ef5e2c4454a07754818..fb8697e1594b829ab1f4523811d0a8766a2bd0bf 100644 --- a/src/main/java/com/small/tictactoe/App.java +++ b/src/main/java/com/small/tictactoe/App.java @@ -1,43 +1,51 @@ package com.small.tictactoe; import javax.swing.*; +import java.awt.*; public class App extends JFrame { - private final GameBoardPanel gameBoardPanel = new GameBoardPanel(); + private transient TicTacToeGamePlayer player = new TicTacToeGame(); + private final GameBoardPanel gameBoardPanel = new GameBoardPanel(player); + private final JTextArea textScore = new JTextArea(); + App() { initGUI(); } + public static void main(String[] args) { + new App(); + } + private void initGUI() { setTitle("Tic-Tac-Toe"); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); + setLayout(new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS)); add(gameBoardPanel); JPanel buttonPanel = new JPanel(); JButton buttonNewGame = new JButton("New Game"); buttonNewGame.addActionListener(e -> newGame()); - JButton buttonGetScore = new JButton ("Get Score"); - buttonGetScore.addActionListener(e -> getScore()); buttonPanel.add(buttonNewGame); - buttonPanel.add(buttonGetScore); + add(buttonPanel); + Dimension d = new Dimension(600, 100); + textScore.setSize(d); + textScore.setMaximumSize(d); + textScore.setMinimumSize(d); + add(textScore); pack(); - setSize(600, 600); + setSize(600, 700); setLocationRelativeTo(null); setVisible(true); } + private void newGame() { gameBoardPanel.newGame(); + textScore.setText(""); } - private void getScore() { - TicTacToeGame game = new TicTacToeGame(); - game.setBoard(gameBoardPanel.getBoard()); - System.out.println(game.getScore()); + public void getScore() { + TicTacToeGame game = (TicTacToeGame) player; + textScore.setText(game.getScore()); } - public static void main(String[] args) { - new App(); - } - } diff --git a/src/main/java/com/small/tictactoe/GameBoardPanel.java b/src/main/java/com/small/tictactoe/GameBoardPanel.java index ea0b9724e71bd80d29ae518ad33a20c7019a64fd..0ef5ddf366f5742c611b8f1976c2fe472e60055f 100644 --- a/src/main/java/com/small/tictactoe/GameBoardPanel.java +++ b/src/main/java/com/small/tictactoe/GameBoardPanel.java @@ -6,49 +6,29 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; public class GameBoardPanel extends JPanel implements MouseListener { - Character [][] board; private final GameTile[][] gameTiles = new GameTile[3][3]; - private char nextPiece = 'x'; + private final transient TicTacToeGamePlayer player; - GameBoardPanel() { + GameBoardPanel(TicTacToeGamePlayer player) { + this.player = player; initGUI(); - } - Character[][] getBoard() { - Character [][] board = new Character[3][3]; - for (int row = 0; row < 3; ++row) { - for (int column = 0; column < 3; ++column) { - board[row][column] = gameTiles[row][column].getCurrentValue(); - } - } - return board; - } - public void placePiece(char xOrO, int row, int col) { - gameTiles[row][col].setCurrentValue(xOrO); + newGame(); } public void newGame() { - nextPiece = 'x'; + player.newGame(); + clearTable(); + repaint(); + } + private void clearTable() { for (int row = 0; row < 3; ++row) { for (int column = 0; column < 3; ++column) { - placePiece(' ', row, column); + gameTiles[row][column].setCurrentValue(' '); } } - repaint(); - } - - public char getNextPiece() { - char returnValue = nextPiece; - - if (nextPiece == 'x') { - nextPiece = 'o'; - } else if (nextPiece == 'o') { - nextPiece = 'x'; - } - return returnValue; } - @Override protected void paintComponent(Graphics g) { super.paintComponent(g); @@ -65,10 +45,9 @@ public class GameBoardPanel extends JPanel implements MouseListener { g.fillRect(0 + 20, (getHeight()) / 3 * 2 - 10, getWidth() - 40, 20); } - void initGUI() { - GridBagLayout gbl = new GridBagLayout(); + private void initGUI() { setBackground(Color.BLACK); - setLayout(gbl); + setLayout(new GridBagLayout()); GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.fill = GridBagConstraints.BOTH; @@ -86,23 +65,37 @@ public class GameBoardPanel extends JPanel implements MouseListener { gameTiles[row][column] = new GameTile(); gameTiles[row][column].addMouseListener(this); add(gameTiles[row][column], gridBagConstraints); - } } } + private void playSquare(int row, int column) { + Character xOrO = player.playSquare(row, column); + if (xOrO == null) { + return; + } + if (xOrO == 'x' || xOrO == 'o' || xOrO == ' ') { + gameTiles[row][column].setCurrentValue(xOrO); + repaint(); + + App app = (App)getRootPane().getParent(); + + app.getScore(); + } + } + @Override public void mouseClicked(MouseEvent e) { - GameTile tile = (GameTile) e.getSource(); - if (e.getButton() == 1) { - if (tile.getCurrentValue() == ' ') { - tile.setCurrentValue(getNextPiece()); + GameTile tile = (GameTile) e.getSource(); + for (int row = 0; row < 3; ++row) { + for (int column = 0; column < 3; ++column) { + if (gameTiles[row][column] == tile) { + playSquare(row, column); + } + } } - } else { - tile.setCurrentValue(' '); } - repaint(); } @Override diff --git a/src/main/java/com/small/tictactoe/GameTile.java b/src/main/java/com/small/tictactoe/GameTile.java index 25add2921090812e7cf12f67ee67ebdbb24ca6a1..1612c550e65a1c9897ca42c9a3d58c2f871bfb3a 100644 --- a/src/main/java/com/small/tictactoe/GameTile.java +++ b/src/main/java/com/small/tictactoe/GameTile.java @@ -19,25 +19,25 @@ public class GameTile extends JPanel { private void clearBackground(Graphics g) { g.setColor(Color.BLACK); - g.fillRect(0,0, getWidth(), getHeight()); + g.fillRect(0, 0, getWidth(), getHeight()); repaint(); } private void drawCross(Graphics g) { - g.setColor(Color.RED ); - int [] x = {10,30,getWidth()-10,getWidth()-30}; - int [] y = {30,10,getHeight() - 30, getHeight()-10}; + g.setColor(Color.RED); + int[] x = {10, 30, getWidth() - 10, getWidth() - 30}; + int[] y = {30, 10, getHeight() - 30, getHeight() - 10}; g.fillPolygon(new Polygon(x, y, 4)); - int [] x1 = {10,30,getWidth()-10,getWidth()-30}; - int [] y1 = {getHeight()-30, getHeight()-10, 30,10}; + int[] x1 = {10, 30, getWidth() - 10, getWidth() - 30}; + int[] y1 = {getHeight() - 30, getHeight() - 10, 30, 10}; g.fillPolygon(new Polygon(x1, y1, 4)); } private void drawNaught(Graphics g) { - g.setColor(Color.GREEN ); - g.fillOval(0,0, getWidth(), getHeight()); - g.setColor(Color.BLACK ); - g.fillOval(20,20, getWidth() - 40, getHeight()- 40); + g.setColor(Color.GREEN); + g.fillOval(0, 0, getWidth(), getHeight()); + g.setColor(Color.BLACK); + g.fillOval(20, 20, getWidth() - 40, getHeight() - 40); } public char getCurrentValue() { diff --git a/src/main/java/com/small/tictactoe/TicTacToe.java b/src/main/java/com/small/tictactoe/TicTacToe.java deleted file mode 100644 index 9e15ca8074d89d5a8a8f879cb7395b60eec46e2d..0000000000000000000000000000000000000000 --- a/src/main/java/com/small/tictactoe/TicTacToe.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.small.tictactoe; - -import java.util.Arrays; - -public class TicTacToe { - public static final String PLAYER_S_WINS = "Player %s wins."; - private final Character [][] board = {{' ',' ',' '},{' ',' ',' '},{' ',' ',' '}}; - private String score = ""; - - public void newGame() { - for (int row = 0; row < 3; ++row) { - for (int column=0; column < 3 ; ++column){ - board[row][column] = ' '; - } - } - - score = ""; - } - public void placePiece(char xOrO, int row, int column) { - board[row][column] = xOrO; - } - void showBoard() { - System.out.println(Arrays.deepToString(board[0])); - System.out.println(Arrays.deepToString(board[1])); - System.out.println(Arrays.deepToString(board[2])); - } - public String getScore() { - for (int i = 0; i < 3; ++i) { - if (winByRow(i)) return score; - } - for (int i = 0; i < 3; ++i){ - if (winByColumn(i)) return score; - } - if (winByCrisCross()) { - return score; - } - - score = "Tie Game"; - return score; - } - - private boolean allSame(Character... row) { - return Arrays.stream(row).allMatch(e->e=='O') || Arrays.stream(row).allMatch(e->e=='X'); - } - - private boolean winByCrisCross() { - if (allSame(board[0][0], board[1][1], board[2][2]) || - allSame(board[0][2], board[1][1], board[2][0])) { - score = String.format(PLAYER_S_WINS, board[1][1]); - return true; - } - return false; - } - - private boolean winByColumn(int column) { - if (allSame(board[0][column], board[1][column], board[2][column])) { - score = String.format(PLAYER_S_WINS, board[0][column]); - return true; - } - return false; - } - - private boolean winByRow(int row) { - if (allSame(board[row])) { - score = String.format(PLAYER_S_WINS, board[row][0]); - return true; - } - return false; - } -} diff --git a/src/main/java/com/small/tictactoe/TicTacToeGame.java b/src/main/java/com/small/tictactoe/TicTacToeGame.java index 8cfbce8c8a7dd4c4754fafa357e0eb56c6a0c273..4edeecb93f0a47bf57205aeebaa196af4a1ef2ef 100644 --- a/src/main/java/com/small/tictactoe/TicTacToeGame.java +++ b/src/main/java/com/small/tictactoe/TicTacToeGame.java @@ -2,42 +2,115 @@ package com.small.tictactoe; import java.util.Arrays; -public class TicTacToeGame { +public class TicTacToeGame implements TicTacToeGamePlayer { public static final String PLAYER_S_WINS = "Player %s wins."; - private Character [][] board; - private String score; + private Character[][] board; + private Character nextCharacter = 'x'; + private Character winPiece = ' '; + private Character winDirection = ' '; + private int winRow = -1; + private int winColumn = -1; + + public TicTacToeGame() { + board = new Character[3][3]; + newGame(); + } + + private void resetBoardValues() { + for (int row = 0; row < 3; ++row) { + for (int column = 0; column < 3; ++column) { + board[row][column] = ' '; + } + } + } + + private void resetGameState() { + nextCharacter = 'x'; + winPiece = ' '; + winDirection = ' '; + winRow = -1; + winColumn = -1; + } public String getScore() { + if (winPiece != ' ') { + return String.format(PLAYER_S_WINS, winPiece); + } + if (noneLeft()) { + return "Tie Game"; + } + return ""; + } + + private void updateGameState() { for (int i = 0; i < 3; ++i) { - if (winByRow(i)) return score; + if (winByRow(i)) { + return; + } } - for (int i = 0; i < 3; ++i){ - if (winByColumn(i)) return score; + for (int i = 0; i < 3; ++i) { + if (winByColumn(i)) { + return; + } } - if (winByCrisCross()) { - return score; + winByCrisCross(); + } + + @Override + public void newGame() { + resetBoardValues(); + resetGameState(); + } + + @Override + public Character playSquare(int row, int column) { + if (winPiece == ' ' && board[row][column] == ' ') { + board[row][column] = getNextPiece(); + updateGameState(); + return board[row][column]; } + return null; + } - score = "Tie Game"; - return score; + @Override + public Character getNextPiece() { + Character currentCharacter = nextCharacter; + nextCharacter = nextCharacter == 'x' ? 'o' : 'x'; + return currentCharacter; } private boolean allSame(Character... row) { - return Arrays.stream(row).allMatch(e->e=='o') || Arrays.stream(row).allMatch(e->e=='x'); + return Arrays.stream(row).allMatch(e -> e == 'o') || Arrays.stream(row).allMatch(e -> e == 'x'); } + private boolean noneLeft() { + return Arrays.stream(board[0]).noneMatch(e -> e == ' ') && + Arrays.stream(board[1]).noneMatch(e -> e == ' ') && + Arrays.stream(board[2]).noneMatch(e -> e == ' '); + } private boolean winByCrisCross() { - if (allSame(board[0][0], board[1][1], board[2][2]) || - allSame(board[0][2], board[1][1], board[2][0])) { - score = String.format(PLAYER_S_WINS, board[1][1]); - return true; + boolean returnValue = false; + if (allSame(board[0][0], board[1][1], board[2][2])) { + winColumn = 0; + returnValue = true; + } else if (allSame(board[0][2], board[1][1], board[2][0])) { + winColumn = 2; + returnValue = true; } - return false; + if (returnValue) { + winDirection = 'd'; + winRow = 0; + winPiece = board[winRow][winColumn]; + } + return returnValue; } private boolean winByColumn(int column) { if (allSame(board[0][column], board[1][column], board[2][column])) { - score = String.format(PLAYER_S_WINS, board[0][column]); + winDirection = 'c'; + winRow = 0; + winColumn = column; + winPiece = board[winRow][winColumn]; return true; } return false; @@ -45,7 +118,10 @@ public class TicTacToeGame { private boolean winByRow(int row) { if (allSame(board[row])) { - score = String.format(PLAYER_S_WINS, board[row][0]); + winDirection = 'r'; + winRow = row; + winColumn = 0; + winPiece = board[winRow][winColumn]; return true; } return false; @@ -53,5 +129,6 @@ public class TicTacToeGame { public void setBoard(Character[][] board) { this.board = board; + updateGameState(); } } \ No newline at end of file diff --git a/src/main/java/com/small/tictactoe/TicTacToeGamePlayer.java b/src/main/java/com/small/tictactoe/TicTacToeGamePlayer.java new file mode 100644 index 0000000000000000000000000000000000000000..677f53f14520776a389a02cc13609837744fe97d --- /dev/null +++ b/src/main/java/com/small/tictactoe/TicTacToeGamePlayer.java @@ -0,0 +1,9 @@ +package com.small.tictactoe; + +public interface TicTacToeGamePlayer { + Character getNextPiece(); + + Character playSquare(int row, int column); + + void newGame(); +} diff --git a/src/test/groovy/com/small/tictactoe/TicTacToeGame_Specification.groovy b/src/test/groovy/com/small/tictactoe/TicTacToeGame_Specification.groovy new file mode 100644 index 0000000000000000000000000000000000000000..630332068ddaa5ce1126f1d7781689179308d475 --- /dev/null +++ b/src/test/groovy/com/small/tictactoe/TicTacToeGame_Specification.groovy @@ -0,0 +1,56 @@ +package com.small.tictactoe + +import spock.lang.Specification + +class TicTacToeGame_Specification extends Specification { + + def game = new TicTacToeGame() + + void setup() { + game = new TicTacToeGame() + } + def "should show tie game when no play has happened"() { + expect: + "Tie Game" == game.getScore() + + } + + def "should show tie game when new game"() { + game.playSquare(0, 0) + game.playSquare(0, 1) + game.playSquare(0, 2) + game.newGame() + expect: + game.getScore() == "Tie Game" + } + + def "should show player x wins "() { + game.playSquare(0, 0) + game.playSquare(2, 0) + game.playSquare(0, 1) + game.playSquare(2, 1) + game.playSquare(0, 2) + expect: + game.getScore() == "Player x wins." + } + + def "should show that play begins with x and swaps to o each call"() { + expect: + (char)'x' == game.getNextPiece() + (char)'o' == game.getNextPiece() + (char)'x' == game.getNextPiece() + (char)'o' == game.getNextPiece() + (char)'x' == game.getNextPiece() + (char)'o' == game.getNextPiece() + } + + def "should show player x wins when importing a winning board"() { + Character [][] board = new Character[3][3] + board =[['x' as char,'x' as char, 'x' as char], + [' ' as char,' ' as char, ' ' as char], + [' ' as char,' ' as char, ' ' as char]] + game.setBoard(board) + expect: + "Player x wins." == game.getScore() + } +} diff --git a/src/test/groovy/com/small/tictactoe/TicTacToe_Specification.groovy b/src/test/groovy/com/small/tictactoe/TicTacToe_Specification.groovy deleted file mode 100644 index ee8b588576d37f46775cabcd8a543df419f1ceb4..0000000000000000000000000000000000000000 --- a/src/test/groovy/com/small/tictactoe/TicTacToe_Specification.groovy +++ /dev/null @@ -1,56 +0,0 @@ -package com.small.tictactoe - -import spock.lang.Specification - -class TicTacToe_Specification extends Specification { - def game = new TicTacToe() - void setup() { - game = new TicTacToe() - } - - def "should show tie "() { - expect: - "Tie Game" == game.getScore() - } - - def "should show player O win horizontally"() { - game.placePiece('O' as char, 0, 0) - game.placePiece('O' as char, 0, 1) - game.placePiece('O' as char, 0, 2) - expect: - "Player O wins." == game.getScore() - } - - def "should show player X win vertically"() { - game.placePiece('X' as char, 0, 0) - game.placePiece('X' as char, 1, 0) - game.placePiece('X' as char, 2, 0) - expect: - "Player X wins." == game.getScore() - } - - def "should show player O win criss-cross"() { - game.placePiece('O' as char, 0, 0) - game.placePiece('O' as char, 1, 1) - game.placePiece('O' as char, 2, 2) - expect: - "Player O wins." == game.getScore() - } - - def "should play the game"() { - game.newGame() - game.showBoard() - game.placePiece('X' as char, 0,0) - game.showBoard() - game.placePiece('O' as char, 1,1) - game.showBoard() - game.placePiece('X' as char, 0,2) - game.placePiece('O' as char, 0,1) - game.placePiece('X' as char, 2,1) - game.placePiece('O' as char, 2,2) - game.showBoard() - println(game.getScore()) - expect: - true - } -} \ No newline at end of file