From d23b54e22a344c1242d577869668728d86af5aeb Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 13:38:43 +0200 Subject: [PATCH 01/17] import and rename for usage in intellij --- .idea/.gitignore | 8 ++ .idea/misc.xml | 6 + .idea/modules.xml | 9 ++ .idea/uiDesigner.xml | 124 ++++++++++++++++++ .idea/vcs.xml | 6 + CodeKatas_20240507_CodeSmells.iml | 28 ++++ java/test/{Game_Should.java => GameTest.java} | 2 +- 7 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 CodeKatas_20240507_CodeSmells.iml rename java/test/{Game_Should.java => GameTest.java} (99%) diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..69ace3f --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..ce31086 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CodeKatas_20240507_CodeSmells.iml b/CodeKatas_20240507_CodeSmells.iml new file mode 100644 index 0000000..1d21cfd --- /dev/null +++ b/CodeKatas_20240507_CodeSmells.iml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java/test/Game_Should.java b/java/test/GameTest.java similarity index 99% rename from java/test/Game_Should.java rename to java/test/GameTest.java index 22afb59..51816fb 100644 --- a/java/test/Game_Should.java +++ b/java/test/GameTest.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -public class Game_Should { +public class GameTest { private Game game; @BeforeEach From 99219431c95198a7db7c53129f290443eab302e7 Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 13:40:36 +0200 Subject: [PATCH 02/17] rename attributes, reformat code --- java/src/Board.java | 42 ++++++++++++---------------- java/src/Game.java | 61 ++++++++++++++++++++--------------------- java/src/Tile.java | 12 ++++---- java/test/GameTest.java | 26 +++++++----------- 4 files changed, 63 insertions(+), 78 deletions(-) diff --git a/java/src/Board.java b/java/src/Board.java index 014a39d..9a3aa7c 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -1,42 +1,36 @@ import java.util.ArrayList; import java.util.List; -public class Board -{ - private List _plays = new ArrayList<>(); +public class Board { + private List plays = new ArrayList<>(); - public Board() - { - for (int i = 0; i < 3; i++) - { - for (int j = 0; j < 3; j++) - { + public Board() { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { Tile tile = new Tile(); - tile.X = i; - tile.Y = j; - tile.Symbol = ' '; - _plays.add(tile); + tile.x = i; + tile.y = j; + tile.symbol = ' '; + plays.add(tile); } } } - public Tile TileAt(int x, int y) - { - for (Tile t : _plays) { - if (t.X == x && t.Y == y){ + public Tile TileAt(int x, int y) { + for (Tile t : plays) { + if (t.x == x && t.y == y) { return t; } } return null; } - public void AddTileAt(char symbol, int x, int y) - { + public void AddTileAt(char symbol, int x, int y) { Tile newTile = new Tile(); - newTile.X = x; - newTile.Y = y; - newTile.Symbol = symbol; + newTile.x = x; + newTile.y = y; + newTile.symbol = symbol; - TileAt(x,y).Symbol = symbol; + TileAt(x, y).symbol = symbol; } -} \ No newline at end of file +} diff --git a/java/src/Game.java b/java/src/Game.java index bf4c95d..1ec832b 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -1,69 +1,68 @@ public class Game { - private char _lastSymbol = ' '; - private Board _board = new Board(); + private char lastSymbol = ' '; + private Board board = new Board(); public void Play(char symbol, int x, int y) throws Exception { //if first move - if (_lastSymbol == ' ') { + if (lastSymbol == ' ') { //if player is X if (symbol == 'O') { throw new Exception("Invalid first player"); } } //if not first move but player repeated - else if (symbol == _lastSymbol) { + else if (symbol == lastSymbol) { throw new Exception("Invalid next player"); } //if not first move but play on an already played tile - else if (_board.TileAt(x, y).Symbol != ' ') { + else if (board.TileAt(x, y).symbol != ' ') { throw new Exception("Invalid position"); } // update game state - _lastSymbol = symbol; - _board.AddTileAt(symbol, x, y); + lastSymbol = symbol; + board.AddTileAt(symbol, x, y); } public char Winner() { //if the positions in first row are taken - if (_board.TileAt(0, 0).Symbol != ' ' && - _board.TileAt(0, 1).Symbol != ' ' && - _board.TileAt(0, 2).Symbol != ' ') { + if (board.TileAt(0, 0).symbol != ' ' && + board.TileAt(0, 1).symbol != ' ' && + board.TileAt(0, 2).symbol != ' ') { //if first row is full with same symbol - if (_board.TileAt(0, 0).Symbol == - _board.TileAt(0, 1).Symbol && - _board.TileAt(0, 2).Symbol == _board.TileAt(0, 1).Symbol) { - return _board.TileAt(0, 0).Symbol; + if (board.TileAt(0, 0).symbol == + board.TileAt(0, 1).symbol && + board.TileAt(0, 2).symbol == board.TileAt(0, 1).symbol) { + return board.TileAt(0, 0).symbol; } } //if the positions in first row are taken - if (_board.TileAt(1, 0).Symbol != ' ' && - _board.TileAt(1, 1).Symbol != ' ' && - _board.TileAt(1, 2).Symbol != ' ') { + if (board.TileAt(1, 0).symbol != ' ' && + board.TileAt(1, 1).symbol != ' ' && + board.TileAt(1, 2).symbol != ' ') { //if middle row is full with same symbol - if (_board.TileAt(1, 0).Symbol == - _board.TileAt(1, 1).Symbol && - _board.TileAt(1, 2).Symbol == - _board.TileAt(1, 1).Symbol) { - return _board.TileAt(1, 0).Symbol; + if (board.TileAt(1, 0).symbol == + board.TileAt(1, 1).symbol && + board.TileAt(1, 2).symbol == + board.TileAt(1, 1).symbol) { + return board.TileAt(1, 0).symbol; } } //if the positions in first row are taken - if (_board.TileAt(2, 0).Symbol != ' ' && - _board.TileAt(2, 1).Symbol != ' ' && - _board.TileAt(2, 2).Symbol != ' ') { + if (board.TileAt(2, 0).symbol != ' ' && + board.TileAt(2, 1).symbol != ' ' && + board.TileAt(2, 2).symbol != ' ') { //if middle row is full with same symbol - if (_board.TileAt(2, 0).Symbol == - _board.TileAt(2, 1).Symbol && - _board.TileAt(2, 2).Symbol == - _board.TileAt(2, 1).Symbol) { - return _board.TileAt(2, 0).Symbol; + if (board.TileAt(2, 0).symbol == + board.TileAt(2, 1).symbol && + board.TileAt(2, 2).symbol == + board.TileAt(2, 1).symbol) { + return board.TileAt(2, 0).symbol; } } return ' '; } } - diff --git a/java/src/Tile.java b/java/src/Tile.java index 03be347..7f87826 100644 --- a/java/src/Tile.java +++ b/java/src/Tile.java @@ -1,7 +1,5 @@ - -public class Tile -{ - public int X; - public int Y; - public char Symbol; -} \ No newline at end of file +public class Tile { + public int x; + public int y; + public char symbol; +} diff --git a/java/test/GameTest.java b/java/test/GameTest.java index 51816fb..4f98f8d 100644 --- a/java/test/GameTest.java +++ b/java/test/GameTest.java @@ -8,7 +8,7 @@ public class GameTest { private Game game; @BeforeEach - public void InitializeGame(){ + public void InitializeGame() { game = new Game(); } @@ -19,7 +19,7 @@ public void NotAllowPlayerOToPlayFirst() { @Test public void NotAllowPlayerXToPlayTwiceInARow() { - assertThrows(Exception.class, () ->{ + assertThrows(Exception.class, () -> { game.Play('X', 0, 0); game.Play('X', 1, 0); }); @@ -27,7 +27,7 @@ public void NotAllowPlayerXToPlayTwiceInARow() { @Test public void NotAllowPlayerToPlayInLastPlayedPosition() { - assertThrows(Exception.class, () ->{ + assertThrows(Exception.class, () -> { game.Play('X', 0, 0); game.Play('O', 0, 0); }); @@ -35,7 +35,7 @@ public void NotAllowPlayerToPlayInLastPlayedPosition() { @Test public void NotAllowPlayerToPlayInAnyPlayedPosition() { - assertThrows(Exception.class, () ->{ + assertThrows(Exception.class, () -> { game.Play('X', 0, 0); game.Play('O', 1, 0); game.Play('X', 0, 0); @@ -43,8 +43,7 @@ public void NotAllowPlayerToPlayInAnyPlayedPosition() { } @Test - public void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception - { + public void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { game.Play('X', 0, 0); game.Play('O', 1, 0); game.Play('X', 0, 1); @@ -57,8 +56,7 @@ public void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception } @Test - public void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception - { + public void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception { game.Play('X', 2, 2); game.Play('O', 0, 0); game.Play('X', 1, 0); @@ -72,8 +70,7 @@ public void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception } @Test - public void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception - { + public void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception { game.Play('X', 1, 0); game.Play('O', 0, 0); game.Play('X', 1, 1); @@ -86,8 +83,7 @@ public void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception } @Test - public void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception - { + public void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception { game.Play('X', 0, 0); game.Play('O', 1, 0); game.Play('X', 2, 0); @@ -101,8 +97,7 @@ public void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception } @Test - public void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception - { + public void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception { game.Play('X', 2, 0); game.Play('O', 0, 0); game.Play('X', 2, 1); @@ -115,8 +110,7 @@ public void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception } @Test - public void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception - { + public void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception { game.Play('X', 0, 0); game.Play('O', 2, 0); game.Play('X', 1, 0); From 1f1eb4d2bb04a1bf4d89c9ef59b342c00f4e9c6a Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 13:41:55 +0200 Subject: [PATCH 03/17] make attributes that are only instantiated once final --- java/src/Board.java | 2 +- java/src/Game.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/src/Board.java b/java/src/Board.java index 9a3aa7c..635b076 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -2,7 +2,7 @@ import java.util.List; public class Board { - private List plays = new ArrayList<>(); + private final List plays = new ArrayList<>(); public Board() { for (int i = 0; i < 3; i++) { diff --git a/java/src/Game.java b/java/src/Game.java index 1ec832b..ec80e6e 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -1,6 +1,6 @@ public class Game { private char lastSymbol = ' '; - private Board board = new Board(); + private final Board board = new Board(); public void Play(char symbol, int x, int y) throws Exception { //if first move From 4c57cf202302e0c024b2fa74a29cb876e1eb7802 Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 13:50:32 +0200 Subject: [PATCH 04/17] encapsulate attributes --- .idea/compiler.xml | 8 ++++++ CodeKatas_20240507_CodeSmells.iml | 9 ++++++ java/CodeSmells.iml | 25 ++++++++++++++++ java/src/Board.java | 16 +++++------ java/src/Game.java | 48 +++++++++++++++---------------- java/src/Tile.java | 11 +++++-- 6 files changed, 82 insertions(+), 35 deletions(-) create mode 100644 .idea/compiler.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..a1757ae --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/CodeKatas_20240507_CodeSmells.iml b/CodeKatas_20240507_CodeSmells.iml index 1d21cfd..7c56e11 100644 --- a/CodeKatas_20240507_CodeSmells.iml +++ b/CodeKatas_20240507_CodeSmells.iml @@ -24,5 +24,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/java/CodeSmells.iml b/java/CodeSmells.iml index d0c7dd5..f3fdec3 100644 --- a/java/CodeSmells.iml +++ b/java/CodeSmells.iml @@ -10,5 +10,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java/src/Board.java b/java/src/Board.java index 635b076..e49b977 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -8,9 +8,9 @@ public Board() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { Tile tile = new Tile(); - tile.x = i; - tile.y = j; - tile.symbol = ' '; + tile.setX(i); + tile.setY(j); + tile.setSymbol(' '); plays.add(tile); } } @@ -18,7 +18,7 @@ public Board() { public Tile TileAt(int x, int y) { for (Tile t : plays) { - if (t.x == x && t.y == y) { + if (t.getX() == x && t.getY() == y) { return t; } } @@ -27,10 +27,10 @@ public Tile TileAt(int x, int y) { public void AddTileAt(char symbol, int x, int y) { Tile newTile = new Tile(); - newTile.x = x; - newTile.y = y; - newTile.symbol = symbol; + newTile.setX(x); + newTile.setY(y); + newTile.setSymbol(symbol); - TileAt(x, y).symbol = symbol; + TileAt(x, y).setSymbol(symbol); } } diff --git a/java/src/Game.java b/java/src/Game.java index ec80e6e..5515e28 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -15,7 +15,7 @@ else if (symbol == lastSymbol) { throw new Exception("Invalid next player"); } //if not first move but play on an already played tile - else if (board.TileAt(x, y).symbol != ' ') { + else if (board.TileAt(x, y).getSymbol() != ' ') { throw new Exception("Invalid position"); } @@ -26,40 +26,40 @@ else if (board.TileAt(x, y).symbol != ' ') { public char Winner() { //if the positions in first row are taken - if (board.TileAt(0, 0).symbol != ' ' && - board.TileAt(0, 1).symbol != ' ' && - board.TileAt(0, 2).symbol != ' ') { + if (board.TileAt(0, 0).getSymbol() != ' ' && + board.TileAt(0, 1).getSymbol() != ' ' && + board.TileAt(0, 2).getSymbol() != ' ') { //if first row is full with same symbol - if (board.TileAt(0, 0).symbol == - board.TileAt(0, 1).symbol && - board.TileAt(0, 2).symbol == board.TileAt(0, 1).symbol) { - return board.TileAt(0, 0).symbol; + if (board.TileAt(0, 0).getSymbol() == + board.TileAt(0, 1).getSymbol() && + board.TileAt(0, 2).getSymbol() == board.TileAt(0, 1).getSymbol()) { + return board.TileAt(0, 0).getSymbol(); } } //if the positions in first row are taken - if (board.TileAt(1, 0).symbol != ' ' && - board.TileAt(1, 1).symbol != ' ' && - board.TileAt(1, 2).symbol != ' ') { + if (board.TileAt(1, 0).getSymbol() != ' ' && + board.TileAt(1, 1).getSymbol() != ' ' && + board.TileAt(1, 2).getSymbol() != ' ') { //if middle row is full with same symbol - if (board.TileAt(1, 0).symbol == - board.TileAt(1, 1).symbol && - board.TileAt(1, 2).symbol == - board.TileAt(1, 1).symbol) { - return board.TileAt(1, 0).symbol; + if (board.TileAt(1, 0).getSymbol() == + board.TileAt(1, 1).getSymbol() && + board.TileAt(1, 2).getSymbol() == + board.TileAt(1, 1).getSymbol()) { + return board.TileAt(1, 0).getSymbol(); } } //if the positions in first row are taken - if (board.TileAt(2, 0).symbol != ' ' && - board.TileAt(2, 1).symbol != ' ' && - board.TileAt(2, 2).symbol != ' ') { + if (board.TileAt(2, 0).getSymbol() != ' ' && + board.TileAt(2, 1).getSymbol() != ' ' && + board.TileAt(2, 2).getSymbol() != ' ') { //if middle row is full with same symbol - if (board.TileAt(2, 0).symbol == - board.TileAt(2, 1).symbol && - board.TileAt(2, 2).symbol == - board.TileAt(2, 1).symbol) { - return board.TileAt(2, 0).symbol; + if (board.TileAt(2, 0).getSymbol() == + board.TileAt(2, 1).getSymbol() && + board.TileAt(2, 2).getSymbol() == + board.TileAt(2, 1).getSymbol()) { + return board.TileAt(2, 0).getSymbol(); } } diff --git a/java/src/Tile.java b/java/src/Tile.java index 7f87826..a11721c 100644 --- a/java/src/Tile.java +++ b/java/src/Tile.java @@ -1,5 +1,10 @@ +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter public class Tile { - public int x; - public int y; - public char symbol; + private int x; + private int y; + private char symbol; } From 8223e4f38c8a6b99e0d345eb520701d462d4f5be Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 13:52:12 +0200 Subject: [PATCH 05/17] rename methods to match java style guides --- java/src/Board.java | 6 +-- java/src/Game.java | 54 +++++++++++------------ java/test/GameTest.java | 96 ++++++++++++++++++++--------------------- 3 files changed, 78 insertions(+), 78 deletions(-) diff --git a/java/src/Board.java b/java/src/Board.java index e49b977..ff90cae 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -16,7 +16,7 @@ public Board() { } } - public Tile TileAt(int x, int y) { + public Tile tileAt(int x, int y) { for (Tile t : plays) { if (t.getX() == x && t.getY() == y) { return t; @@ -25,12 +25,12 @@ public Tile TileAt(int x, int y) { return null; } - public void AddTileAt(char symbol, int x, int y) { + public void addTileAt(char symbol, int x, int y) { Tile newTile = new Tile(); newTile.setX(x); newTile.setY(y); newTile.setSymbol(symbol); - TileAt(x, y).setSymbol(symbol); + tileAt(x, y).setSymbol(symbol); } } diff --git a/java/src/Game.java b/java/src/Game.java index 5515e28..cccec26 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -2,7 +2,7 @@ public class Game { private char lastSymbol = ' '; private final Board board = new Board(); - public void Play(char symbol, int x, int y) throws Exception { + public void play(char symbol, int x, int y) throws Exception { //if first move if (lastSymbol == ' ') { //if player is X @@ -15,51 +15,51 @@ else if (symbol == lastSymbol) { throw new Exception("Invalid next player"); } //if not first move but play on an already played tile - else if (board.TileAt(x, y).getSymbol() != ' ') { + else if (board.tileAt(x, y).getSymbol() != ' ') { throw new Exception("Invalid position"); } // update game state lastSymbol = symbol; - board.AddTileAt(symbol, x, y); + board.addTileAt(symbol, x, y); } - public char Winner() { + public char winner() { //if the positions in first row are taken - if (board.TileAt(0, 0).getSymbol() != ' ' && - board.TileAt(0, 1).getSymbol() != ' ' && - board.TileAt(0, 2).getSymbol() != ' ') { + if (board.tileAt(0, 0).getSymbol() != ' ' && + board.tileAt(0, 1).getSymbol() != ' ' && + board.tileAt(0, 2).getSymbol() != ' ') { //if first row is full with same symbol - if (board.TileAt(0, 0).getSymbol() == - board.TileAt(0, 1).getSymbol() && - board.TileAt(0, 2).getSymbol() == board.TileAt(0, 1).getSymbol()) { - return board.TileAt(0, 0).getSymbol(); + if (board.tileAt(0, 0).getSymbol() == + board.tileAt(0, 1).getSymbol() && + board.tileAt(0, 2).getSymbol() == board.tileAt(0, 1).getSymbol()) { + return board.tileAt(0, 0).getSymbol(); } } //if the positions in first row are taken - if (board.TileAt(1, 0).getSymbol() != ' ' && - board.TileAt(1, 1).getSymbol() != ' ' && - board.TileAt(1, 2).getSymbol() != ' ') { + if (board.tileAt(1, 0).getSymbol() != ' ' && + board.tileAt(1, 1).getSymbol() != ' ' && + board.tileAt(1, 2).getSymbol() != ' ') { //if middle row is full with same symbol - if (board.TileAt(1, 0).getSymbol() == - board.TileAt(1, 1).getSymbol() && - board.TileAt(1, 2).getSymbol() == - board.TileAt(1, 1).getSymbol()) { - return board.TileAt(1, 0).getSymbol(); + if (board.tileAt(1, 0).getSymbol() == + board.tileAt(1, 1).getSymbol() && + board.tileAt(1, 2).getSymbol() == + board.tileAt(1, 1).getSymbol()) { + return board.tileAt(1, 0).getSymbol(); } } //if the positions in first row are taken - if (board.TileAt(2, 0).getSymbol() != ' ' && - board.TileAt(2, 1).getSymbol() != ' ' && - board.TileAt(2, 2).getSymbol() != ' ') { + if (board.tileAt(2, 0).getSymbol() != ' ' && + board.tileAt(2, 1).getSymbol() != ' ' && + board.tileAt(2, 2).getSymbol() != ' ') { //if middle row is full with same symbol - if (board.TileAt(2, 0).getSymbol() == - board.TileAt(2, 1).getSymbol() && - board.TileAt(2, 2).getSymbol() == - board.TileAt(2, 1).getSymbol()) { - return board.TileAt(2, 0).getSymbol(); + if (board.tileAt(2, 0).getSymbol() == + board.tileAt(2, 1).getSymbol() && + board.tileAt(2, 2).getSymbol() == + board.tileAt(2, 1).getSymbol()) { + return board.tileAt(2, 0).getSymbol(); } } diff --git a/java/test/GameTest.java b/java/test/GameTest.java index 4f98f8d..61096db 100644 --- a/java/test/GameTest.java +++ b/java/test/GameTest.java @@ -14,111 +14,111 @@ public void InitializeGame() { @Test public void NotAllowPlayerOToPlayFirst() { - assertThrows(Exception.class, () -> game.Play('O', 0, 0)); + assertThrows(Exception.class, () -> game.play('O', 0, 0)); } @Test public void NotAllowPlayerXToPlayTwiceInARow() { assertThrows(Exception.class, () -> { - game.Play('X', 0, 0); - game.Play('X', 1, 0); + game.play('X', 0, 0); + game.play('X', 1, 0); }); } @Test public void NotAllowPlayerToPlayInLastPlayedPosition() { assertThrows(Exception.class, () -> { - game.Play('X', 0, 0); - game.Play('O', 0, 0); + game.play('X', 0, 0); + game.play('O', 0, 0); }); } @Test public void NotAllowPlayerToPlayInAnyPlayedPosition() { assertThrows(Exception.class, () -> { - game.Play('X', 0, 0); - game.Play('O', 1, 0); - game.Play('X', 0, 0); + game.play('X', 0, 0); + game.play('O', 1, 0); + game.play('X', 0, 0); }); } @Test public void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { - game.Play('X', 0, 0); - game.Play('O', 1, 0); - game.Play('X', 0, 1); - game.Play('O', 1, 1); - game.Play('X', 0, 2); + game.play('X', 0, 0); + game.play('O', 1, 0); + game.play('X', 0, 1); + game.play('O', 1, 1); + game.play('X', 0, 2); - char winner = game.Winner(); + char winner = game.winner(); assertEquals('X', winner); } @Test public void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception { - game.Play('X', 2, 2); - game.Play('O', 0, 0); - game.Play('X', 1, 0); - game.Play('O', 0, 1); - game.Play('X', 1, 1); - game.Play('O', 0, 2); + game.play('X', 2, 2); + game.play('O', 0, 0); + game.play('X', 1, 0); + game.play('O', 0, 1); + game.play('X', 1, 1); + game.play('O', 0, 2); - char winner = game.Winner(); + char winner = game.winner(); assertEquals('O', winner); } @Test public void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception { - game.Play('X', 1, 0); - game.Play('O', 0, 0); - game.Play('X', 1, 1); - game.Play('O', 0, 1); - game.Play('X', 1, 2); + game.play('X', 1, 0); + game.play('O', 0, 0); + game.play('X', 1, 1); + game.play('O', 0, 1); + game.play('X', 1, 2); - char winner = game.Winner(); + char winner = game.winner(); assertEquals('X', winner); } @Test public void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception { - game.Play('X', 0, 0); - game.Play('O', 1, 0); - game.Play('X', 2, 0); - game.Play('O', 1, 1); - game.Play('X', 2, 1); - game.Play('O', 1, 2); + game.play('X', 0, 0); + game.play('O', 1, 0); + game.play('X', 2, 0); + game.play('O', 1, 1); + game.play('X', 2, 1); + game.play('O', 1, 2); - char winner = game.Winner(); + char winner = game.winner(); assertEquals('O', winner); } @Test public void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception { - game.Play('X', 2, 0); - game.Play('O', 0, 0); - game.Play('X', 2, 1); - game.Play('O', 0, 1); - game.Play('X', 2, 2); + game.play('X', 2, 0); + game.play('O', 0, 0); + game.play('X', 2, 1); + game.play('O', 0, 1); + game.play('X', 2, 2); - char winner = game.Winner(); + char winner = game.winner(); assertEquals('X', winner); } @Test public void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception { - game.Play('X', 0, 0); - game.Play('O', 2, 0); - game.Play('X', 1, 0); - game.Play('O', 2, 1); - game.Play('X', 1, 1); - game.Play('O', 2, 2); - - char winner = game.Winner(); + game.play('X', 0, 0); + game.play('O', 2, 0); + game.play('X', 1, 0); + game.play('O', 2, 1); + game.play('X', 1, 1); + game.play('O', 2, 2); + + char winner = game.winner(); assertEquals('O', winner); } From 013c206c4bcb89825da7dc06c891d91779883fa7 Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 14:00:06 +0200 Subject: [PATCH 06/17] use optional for better winner interface --- java/src/Game.java | 12 ++++---- java/test/GameTest.java | 62 +++++++++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/java/src/Game.java b/java/src/Game.java index cccec26..9d08db6 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -1,3 +1,5 @@ +import java.util.Optional; + public class Game { private char lastSymbol = ' '; private final Board board = new Board(); @@ -24,7 +26,7 @@ else if (board.tileAt(x, y).getSymbol() != ' ') { board.addTileAt(symbol, x, y); } - public char winner() { + public Optional computeWinner() { //if the positions in first row are taken if (board.tileAt(0, 0).getSymbol() != ' ' && board.tileAt(0, 1).getSymbol() != ' ' && @@ -33,7 +35,7 @@ public char winner() { if (board.tileAt(0, 0).getSymbol() == board.tileAt(0, 1).getSymbol() && board.tileAt(0, 2).getSymbol() == board.tileAt(0, 1).getSymbol()) { - return board.tileAt(0, 0).getSymbol(); + return Optional.of(board.tileAt(0, 0).getSymbol()); } } @@ -46,7 +48,7 @@ public char winner() { board.tileAt(1, 1).getSymbol() && board.tileAt(1, 2).getSymbol() == board.tileAt(1, 1).getSymbol()) { - return board.tileAt(1, 0).getSymbol(); + return Optional.of(board.tileAt(1, 0).getSymbol()); } } @@ -59,10 +61,10 @@ public char winner() { board.tileAt(2, 1).getSymbol() && board.tileAt(2, 2).getSymbol() == board.tileAt(2, 1).getSymbol()) { - return board.tileAt(2, 0).getSymbol(); + return Optional.of(board.tileAt(2, 0).getSymbol()); } } - return ' '; + return Optional.empty(); } } diff --git a/java/test/GameTest.java b/java/test/GameTest.java index 61096db..c9d9d67 100644 --- a/java/test/GameTest.java +++ b/java/test/GameTest.java @@ -1,8 +1,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.Optional; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class GameTest { private Game game; @@ -42,6 +45,35 @@ public void NotAllowPlayerToPlayInAnyPlayedPosition() { }); } + @Test + public void DeclareNoWinnerWithUnfinishedGrid() throws Exception { + game.play('X', 0, 0); + game.play('O', 1, 0); + game.play('X', 0, 1); + game.play('O', 1, 1); + + Optional winner = game.computeWinner(); + + assertTrue(winner.isEmpty()); + } + + @Test + public void DeclareNoWinnerWithTiedGrid() throws Exception { + game.play('X', 1, 1); + game.play('O', 0, 0); + game.play('X', 0, 2); + game.play('O', 2, 0); + game.play('X', 1, 0); + game.play('0', 1, 2); + game.play('X', 2, 1); + game.play('O', 0, 1); + game.play('X', 2, 2); + + Optional winner = game.computeWinner(); + + assertTrue(winner.isEmpty()); + } + @Test public void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { game.play('X', 0, 0); @@ -50,9 +82,10 @@ public void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { game.play('O', 1, 1); game.play('X', 0, 2); - char winner = game.winner(); + Optional winner = game.computeWinner(); - assertEquals('X', winner); + assertTrue(winner.isPresent()); + assertEquals('X', winner.get()); } @Test @@ -64,9 +97,10 @@ public void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception { game.play('X', 1, 1); game.play('O', 0, 2); - char winner = game.winner(); + Optional winner = game.computeWinner(); - assertEquals('O', winner); + assertTrue(winner.isPresent()); + assertEquals('O', winner.get()); } @Test @@ -77,9 +111,10 @@ public void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception { game.play('O', 0, 1); game.play('X', 1, 2); - char winner = game.winner(); + Optional winner = game.computeWinner(); - assertEquals('X', winner); + assertTrue(winner.isPresent()); + assertEquals('X', winner.get()); } @Test @@ -91,9 +126,10 @@ public void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception { game.play('X', 2, 1); game.play('O', 1, 2); - char winner = game.winner(); + Optional winner = game.computeWinner(); - assertEquals('O', winner); + assertTrue(winner.isPresent()); + assertEquals('O', winner.get()); } @Test @@ -104,9 +140,10 @@ public void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception { game.play('O', 0, 1); game.play('X', 2, 2); - char winner = game.winner(); + Optional winner = game.computeWinner(); - assertEquals('X', winner); + assertTrue(winner.isPresent()); + assertEquals('X', winner.get()); } @Test @@ -118,8 +155,9 @@ public void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception { game.play('X', 1, 1); game.play('O', 2, 2); - char winner = game.winner(); + Optional winner = game.computeWinner(); - assertEquals('O', winner); + assertTrue(winner.isPresent()); + assertEquals('O', winner.get()); } } From 5035b3d3f220ac9b6dade403e2a4752090cc3e27 Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 14:08:51 +0200 Subject: [PATCH 07/17] use optional for tile symbols --- java/src/Board.java | 7 ++++--- java/src/Game.java | 49 ++++++++++++++++++++------------------------- java/src/Tile.java | 4 +++- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/java/src/Board.java b/java/src/Board.java index ff90cae..5a4c03f 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -1,5 +1,6 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; public class Board { private final List plays = new ArrayList<>(); @@ -10,7 +11,7 @@ public Board() { Tile tile = new Tile(); tile.setX(i); tile.setY(j); - tile.setSymbol(' '); + tile.setSymbol(Optional.empty()); plays.add(tile); } } @@ -29,8 +30,8 @@ public void addTileAt(char symbol, int x, int y) { Tile newTile = new Tile(); newTile.setX(x); newTile.setY(y); - newTile.setSymbol(symbol); + newTile.setSymbol(Optional.of(symbol)); - tileAt(x, y).setSymbol(symbol); + tileAt(x, y).setSymbol(Optional.of(symbol)); } } diff --git a/java/src/Game.java b/java/src/Game.java index 9d08db6..d91d37d 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -1,23 +1,23 @@ import java.util.Optional; public class Game { - private char lastSymbol = ' '; + private Character lastSymbol = null; private final Board board = new Board(); public void play(char symbol, int x, int y) throws Exception { //if first move - if (lastSymbol == ' ') { + if (lastSymbol == null) { //if player is X if (symbol == 'O') { throw new Exception("Invalid first player"); } } //if not first move but player repeated - else if (symbol == lastSymbol) { + else if (lastSymbol.equals(symbol)) { throw new Exception("Invalid next player"); } //if not first move but play on an already played tile - else if (board.tileAt(x, y).getSymbol() != ' ') { + else if (board.tileAt(x, y).getSymbol().isPresent()) { throw new Exception("Invalid position"); } @@ -28,40 +28,35 @@ else if (board.tileAt(x, y).getSymbol() != ' ') { public Optional computeWinner() { //if the positions in first row are taken - if (board.tileAt(0, 0).getSymbol() != ' ' && - board.tileAt(0, 1).getSymbol() != ' ' && - board.tileAt(0, 2).getSymbol() != ' ') { + if (board.tileAt(0, 0).getSymbol().isPresent() && + board.tileAt(0, 1).getSymbol().isPresent() && + board.tileAt(0, 2).getSymbol().isPresent()) { //if first row is full with same symbol - if (board.tileAt(0, 0).getSymbol() == - board.tileAt(0, 1).getSymbol() && - board.tileAt(0, 2).getSymbol() == board.tileAt(0, 1).getSymbol()) { - return Optional.of(board.tileAt(0, 0).getSymbol()); + if (board.tileAt(0, 0).getSymbol().equals(board.tileAt(0, 1).getSymbol()) && + board.tileAt(0, 2).getSymbol().equals(board.tileAt(0, 1).getSymbol())) { + return board.tileAt(0, 0).getSymbol(); } } //if the positions in first row are taken - if (board.tileAt(1, 0).getSymbol() != ' ' && - board.tileAt(1, 1).getSymbol() != ' ' && - board.tileAt(1, 2).getSymbol() != ' ') { + if (board.tileAt(1, 0).getSymbol().isPresent() && + board.tileAt(1, 1).getSymbol().isPresent() && + board.tileAt(1, 2).getSymbol().isPresent()) { //if middle row is full with same symbol - if (board.tileAt(1, 0).getSymbol() == - board.tileAt(1, 1).getSymbol() && - board.tileAt(1, 2).getSymbol() == - board.tileAt(1, 1).getSymbol()) { - return Optional.of(board.tileAt(1, 0).getSymbol()); + if (board.tileAt(1, 0).getSymbol().equals(board.tileAt(1, 1).getSymbol()) && + board.tileAt(1, 2).getSymbol().equals(board.tileAt(1, 1).getSymbol())) { + return board.tileAt(1, 0).getSymbol(); } } //if the positions in first row are taken - if (board.tileAt(2, 0).getSymbol() != ' ' && - board.tileAt(2, 1).getSymbol() != ' ' && - board.tileAt(2, 2).getSymbol() != ' ') { + if (board.tileAt(2, 0).getSymbol().isPresent() && + board.tileAt(2, 1).getSymbol().isPresent() && + board.tileAt(2, 2).getSymbol().isPresent()) { //if middle row is full with same symbol - if (board.tileAt(2, 0).getSymbol() == - board.tileAt(2, 1).getSymbol() && - board.tileAt(2, 2).getSymbol() == - board.tileAt(2, 1).getSymbol()) { - return Optional.of(board.tileAt(2, 0).getSymbol()); + if (board.tileAt(2, 0).getSymbol().equals(board.tileAt(2, 1).getSymbol()) && + board.tileAt(2, 2).getSymbol().equals(board.tileAt(2, 1).getSymbol())) { + return board.tileAt(2, 0).getSymbol(); } } diff --git a/java/src/Tile.java b/java/src/Tile.java index a11721c..f2025b1 100644 --- a/java/src/Tile.java +++ b/java/src/Tile.java @@ -1,10 +1,12 @@ import lombok.Getter; import lombok.Setter; +import java.util.Optional; + @Getter @Setter public class Tile { private int x; private int y; - private char symbol; + private Optional symbol; } From 638dff435a41d62ca29ba23b6938f12e0f2e3b9c Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 14:22:28 +0200 Subject: [PATCH 08/17] use symbol class for played symbols --- java/src/Board.java | 2 +- java/src/Game.java | 19 +++++++++-------- java/src/Symbol.java | 18 ++++++++++++++++ java/src/Tile.java | 2 +- java/test/GameTest.java | 46 +++++++++++++++++++++++++++-------------- 5 files changed, 61 insertions(+), 26 deletions(-) create mode 100644 java/src/Symbol.java diff --git a/java/src/Board.java b/java/src/Board.java index 5a4c03f..7d5da30 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -26,7 +26,7 @@ public Tile tileAt(int x, int y) { return null; } - public void addTileAt(char symbol, int x, int y) { + public void addTileAt(Symbol symbol, int x, int y) { Tile newTile = new Tile(); newTile.setX(x); newTile.setY(y); diff --git a/java/src/Game.java b/java/src/Game.java index d91d37d..1bae81a 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -1,21 +1,22 @@ import java.util.Optional; public class Game { - private Character lastSymbol = null; + private Symbol lastSymbol = null; private final Board board = new Board(); - public void play(char symbol, int x, int y) throws Exception { + public void play(char rawSymbol, int x, int y) throws Exception { + Symbol symbol = Symbol.fromChar(rawSymbol); + //if first move - if (lastSymbol == null) { - //if player is X - if (symbol == 'O') { - throw new Exception("Invalid first player"); - } + if (lastSymbol == null && symbol == Symbol.O) { + throw new Exception("Invalid first player"); } + //if not first move but player repeated - else if (lastSymbol.equals(symbol)) { + else if (symbol.equals(lastSymbol)) { throw new Exception("Invalid next player"); } + //if not first move but play on an already played tile else if (board.tileAt(x, y).getSymbol().isPresent()) { throw new Exception("Invalid position"); @@ -26,7 +27,7 @@ else if (board.tileAt(x, y).getSymbol().isPresent()) { board.addTileAt(symbol, x, y); } - public Optional computeWinner() { + public Optional computeWinner() { //if the positions in first row are taken if (board.tileAt(0, 0).getSymbol().isPresent() && board.tileAt(0, 1).getSymbol().isPresent() && diff --git a/java/src/Symbol.java b/java/src/Symbol.java new file mode 100644 index 0000000..1e5144c --- /dev/null +++ b/java/src/Symbol.java @@ -0,0 +1,18 @@ +import lombok.AllArgsConstructor; + +import java.util.Arrays; + +@AllArgsConstructor +public enum Symbol { + X('X'), + O('O'); + + private final char asChar; + + public static Symbol fromChar(char in) { + return Arrays.stream(Symbol.values()) + .filter(s -> s.asChar == Character.toUpperCase(in)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Invalid symbol: %s".formatted(in))); + } +} diff --git a/java/src/Tile.java b/java/src/Tile.java index f2025b1..438bb97 100644 --- a/java/src/Tile.java +++ b/java/src/Tile.java @@ -8,5 +8,5 @@ public class Tile { private int x; private int y; - private Optional symbol; + private Optional symbol; } diff --git a/java/test/GameTest.java b/java/test/GameTest.java index c9d9d67..9f85f0b 100644 --- a/java/test/GameTest.java +++ b/java/test/GameTest.java @@ -3,6 +3,7 @@ import java.util.Optional; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -52,7 +53,7 @@ public void DeclareNoWinnerWithUnfinishedGrid() throws Exception { game.play('X', 0, 1); game.play('O', 1, 1); - Optional winner = game.computeWinner(); + Optional winner = game.computeWinner(); assertTrue(winner.isEmpty()); } @@ -64,12 +65,12 @@ public void DeclareNoWinnerWithTiedGrid() throws Exception { game.play('X', 0, 2); game.play('O', 2, 0); game.play('X', 1, 0); - game.play('0', 1, 2); + game.play('O', 1, 2); game.play('X', 2, 1); game.play('O', 0, 1); game.play('X', 2, 2); - Optional winner = game.computeWinner(); + Optional winner = game.computeWinner(); assertTrue(winner.isEmpty()); } @@ -82,10 +83,10 @@ public void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { game.play('O', 1, 1); game.play('X', 0, 2); - Optional winner = game.computeWinner(); + Optional winner = game.computeWinner(); assertTrue(winner.isPresent()); - assertEquals('X', winner.get()); + assertEquals(Symbol.X, winner.get()); } @Test @@ -97,10 +98,10 @@ public void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception { game.play('X', 1, 1); game.play('O', 0, 2); - Optional winner = game.computeWinner(); + Optional winner = game.computeWinner(); assertTrue(winner.isPresent()); - assertEquals('O', winner.get()); + assertEquals(Symbol.O, winner.get()); } @Test @@ -111,10 +112,10 @@ public void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception { game.play('O', 0, 1); game.play('X', 1, 2); - Optional winner = game.computeWinner(); + Optional winner = game.computeWinner(); assertTrue(winner.isPresent()); - assertEquals('X', winner.get()); + assertEquals(Symbol.X, winner.get()); } @Test @@ -126,10 +127,10 @@ public void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception { game.play('X', 2, 1); game.play('O', 1, 2); - Optional winner = game.computeWinner(); + Optional winner = game.computeWinner(); assertTrue(winner.isPresent()); - assertEquals('O', winner.get()); + assertEquals(Symbol.O, winner.get()); } @Test @@ -140,10 +141,10 @@ public void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception { game.play('O', 0, 1); game.play('X', 2, 2); - Optional winner = game.computeWinner(); + Optional winner = game.computeWinner(); assertTrue(winner.isPresent()); - assertEquals('X', winner.get()); + assertEquals(Symbol.X, winner.get()); } @Test @@ -155,9 +156,24 @@ public void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception { game.play('X', 1, 1); game.play('O', 2, 2); - Optional winner = game.computeWinner(); + Optional winner = game.computeWinner(); assertTrue(winner.isPresent()); - assertEquals('O', winner.get()); + assertEquals(Symbol.O, winner.get()); + } + + @Test + public void NotAllowPlayingIllegalSymbol() throws Exception { + game.play('X', 0, 0); + game.play('O', 2, 0); + assertThrows(IllegalArgumentException.class, () -> game.play('Y', 1, 0)); + } + + @Test + public void AllowPlayingLowercaseVariants() throws Exception { + game.play('X', 0, 0); + game.play('O', 2, 0); + assertDoesNotThrow(() -> game.play('x', 1, 0)); + assertDoesNotThrow(() -> game.play('o', 2, 1)); } } From 4f32be57957819d0f5483eee15e330e37961d1e4 Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 14:23:44 +0200 Subject: [PATCH 09/17] remove useless duplication --- java/src/Board.java | 9 --------- java/src/Game.java | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/java/src/Board.java b/java/src/Board.java index 7d5da30..adf10ed 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -25,13 +25,4 @@ public Tile tileAt(int x, int y) { } return null; } - - public void addTileAt(Symbol symbol, int x, int y) { - Tile newTile = new Tile(); - newTile.setX(x); - newTile.setY(y); - newTile.setSymbol(Optional.of(symbol)); - - tileAt(x, y).setSymbol(Optional.of(symbol)); - } } diff --git a/java/src/Game.java b/java/src/Game.java index 1bae81a..1c081cc 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -24,7 +24,7 @@ else if (board.tileAt(x, y).getSymbol().isPresent()) { // update game state lastSymbol = symbol; - board.addTileAt(symbol, x, y); + board.tileAt(x, y).setSymbol(Optional.of(symbol)); } public Optional computeWinner() { From 68477bad6260fd81e326f118b7dd9e66a8345cdb Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 14:26:17 +0200 Subject: [PATCH 10/17] change optional of symbol back to nullable for tile state --- java/src/Board.java | 1 - java/src/Game.java | 30 +++++++++++++++--------------- java/src/Tile.java | 4 +--- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/java/src/Board.java b/java/src/Board.java index adf10ed..5c639de 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -11,7 +11,6 @@ public Board() { Tile tile = new Tile(); tile.setX(i); tile.setY(j); - tile.setSymbol(Optional.empty()); plays.add(tile); } } diff --git a/java/src/Game.java b/java/src/Game.java index 1c081cc..f7cc9ac 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -6,7 +6,7 @@ public class Game { public void play(char rawSymbol, int x, int y) throws Exception { Symbol symbol = Symbol.fromChar(rawSymbol); - + //if first move if (lastSymbol == null && symbol == Symbol.O) { throw new Exception("Invalid first player"); @@ -18,46 +18,46 @@ else if (symbol.equals(lastSymbol)) { } //if not first move but play on an already played tile - else if (board.tileAt(x, y).getSymbol().isPresent()) { + else if (board.tileAt(x, y).getSymbol() != null) { throw new Exception("Invalid position"); } // update game state lastSymbol = symbol; - board.tileAt(x, y).setSymbol(Optional.of(symbol)); + board.tileAt(x, y).setSymbol(symbol); } public Optional computeWinner() { //if the positions in first row are taken - if (board.tileAt(0, 0).getSymbol().isPresent() && - board.tileAt(0, 1).getSymbol().isPresent() && - board.tileAt(0, 2).getSymbol().isPresent()) { + if (board.tileAt(0, 0).getSymbol() != null && + board.tileAt(0, 1).getSymbol() != null && + board.tileAt(0, 2).getSymbol() != null) { //if first row is full with same symbol if (board.tileAt(0, 0).getSymbol().equals(board.tileAt(0, 1).getSymbol()) && board.tileAt(0, 2).getSymbol().equals(board.tileAt(0, 1).getSymbol())) { - return board.tileAt(0, 0).getSymbol(); + return Optional.ofNullable(board.tileAt(0, 0).getSymbol()); } } //if the positions in first row are taken - if (board.tileAt(1, 0).getSymbol().isPresent() && - board.tileAt(1, 1).getSymbol().isPresent() && - board.tileAt(1, 2).getSymbol().isPresent()) { + if (board.tileAt(1, 0).getSymbol() != null && + board.tileAt(1, 1).getSymbol() != null && + board.tileAt(1, 2).getSymbol() != null) { //if middle row is full with same symbol if (board.tileAt(1, 0).getSymbol().equals(board.tileAt(1, 1).getSymbol()) && board.tileAt(1, 2).getSymbol().equals(board.tileAt(1, 1).getSymbol())) { - return board.tileAt(1, 0).getSymbol(); + return Optional.ofNullable(board.tileAt(1, 0).getSymbol()); } } //if the positions in first row are taken - if (board.tileAt(2, 0).getSymbol().isPresent() && - board.tileAt(2, 1).getSymbol().isPresent() && - board.tileAt(2, 2).getSymbol().isPresent()) { + if (board.tileAt(2, 0).getSymbol() != null && + board.tileAt(2, 1).getSymbol() != null && + board.tileAt(2, 2).getSymbol() != null) { //if middle row is full with same symbol if (board.tileAt(2, 0).getSymbol().equals(board.tileAt(2, 1).getSymbol()) && board.tileAt(2, 2).getSymbol().equals(board.tileAt(2, 1).getSymbol())) { - return board.tileAt(2, 0).getSymbol(); + return Optional.ofNullable(board.tileAt(2, 0).getSymbol()); } } diff --git a/java/src/Tile.java b/java/src/Tile.java index 438bb97..a8f5b0f 100644 --- a/java/src/Tile.java +++ b/java/src/Tile.java @@ -1,12 +1,10 @@ import lombok.Getter; import lombok.Setter; -import java.util.Optional; - @Getter @Setter public class Tile { private int x; private int y; - private Optional symbol; + private Symbol symbol; } From 39cbf7939b496933614f4a770d6da49bf0fed3d7 Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 14:27:28 +0200 Subject: [PATCH 11/17] change tile constructor --- java/src/Board.java | 5 +---- java/src/Tile.java | 9 +++++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/java/src/Board.java b/java/src/Board.java index 5c639de..ddd240c 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -1,6 +1,5 @@ import java.util.ArrayList; import java.util.List; -import java.util.Optional; public class Board { private final List plays = new ArrayList<>(); @@ -8,9 +7,7 @@ public class Board { public Board() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - Tile tile = new Tile(); - tile.setX(i); - tile.setY(j); + Tile tile = new Tile(i, j); plays.add(tile); } } diff --git a/java/src/Tile.java b/java/src/Tile.java index a8f5b0f..d5dddc4 100644 --- a/java/src/Tile.java +++ b/java/src/Tile.java @@ -4,7 +4,12 @@ @Getter @Setter public class Tile { - private int x; - private int y; + private final int x; + private final int y; private Symbol symbol; + + public Tile(int x, int y) { + this.x = x; + this.y = y; + } } From 9b5c7a5620b337d9bc75c8665963c86690baf982 Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 14:28:06 +0200 Subject: [PATCH 12/17] restrict getters/setters --- java/src/Tile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/Tile.java b/java/src/Tile.java index d5dddc4..898fadb 100644 --- a/java/src/Tile.java +++ b/java/src/Tile.java @@ -2,10 +2,10 @@ import lombok.Setter; @Getter -@Setter public class Tile { private final int x; private final int y; + @Setter private Symbol symbol; public Tile(int x, int y) { From 8f2aa69e2c59e4d5f292e7f08bb7b34ec95fa1dc Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 14:34:14 +0200 Subject: [PATCH 13/17] remove useless public modifiers --- java/test/GameTest.java | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/java/test/GameTest.java b/java/test/GameTest.java index 9f85f0b..0470009 100644 --- a/java/test/GameTest.java +++ b/java/test/GameTest.java @@ -12,17 +12,17 @@ public class GameTest { private Game game; @BeforeEach - public void InitializeGame() { + void InitializeGame() { game = new Game(); } @Test - public void NotAllowPlayerOToPlayFirst() { + void NotAllowPlayerOToPlayFirst() { assertThrows(Exception.class, () -> game.play('O', 0, 0)); } @Test - public void NotAllowPlayerXToPlayTwiceInARow() { + void NotAllowPlayerXToPlayTwiceInARow() { assertThrows(Exception.class, () -> { game.play('X', 0, 0); game.play('X', 1, 0); @@ -30,7 +30,7 @@ public void NotAllowPlayerXToPlayTwiceInARow() { } @Test - public void NotAllowPlayerToPlayInLastPlayedPosition() { + void NotAllowPlayerToPlayInLastPlayedPosition() { assertThrows(Exception.class, () -> { game.play('X', 0, 0); game.play('O', 0, 0); @@ -38,7 +38,7 @@ public void NotAllowPlayerToPlayInLastPlayedPosition() { } @Test - public void NotAllowPlayerToPlayInAnyPlayedPosition() { + void NotAllowPlayerToPlayInAnyPlayedPosition() { assertThrows(Exception.class, () -> { game.play('X', 0, 0); game.play('O', 1, 0); @@ -47,7 +47,7 @@ public void NotAllowPlayerToPlayInAnyPlayedPosition() { } @Test - public void DeclareNoWinnerWithUnfinishedGrid() throws Exception { + void DeclareNoWinnerWithUnfinishedGrid() throws Exception { game.play('X', 0, 0); game.play('O', 1, 0); game.play('X', 0, 1); @@ -59,7 +59,7 @@ public void DeclareNoWinnerWithUnfinishedGrid() throws Exception { } @Test - public void DeclareNoWinnerWithTiedGrid() throws Exception { + void DeclareNoWinnerWithTiedGrid() throws Exception { game.play('X', 1, 1); game.play('O', 0, 0); game.play('X', 0, 2); @@ -76,7 +76,7 @@ public void DeclareNoWinnerWithTiedGrid() throws Exception { } @Test - public void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { + void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { game.play('X', 0, 0); game.play('O', 1, 0); game.play('X', 0, 1); @@ -90,7 +90,7 @@ public void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { } @Test - public void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception { + void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception { game.play('X', 2, 2); game.play('O', 0, 0); game.play('X', 1, 0); @@ -105,7 +105,7 @@ public void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception { } @Test - public void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception { + void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception { game.play('X', 1, 0); game.play('O', 0, 0); game.play('X', 1, 1); @@ -119,7 +119,7 @@ public void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception { } @Test - public void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception { + void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception { game.play('X', 0, 0); game.play('O', 1, 0); game.play('X', 2, 0); @@ -134,7 +134,7 @@ public void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception { } @Test - public void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception { + void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception { game.play('X', 2, 0); game.play('O', 0, 0); game.play('X', 2, 1); @@ -148,7 +148,7 @@ public void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception { } @Test - public void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception { + void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception { game.play('X', 0, 0); game.play('O', 2, 0); game.play('X', 1, 0); @@ -163,14 +163,14 @@ public void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception { } @Test - public void NotAllowPlayingIllegalSymbol() throws Exception { + void NotAllowPlayingIllegalSymbol() throws Exception { game.play('X', 0, 0); game.play('O', 2, 0); assertThrows(IllegalArgumentException.class, () -> game.play('Y', 1, 0)); } @Test - public void AllowPlayingLowercaseVariants() throws Exception { + void AllowPlayingLowercaseVariants() throws Exception { game.play('X', 0, 0); game.play('O', 2, 0); assertDoesNotThrow(() -> game.play('x', 1, 0)); From 51f694a106ab89713a73ddb6a43f340ecc6d1a1c Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 14:56:49 +0200 Subject: [PATCH 14/17] add disabled failing test for unimplemented win check --- java/src/Board.java | 13 +++++++++++++ java/src/Game.java | 4 ++++ java/src/Symbol.java | 2 ++ java/test/GameTest.java | 27 +++++++++++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/java/src/Board.java b/java/src/Board.java index ddd240c..cbf2dea 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -1,5 +1,6 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; public class Board { private final List plays = new ArrayList<>(); @@ -21,4 +22,16 @@ public Tile tileAt(int x, int y) { } return null; } + + public String print() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + Tile currentTile = this.tileAt(i, j); + sb.append(Optional.ofNullable(currentTile.getSymbol()).map(Symbol::getAsChar).orElse(' ')); + } + sb.append("\n"); + } + return sb.toString(); + } } diff --git a/java/src/Game.java b/java/src/Game.java index f7cc9ac..fe3109a 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -63,4 +63,8 @@ public Optional computeWinner() { return Optional.empty(); } + + public String printBoard() { + return board.print(); + } } diff --git a/java/src/Symbol.java b/java/src/Symbol.java index 1e5144c..873833a 100644 --- a/java/src/Symbol.java +++ b/java/src/Symbol.java @@ -1,8 +1,10 @@ import lombok.AllArgsConstructor; +import lombok.Getter; import java.util.Arrays; @AllArgsConstructor +@Getter public enum Symbol { X('X'), O('O'); diff --git a/java/test/GameTest.java b/java/test/GameTest.java index 0470009..2e1689a 100644 --- a/java/test/GameTest.java +++ b/java/test/GameTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.Optional; @@ -176,4 +177,30 @@ void AllowPlayingLowercaseVariants() throws Exception { assertDoesNotThrow(() -> game.play('x', 1, 0)); assertDoesNotThrow(() -> game.play('o', 2, 1)); } + + @Test + void DeclarePlayerXAsAWinnerIfThreeInLeftColumn() throws Exception { + game.play('X', 0, 0); + game.play('O', 1, 0); + game.play('X', 0, 1); + game.play('O', 1, 1); + game.play('X', 0, 2); + + assertTrue(game.computeWinner().isPresent()); + assertEquals(Symbol.X, game.computeWinner().get()); + } + + @Test + @Disabled("Not implemented") + void DeclarePlayerXAsAWinnerIfThreeDiagonallyRightDown() throws Exception { + game.play('X', 0, 0); + game.play('O', 1, 0); + game.play('X', 1, 1); + game.play('O', 2, 1); + game.play('X', 2, 2); + + System.out.println(game.printBoard()); + assertTrue(game.computeWinner().isPresent()); + assertEquals(Symbol.X, game.computeWinner().get()); + } } From 5a836de466c3e88503a164b541731c690cd7269d Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 15:00:37 +0200 Subject: [PATCH 15/17] use runtime exceptions --- java/src/Game.java | 8 ++++---- java/test/GameTest.java | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/java/src/Game.java b/java/src/Game.java index fe3109a..2475b9a 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -4,22 +4,22 @@ public class Game { private Symbol lastSymbol = null; private final Board board = new Board(); - public void play(char rawSymbol, int x, int y) throws Exception { + public void play(char rawSymbol, int x, int y) { Symbol symbol = Symbol.fromChar(rawSymbol); //if first move if (lastSymbol == null && symbol == Symbol.O) { - throw new Exception("Invalid first player"); + throw new IllegalArgumentException("Invalid first player"); } //if not first move but player repeated else if (symbol.equals(lastSymbol)) { - throw new Exception("Invalid next player"); + throw new IllegalArgumentException("Invalid next player"); } //if not first move but play on an already played tile else if (board.tileAt(x, y).getSymbol() != null) { - throw new Exception("Invalid position"); + throw new IllegalArgumentException("Invalid position"); } // update game state diff --git a/java/test/GameTest.java b/java/test/GameTest.java index 2e1689a..c1da8f6 100644 --- a/java/test/GameTest.java +++ b/java/test/GameTest.java @@ -19,12 +19,12 @@ void InitializeGame() { @Test void NotAllowPlayerOToPlayFirst() { - assertThrows(Exception.class, () -> game.play('O', 0, 0)); + assertThrows(IllegalArgumentException.class, () -> game.play('O', 0, 0)); } @Test void NotAllowPlayerXToPlayTwiceInARow() { - assertThrows(Exception.class, () -> { + assertThrows(IllegalArgumentException.class, () -> { game.play('X', 0, 0); game.play('X', 1, 0); }); @@ -32,7 +32,7 @@ void NotAllowPlayerXToPlayTwiceInARow() { @Test void NotAllowPlayerToPlayInLastPlayedPosition() { - assertThrows(Exception.class, () -> { + assertThrows(IllegalArgumentException.class, () -> { game.play('X', 0, 0); game.play('O', 0, 0); }); @@ -40,7 +40,7 @@ void NotAllowPlayerToPlayInLastPlayedPosition() { @Test void NotAllowPlayerToPlayInAnyPlayedPosition() { - assertThrows(Exception.class, () -> { + assertThrows(IllegalArgumentException.class, () -> { game.play('X', 0, 0); game.play('O', 1, 0); game.play('X', 0, 0); @@ -48,7 +48,7 @@ void NotAllowPlayerToPlayInAnyPlayedPosition() { } @Test - void DeclareNoWinnerWithUnfinishedGrid() throws Exception { + void DeclareNoWinnerWithUnfinishedGrid() { game.play('X', 0, 0); game.play('O', 1, 0); game.play('X', 0, 1); @@ -60,7 +60,7 @@ void DeclareNoWinnerWithUnfinishedGrid() throws Exception { } @Test - void DeclareNoWinnerWithTiedGrid() throws Exception { + void DeclareNoWinnerWithTiedGrid() { game.play('X', 1, 1); game.play('O', 0, 0); game.play('X', 0, 2); @@ -77,7 +77,7 @@ void DeclareNoWinnerWithTiedGrid() throws Exception { } @Test - void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { + void DeclarePlayerXAsAWinnerIfThreeInTopRow() { game.play('X', 0, 0); game.play('O', 1, 0); game.play('X', 0, 1); @@ -91,7 +91,7 @@ void DeclarePlayerXAsAWinnerIfThreeInTopRow() throws Exception { } @Test - void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception { + void DeclarePlayerOAsAWinnerIfThreeInTopRow() { game.play('X', 2, 2); game.play('O', 0, 0); game.play('X', 1, 0); @@ -106,7 +106,7 @@ void DeclarePlayerOAsAWinnerIfThreeInTopRow() throws Exception { } @Test - void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception { + void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() { game.play('X', 1, 0); game.play('O', 0, 0); game.play('X', 1, 1); @@ -120,7 +120,7 @@ void DeclarePlayerXAsAWinnerIfThreeInMiddleRow() throws Exception { } @Test - void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception { + void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() { game.play('X', 0, 0); game.play('O', 1, 0); game.play('X', 2, 0); @@ -135,7 +135,7 @@ void DeclarePlayerOAsAWinnerIfThreeInMiddleRow() throws Exception { } @Test - void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception { + void DeclarePlayerXAsAWinnerIfThreeInBottomRow() { game.play('X', 2, 0); game.play('O', 0, 0); game.play('X', 2, 1); @@ -149,7 +149,7 @@ void DeclarePlayerXAsAWinnerIfThreeInBottomRow() throws Exception { } @Test - void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception { + void DeclarePlayerOAsAWinnerIfThreeInBottomRow() { game.play('X', 0, 0); game.play('O', 2, 0); game.play('X', 1, 0); @@ -164,14 +164,14 @@ void DeclarePlayerOAsAWinnerIfThreeInBottomRow() throws Exception { } @Test - void NotAllowPlayingIllegalSymbol() throws Exception { + void NotAllowPlayingIllegalSymbol() { game.play('X', 0, 0); game.play('O', 2, 0); assertThrows(IllegalArgumentException.class, () -> game.play('Y', 1, 0)); } @Test - void AllowPlayingLowercaseVariants() throws Exception { + void AllowPlayingLowercaseVariants() { game.play('X', 0, 0); game.play('O', 2, 0); assertDoesNotThrow(() -> game.play('x', 1, 0)); @@ -179,7 +179,7 @@ void AllowPlayingLowercaseVariants() throws Exception { } @Test - void DeclarePlayerXAsAWinnerIfThreeInLeftColumn() throws Exception { + void DeclarePlayerXAsAWinnerIfThreeInLeftColumn() { game.play('X', 0, 0); game.play('O', 1, 0); game.play('X', 0, 1); @@ -192,7 +192,7 @@ void DeclarePlayerXAsAWinnerIfThreeInLeftColumn() throws Exception { @Test @Disabled("Not implemented") - void DeclarePlayerXAsAWinnerIfThreeDiagonallyRightDown() throws Exception { + void DeclarePlayerXAsAWinnerIfThreeDiagonallyRightDown() { game.play('X', 0, 0); game.play('O', 1, 0); game.play('X', 1, 1); From a5ef43a80efe17ce7802fc5d0dbef7174252b904 Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 15:06:02 +0200 Subject: [PATCH 16/17] refactor board public interface --- java/src/Board.java | 24 ++++++++++++++++-------- java/src/Game.java | 40 ++++++++++++++++++++-------------------- java/src/Tile.java | 7 ++----- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/java/src/Board.java b/java/src/Board.java index cbf2dea..0150908 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -14,13 +14,12 @@ public Board() { } } - public Tile tileAt(int x, int y) { - for (Tile t : plays) { - if (t.getX() == x && t.getY() == y) { - return t; - } - } - return null; + public Symbol getSymbolAt(int x, int y) { + return this.tileAt(x, y).getSymbol(); + } + + public void setSymbolAt(int x, int y, Symbol symbol) { + this.tileAt(x, y).setSymbol(symbol); } public String print() { @@ -28,10 +27,19 @@ public String print() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { Tile currentTile = this.tileAt(i, j); - sb.append(Optional.ofNullable(currentTile.getSymbol()).map(Symbol::getAsChar).orElse(' ')); + sb.append(Optional.ofNullable(currentTile).map(Tile::getSymbol).map(Symbol::getAsChar).orElse(' ')); } sb.append("\n"); } return sb.toString(); } + + private Tile tileAt(int x, int y) { + for (Tile t : plays) { + if (t.getX() == x && t.getY() == y) { + return t; + } + } + return null; + } } diff --git a/java/src/Game.java b/java/src/Game.java index 2475b9a..f52ef2b 100644 --- a/java/src/Game.java +++ b/java/src/Game.java @@ -18,46 +18,46 @@ else if (symbol.equals(lastSymbol)) { } //if not first move but play on an already played tile - else if (board.tileAt(x, y).getSymbol() != null) { + else if (board.getSymbolAt(x, y) != null) { throw new IllegalArgumentException("Invalid position"); } // update game state lastSymbol = symbol; - board.tileAt(x, y).setSymbol(symbol); + board.setSymbolAt(x, y, symbol); } public Optional computeWinner() { //if the positions in first row are taken - if (board.tileAt(0, 0).getSymbol() != null && - board.tileAt(0, 1).getSymbol() != null && - board.tileAt(0, 2).getSymbol() != null) { + if (board.getSymbolAt(0, 0) != null && + board.getSymbolAt(0, 1) != null && + board.getSymbolAt(0, 2) != null) { //if first row is full with same symbol - if (board.tileAt(0, 0).getSymbol().equals(board.tileAt(0, 1).getSymbol()) && - board.tileAt(0, 2).getSymbol().equals(board.tileAt(0, 1).getSymbol())) { - return Optional.ofNullable(board.tileAt(0, 0).getSymbol()); + if (board.getSymbolAt(0, 0).equals(board.getSymbolAt(0, 1)) && + board.getSymbolAt(0, 2).equals(board.getSymbolAt(0, 1))) { + return Optional.ofNullable(board.getSymbolAt(0, 0)); } } //if the positions in first row are taken - if (board.tileAt(1, 0).getSymbol() != null && - board.tileAt(1, 1).getSymbol() != null && - board.tileAt(1, 2).getSymbol() != null) { + if (board.getSymbolAt(1, 0) != null && + board.getSymbolAt(1, 1) != null && + board.getSymbolAt(1, 2) != null) { //if middle row is full with same symbol - if (board.tileAt(1, 0).getSymbol().equals(board.tileAt(1, 1).getSymbol()) && - board.tileAt(1, 2).getSymbol().equals(board.tileAt(1, 1).getSymbol())) { - return Optional.ofNullable(board.tileAt(1, 0).getSymbol()); + if (board.getSymbolAt(1, 0).equals(board.getSymbolAt(1, 1)) && + board.getSymbolAt(1, 2).equals(board.getSymbolAt(1, 1))) { + return Optional.ofNullable(board.getSymbolAt(1, 0)); } } //if the positions in first row are taken - if (board.tileAt(2, 0).getSymbol() != null && - board.tileAt(2, 1).getSymbol() != null && - board.tileAt(2, 2).getSymbol() != null) { + if (board.getSymbolAt(2, 0) != null && + board.getSymbolAt(2, 1) != null && + board.getSymbolAt(2, 2) != null) { //if middle row is full with same symbol - if (board.tileAt(2, 0).getSymbol().equals(board.tileAt(2, 1).getSymbol()) && - board.tileAt(2, 2).getSymbol().equals(board.tileAt(2, 1).getSymbol())) { - return Optional.ofNullable(board.tileAt(2, 0).getSymbol()); + if (board.getSymbolAt(2, 0).equals(board.getSymbolAt(2, 1)) && + board.getSymbolAt(2, 2).equals(board.getSymbolAt(2, 1))) { + return Optional.ofNullable(board.getSymbolAt(2, 0)); } } diff --git a/java/src/Tile.java b/java/src/Tile.java index 898fadb..378a666 100644 --- a/java/src/Tile.java +++ b/java/src/Tile.java @@ -1,15 +1,12 @@ import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.Setter; @Getter +@RequiredArgsConstructor public class Tile { private final int x; private final int y; @Setter private Symbol symbol; - - public Tile(int x, int y) { - this.x = x; - this.y = y; - } } From fe17acbfd15454f074e86a3beb8821aa4d075e17 Mon Sep 17 00:00:00 2001 From: Adrien Wehrung Date: Tue, 7 May 2024 16:10:50 +0200 Subject: [PATCH 17/17] fix null pointer --- java/src/Board.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/src/Board.java b/java/src/Board.java index 0150908..88500b6 100644 --- a/java/src/Board.java +++ b/java/src/Board.java @@ -15,11 +15,11 @@ public Board() { } public Symbol getSymbolAt(int x, int y) { - return this.tileAt(x, y).getSymbol(); + return Optional.ofNullable(this.tileAt(x, y)).map(Tile::getSymbol).orElse(null); } public void setSymbolAt(int x, int y, Symbol symbol) { - this.tileAt(x, y).setSymbol(symbol); + Optional.ofNullable(this.tileAt(x, y)).ifPresent(t -> t.setSymbol(symbol)); } public String print() {