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