From 31bc0be3dbd208901cb0511d09362097c5ac9c29 Mon Sep 17 00:00:00 2001 From: "Bradley M. Small" <bradley_small@hotamil.com> Date: Wed, 18 Aug 2021 09:25:59 -0400 Subject: [PATCH] beginning separation of logic from presentation --- src/main/java/com/small/tictactoe/App.java | 43 ++++++ .../com/small/tictactoe/GameBoardPanel.java | 127 ++++++++++++++++++ .../java/com/small/tictactoe/GameTile.java | 51 +++++++ .../java/com/small/tictactoe/TicTacToe.java | 16 +-- .../com/small/tictactoe/TicTacToeGame.java | 57 ++++++++ .../tictactoe/TicTacToe_Specification.groovy | 82 +++++------ 6 files changed, 316 insertions(+), 60 deletions(-) create mode 100644 src/main/java/com/small/tictactoe/App.java create mode 100644 src/main/java/com/small/tictactoe/GameBoardPanel.java create mode 100644 src/main/java/com/small/tictactoe/GameTile.java create mode 100644 src/main/java/com/small/tictactoe/TicTacToeGame.java diff --git a/src/main/java/com/small/tictactoe/App.java b/src/main/java/com/small/tictactoe/App.java new file mode 100644 index 0000000..cf2fb3e --- /dev/null +++ b/src/main/java/com/small/tictactoe/App.java @@ -0,0 +1,43 @@ +package com.small.tictactoe; + +import javax.swing.*; + +public class App extends JFrame { + private final GameBoardPanel gameBoardPanel = new GameBoardPanel(); + App() { + initGUI(); + } + + private void initGUI() { + setTitle("Tic-Tac-Toe"); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_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); + pack(); + setSize(600, 600); + setLocationRelativeTo(null); + setVisible(true); + } + private void newGame() { + gameBoardPanel.newGame(); + } + private void getScore() { + TicTacToeGame game = new TicTacToeGame(); + game.setBoard(gameBoardPanel.getBoard()); + System.out.println(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 new file mode 100644 index 0000000..ea0b972 --- /dev/null +++ b/src/main/java/com/small/tictactoe/GameBoardPanel.java @@ -0,0 +1,127 @@ +package com.small.tictactoe; + +import javax.swing.*; +import java.awt.*; +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'; + + GameBoardPanel() { + 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); + } + + public void newGame() { + nextPiece = 'x'; + + for (int row = 0; row < 3; ++row) { + for (int column = 0; column < 3; ++column) { + placePiece(' ', row, column); + } + } + 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); + + drawCrossHatch(g); + } + + private void drawCrossHatch(Graphics g) { + g.setColor(Color.BLUE); + g.fillRect((getWidth()) / 3 - 10, 0 + 20, 20, getHeight() - 40); + g.fillRect((getWidth()) / 3 * 2 - 10, 0 + 20, 20, getHeight() - 40); + + g.fillRect(0 + 20, (getHeight()) / 3 - 10, getWidth() - 40, 20); + g.fillRect(0 + 20, (getHeight()) / 3 * 2 - 10, getWidth() - 40, 20); + } + + void initGUI() { + GridBagLayout gbl = new GridBagLayout(); + setBackground(Color.BLACK); + setLayout(gbl); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + + gridBagConstraints.fill = GridBagConstraints.BOTH; + gridBagConstraints.insets = new Insets(30, 30, 30, 30); + + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 1; + gridBagConstraints.gridheight = 1; + gridBagConstraints.weightx = gridBagConstraints.weighty = 1.0; + for (int column = 0; column < 3; ++column) { + for (int row = 0; row < 3; ++row) { + gridBagConstraints.gridx = column; + gridBagConstraints.gridy = row; + gameTiles[row][column] = new GameTile(); + gameTiles[row][column].addMouseListener(this); + add(gameTiles[row][column], gridBagConstraints); + + } + } + } + + @Override + public void mouseClicked(MouseEvent e) { + GameTile tile = (GameTile) e.getSource(); + + if (e.getButton() == 1) { + if (tile.getCurrentValue() == ' ') { + tile.setCurrentValue(getNextPiece()); + } + } else { + tile.setCurrentValue(' '); + } + repaint(); + } + + @Override + public void mousePressed(MouseEvent e) { + // ignoring this action + } + + @Override + public void mouseReleased(MouseEvent e) { + // ignoring this action + } + + @Override + public void mouseEntered(MouseEvent e) { + // ignoring this action + } + + @Override + public void mouseExited(MouseEvent e) { + // ignoring this action + } +} diff --git a/src/main/java/com/small/tictactoe/GameTile.java b/src/main/java/com/small/tictactoe/GameTile.java new file mode 100644 index 0000000..25add29 --- /dev/null +++ b/src/main/java/com/small/tictactoe/GameTile.java @@ -0,0 +1,51 @@ +package com.small.tictactoe; + +import javax.swing.*; +import java.awt.*; + +public class GameTile extends JPanel { + private char currentValue = ' '; + + @Override + protected void paintComponent(Graphics g) { + if (getCurrentValue() == 'o') { + drawNaught(g); + } else if (getCurrentValue() == 'x') { + drawCross(g); + } else { + clearBackground(g); + } + } + + private void clearBackground(Graphics g) { + g.setColor(Color.BLACK); + 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.fillPolygon(new Polygon(x, y, 4)); + 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); + } + + public char getCurrentValue() { + return currentValue; + } + + public void setCurrentValue(char currentValue) { + this.currentValue = currentValue; + } + +} diff --git a/src/main/java/com/small/tictactoe/TicTacToe.java b/src/main/java/com/small/tictactoe/TicTacToe.java index da6e023..9e15ca8 100644 --- a/src/main/java/com/small/tictactoe/TicTacToe.java +++ b/src/main/java/com/small/tictactoe/TicTacToe.java @@ -16,9 +16,14 @@ public class TicTacToe { score = ""; } - private void placePiece(char xOrO, int row, int column) { + 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; @@ -38,15 +43,6 @@ public class TicTacToe { return Arrays.stream(row).allMatch(e->e=='O') || Arrays.stream(row).allMatch(e->e=='X'); } - public void play(int[][] plays) { - newGame(); - for (int row = 0; row < 3; ++row){ - for (int col = 0; col < 3; ++col){ - placePiece(plays[row][col] == 0 ? 'O' : 'X', row, col); - } - } - } - private boolean winByCrisCross() { if (allSame(board[0][0], board[1][1], board[2][2]) || allSame(board[0][2], board[1][1], board[2][0])) { diff --git a/src/main/java/com/small/tictactoe/TicTacToeGame.java b/src/main/java/com/small/tictactoe/TicTacToeGame.java new file mode 100644 index 0000000..8cfbce8 --- /dev/null +++ b/src/main/java/com/small/tictactoe/TicTacToeGame.java @@ -0,0 +1,57 @@ +package com.small.tictactoe; + +import java.util.Arrays; + +public class TicTacToeGame { + public static final String PLAYER_S_WINS = "Player %s wins."; + private Character [][] board; + private String score; + + 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; + } + + public void setBoard(Character[][] board) { + this.board = board; + } +} \ No newline at end of file diff --git a/src/test/groovy/com/small/tictactoe/TicTacToe_Specification.groovy b/src/test/groovy/com/small/tictactoe/TicTacToe_Specification.groovy index 770623d..ee8b588 100644 --- a/src/test/groovy/com/small/tictactoe/TicTacToe_Specification.groovy +++ b/src/test/groovy/com/small/tictactoe/TicTacToe_Specification.groovy @@ -3,72 +3,54 @@ package com.small.tictactoe import spock.lang.Specification class TicTacToe_Specification extends Specification { - def "should show empty "() { - expect: - "Tie Game" == new TicTacToe().getScore() + def game = new TicTacToe() + void setup() { + game = new TicTacToe() } def "should show tie "() { - TicTacToe game = new TicTacToe(); - int[][] plays = [[0, 1, 0], - [1, 0, 1], - [1, 0, 1]]; - game.play(plays); expect: "Tie Game" == game.getScore() } - def "should show player O win "() { - TicTacToe game = new TicTacToe(); - int[][] plays = [[0, 0, 0], - [1, 1, 0], - [1, 0, 1]]; - game.play(plays); + + 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 "() { - TicTacToe game = new TicTacToe(); - int[][] plays = [[0, 1, 0], - [1, 1, 1], - [0, 0, 1]]; - game.play(plays); - expect: - "Player X wins." == game.getScore() - } + def "should show player X win vertically"() { - TicTacToe game = new TicTacToe(); - int[][] plays = [[1, 1, 0], - [1, 0, 1], - [1, 0, 0]]; - game.play(plays); + 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 vertically"() { - TicTacToe game = new TicTacToe(); - int[][] plays = [[1, 0, 0], - [0, 0, 1], - [1, 0, 1]]; - game.play(plays); - expect: - "Player O wins." == game.getScore() - } + def "should show player O win criss-cross"() { - TicTacToe game = new TicTacToe(); - int[][] plays = [[0, 0, 1], - [1, 0, 1], - [1, 0, 0]]; - game.play(plays); + 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 show player X win criss-cross"() { - TicTacToe game = new TicTacToe(); - int[][] plays = [[0, 0, 1], - [0, 1, 0], - [1, 0, 1]]; - game.play(plays); - expect: - "Player X 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 -- GitLab