From 2294c95517b69cd642f1ecbbe73a8cdec4acec09 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 3 Nov 2021 23:23:13 +0100 Subject: [PATCH] Introduce CellLocation enum in model and viewmodel layer --- .../model/ITicTacToeGame.java | 8 ++- .../model/IUltimateTicTacToe.java | 7 +- .../model/TicTacToeGame.java | 69 +++++++++---------- .../model/UltimateTicTacToe.java | 52 +++++++------- .../model/definitions/CellLocation.java | 24 +++++++ .../UltimateTicTacToePanelViewModel.java | 39 +++++++---- 6 files changed, 121 insertions(+), 78 deletions(-) create mode 100644 src/main/java/de/icaotix/ultimatetictactoe/model/definitions/CellLocation.java diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/ITicTacToeGame.java b/src/main/java/de/icaotix/ultimatetictactoe/model/ITicTacToeGame.java index 088e6d2..500c27a 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/model/ITicTacToeGame.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/ITicTacToeGame.java @@ -1,16 +1,18 @@ package de.icaotix.ultimatetictactoe.model; +import de.icaotix.ultimatetictactoe.model.definitions.CellLocation; import de.icaotix.ultimatetictactoe.model.definitions.CellState; import de.icaotix.ultimatetictactoe.model.definitions.GameState; import java.util.List; +import java.util.Map; public interface ITicTacToeGame { - void setCellState(int cell, CellState state); + void setCellState(CellLocation cell, CellState state); - CellState[] getCells(); + Map getCells(); - List getAvailableFields(); + List getAvailableFields(); boolean isFinished(); diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/IUltimateTicTacToe.java b/src/main/java/de/icaotix/ultimatetictactoe/model/IUltimateTicTacToe.java index b0ca2a0..c34d6bc 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/model/IUltimateTicTacToe.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/IUltimateTicTacToe.java @@ -1,18 +1,19 @@ package de.icaotix.ultimatetictactoe.model; +import de.icaotix.ultimatetictactoe.model.definitions.CellLocation; import de.icaotix.ultimatetictactoe.model.definitions.GameState; import de.icaotix.ultimatetictactoe.model.definitions.Player; public interface IUltimateTicTacToe { void initialize(); - ITicTacToeGame getSubGame(int id); + ITicTacToeGame getSubGame(CellLocation location); - int getActiveField(); + CellLocation getActiveField(); Player getCurrentPlayer(); GameState getGlobalGameState(); - void doPlayerMove(int cell); + void doPlayerMove(CellLocation cell); } diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/TicTacToeGame.java b/src/main/java/de/icaotix/ultimatetictactoe/model/TicTacToeGame.java index b7f8a11..bffce44 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/model/TicTacToeGame.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/TicTacToeGame.java @@ -1,42 +1,41 @@ package de.icaotix.ultimatetictactoe.model; +import de.icaotix.ultimatetictactoe.model.definitions.CellLocation; import de.icaotix.ultimatetictactoe.model.definitions.CellState; import de.icaotix.ultimatetictactoe.model.definitions.GameState; -import java.util.Arrays; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; +import java.util.*; public class TicTacToeGame implements ITicTacToeGame { - public static final Integer[][] winningCombinations = new Integer[][]{ - {0, 1, 2}, - {3, 4, 5}, - {6, 7, 8}, - {0, 3, 6}, - {1, 4, 7}, - {2, 5, 8}, - {0, 4, 8}, - {2, 4, 6} + public static final CellLocation[][] winningCombinations = new CellLocation[][]{ + {CellLocation.TL, CellLocation.TC, CellLocation.TR}, + {CellLocation.ML, CellLocation.MC, CellLocation.MR}, + {CellLocation.BL, CellLocation.BC, CellLocation.BR}, + {CellLocation.TL, CellLocation.ML, CellLocation.BL}, + {CellLocation.TC, CellLocation.MC, CellLocation.BC}, + {CellLocation.TR, CellLocation.MR, CellLocation.BR}, + {CellLocation.TL, CellLocation.MC, CellLocation.BR}, + {CellLocation.TR, CellLocation.MC, CellLocation.BL} }; - private final CellState[] cells; + private final Map cells; private GameState state; public TicTacToeGame() { - this.cells = new CellState[9]; - Arrays.fill(cells, CellState.EMPTY); + this.cells = new HashMap<>(); + for (CellLocation value : CellLocation.values) { + this.cells.put(value, CellState.EMPTY); + } this.state = GameState.RUNNING; } // DEBUG public static void printField(ITicTacToeGame game) { - final var cells = game.getCells(); System.out.println("--------------CURRENT STATE--------------"); - for (int i = 0; i < cells.length; i++) { - System.out.print(cells[i] + " "); - if ((i + 1) % 3 == 0) { + for (Map.Entry entry : game.getCells().entrySet()) { + System.out.print(entry.getValue() + " "); + if ((entry.getKey().ordinal() + 1) % 3 == 0) { System.out.println(); } } @@ -46,25 +45,25 @@ public class TicTacToeGame implements ITicTacToeGame { } @Override - public void setCellState(int cell, CellState state) { + public void setCellState(CellLocation cell, CellState state) { if (this.state != GameState.RUNNING) return; - if (this.cells[cell] == CellState.EMPTY) { - this.cells[cell] = state; + if (this.cells.get(cell) == CellState.EMPTY) { + this.cells.put(cell, state); updateGameState(); } } @Override - public CellState[] getCells() { + public Map getCells() { return cells; } @Override - public List getAvailableFields() { - final LinkedList emptyFields = new LinkedList<>(); - for (int i = 0; i < this.cells.length; i++) { - if (this.cells[i] == CellState.EMPTY) { - emptyFields.add(i); + public List getAvailableFields() { + final LinkedList emptyFields = new LinkedList<>(); + for (Map.Entry cellStateEntry : this.cells.entrySet()) { + if (cellStateEntry.getValue() == CellState.EMPTY) { + emptyFields.add(cellStateEntry.getKey()); } } return emptyFields; @@ -81,13 +80,13 @@ public class TicTacToeGame implements ITicTacToeGame { } private void updateGameState() { - for (Integer[] winningCombination : winningCombinations) { + for (CellLocation[] winningCombination : winningCombinations) { final var interestingStates = new HashSet(); - interestingStates.add(this.cells[winningCombination[0]]); - interestingStates.add(this.cells[winningCombination[1]]); - interestingStates.add(this.cells[winningCombination[2]]); + interestingStates.add(this.cells.get(winningCombination[0])); + interestingStates.add(this.cells.get(winningCombination[1])); + interestingStates.add(this.cells.get(winningCombination[2])); if (interestingStates.size() == 1 && !interestingStates.contains(CellState.EMPTY)) { - switch (this.cells[winningCombination[0]]) { + switch (this.cells.get(winningCombination[0])) { case O -> this.state = GameState.O_WON; case X -> this.state = GameState.X_WON; } @@ -95,7 +94,7 @@ public class TicTacToeGame implements ITicTacToeGame { } } - if (Arrays.stream(this.cells).allMatch(cellState -> cellState != CellState.EMPTY)) { + if (!this.cells.containsValue(CellState.EMPTY)) { this.state = GameState.DRAW; } } diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/UltimateTicTacToe.java b/src/main/java/de/icaotix/ultimatetictactoe/model/UltimateTicTacToe.java index d2c1269..69fdead 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/model/UltimateTicTacToe.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/UltimateTicTacToe.java @@ -1,18 +1,20 @@ package de.icaotix.ultimatetictactoe.model; +import de.icaotix.ultimatetictactoe.model.definitions.CellLocation; import de.icaotix.ultimatetictactoe.model.definitions.CellState; import de.icaotix.ultimatetictactoe.model.definitions.GameState; import de.icaotix.ultimatetictactoe.model.definitions.Player; -import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; public class UltimateTicTacToe implements IUltimateTicTacToe { - private ITicTacToeGame[] subGames; - private GameState[] masterGameStates; + private Map subGames; + private Map masterGameStates; private Player currentPlayer; - private int activeField; + private CellLocation activeField; private GameState globalGameState; public UltimateTicTacToe() { @@ -21,26 +23,26 @@ public class UltimateTicTacToe implements IUltimateTicTacToe { @Override public void initialize() { - this.subGames = new ITicTacToeGame[9]; - for (int i = 0; i < this.subGames.length; i++) { - this.subGames[i] = new TicTacToeGame(); + this.subGames = new HashMap<>(); + this.masterGameStates = new HashMap<>(); + for (CellLocation value : CellLocation.values) { + this.subGames.put(value, new TicTacToeGame()); + this.masterGameStates.put(value, GameState.RUNNING); } - this.masterGameStates = new GameState[9]; - Arrays.fill(this.masterGameStates, GameState.RUNNING); // X always starts in the center field this.currentPlayer = Player.X; - this.activeField = 4; + this.activeField = CellLocation.MC; this.globalGameState = GameState.RUNNING; } @Override - public ITicTacToeGame getSubGame(int id) { - return this.subGames[id]; + public ITicTacToeGame getSubGame(CellLocation location) { + return this.subGames.get(location); } @Override - public int getActiveField() { + public CellLocation getActiveField() { return activeField; } @@ -55,13 +57,13 @@ public class UltimateTicTacToe implements IUltimateTicTacToe { } @Override - public void doPlayerMove(int cell) { + public void doPlayerMove(CellLocation cell) { if (!getSubGame(this.activeField).getAvailableFields().contains(cell)) return; var nextCellState = this.currentPlayer == Player.X ? CellState.X : CellState.O; getSubGame(this.activeField).setCellState(cell, nextCellState); - this.masterGameStates[this.activeField] = getSubGame(this.activeField).getState(); + this.masterGameStates.put(this.activeField, getSubGame(this.activeField).getState()); updateGameState(); @@ -73,13 +75,13 @@ public class UltimateTicTacToe implements IUltimateTicTacToe { } private void updateGameState() { - for (Integer[] winningCombination : TicTacToeGame.winningCombinations) { + for (CellLocation[] winningCombination : TicTacToeGame.winningCombinations) { final var interestingStates = new HashSet(); - interestingStates.add(this.masterGameStates[winningCombination[0]]); - interestingStates.add(this.masterGameStates[winningCombination[1]]); - interestingStates.add(this.masterGameStates[winningCombination[2]]); + interestingStates.add(this.masterGameStates.get(winningCombination[0])); + interestingStates.add(this.masterGameStates.get(winningCombination[1])); + interestingStates.add(this.masterGameStates.get(winningCombination[2])); if (interestingStates.size() == 1 && !interestingStates.contains(GameState.RUNNING)) { - switch (this.masterGameStates[winningCombination[0]]) { + switch (this.masterGameStates.get(winningCombination[0])) { case O_WON -> this.globalGameState = GameState.O_WON; case X_WON -> this.globalGameState = GameState.X_WON; } @@ -87,16 +89,18 @@ public class UltimateTicTacToe implements IUltimateTicTacToe { } } - if (Arrays.stream(this.masterGameStates).allMatch(gameState -> gameState != GameState.RUNNING)) { + if (!this.masterGameStates.containsValue(GameState.RUNNING)) { this.globalGameState = GameState.DRAW; } } - private int findNextAvailableSubGame(int startingIndex) { - while (this.subGames[startingIndex].isFinished()) { + private CellLocation findNextAvailableSubGame(CellLocation startingLocation) { + int startingIndex = startingLocation.ordinal(); + while (this.subGames.get(startingLocation).isFinished()) { startingIndex = (startingIndex + 1) % 9; + startingLocation = CellLocation.values[startingIndex]; } - return startingIndex; + return startingLocation; } } diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/CellLocation.java b/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/CellLocation.java new file mode 100644 index 0000000..e4d55b4 --- /dev/null +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/CellLocation.java @@ -0,0 +1,24 @@ +package de.icaotix.ultimatetictactoe.model.definitions; + +public enum CellLocation { + TL("Top Left"), + TC("Top Center"), + TR("Top Right"), + ML("Middle Left"), + MC("Middle Center"), + MR("Middle Right"), + BL("Bottom Left"), + BC("Bottom Center"), + BR("Bottom Right"); + + public static final CellLocation[] values = values(); + private final String longName; + + CellLocation(String longName) { + this.longName = longName; + } + + public String getLongName() { + return longName; + } +} diff --git a/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/UltimateTicTacToePanelViewModel.java b/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/UltimateTicTacToePanelViewModel.java index 61aaf18..2a93fbc 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/UltimateTicTacToePanelViewModel.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/UltimateTicTacToePanelViewModel.java @@ -2,21 +2,27 @@ package de.icaotix.ultimatetictactoe.viewmodel; import de.icaotix.ultimatetictactoe.model.IUltimateTicTacToe; +import de.icaotix.ultimatetictactoe.model.definitions.CellLocation; import de.icaotix.ultimatetictactoe.model.definitions.GameState; +import java.util.HashMap; +import java.util.Map; import java.util.function.Consumer; public class UltimateTicTacToePanelViewModel { - private final TicTacToePanelViewModel[] subGameViewModels; + private final Map subGameViewModels; private final IUltimateTicTacToe ultimateTicTacToe; private Consumer gameResultCallback; private Consumer currentPlayerCallback; public UltimateTicTacToePanelViewModel(IUltimateTicTacToe ultimateTicTacToe, ViewModelFactory viewModelFactory) { - this.subGameViewModels = new TicTacToePanelViewModel[9]; - for (int i = 0; i < this.subGameViewModels.length; i++) { - this.subGameViewModels[i] = viewModelFactory.getTicTacToePanelViewModel(this); + this.subGameViewModels = new HashMap<>(); + for (CellLocation value : CellLocation.values) { + this.subGameViewModels.put( + value, + viewModelFactory.getTicTacToePanelViewModel(this) + ); } this.ultimateTicTacToe = ultimateTicTacToe; } @@ -31,34 +37,41 @@ public class UltimateTicTacToePanelViewModel { } } // Setup next move - final int fieldId = this.ultimateTicTacToe.getActiveField(); - final var availableFields = this.ultimateTicTacToe.getSubGame(fieldId).getAvailableFields(); - this.subGameViewModels[fieldId].activate(availableFields); + final var cellLocation = this.ultimateTicTacToe.getActiveField(); + final var availableFields = this.ultimateTicTacToe + .getSubGame(cellLocation) + .getAvailableFields() + .stream() + .mapToInt(Enum::ordinal) + .boxed() + .toList(); + + this.subGameViewModels.get(cellLocation).activate(availableFields); this.currentPlayerCallback.accept("Current player: " + this.ultimateTicTacToe.getCurrentPlayer().name()); } public void onCellClicked(int cell) { - final int oldActiveGameId = this.ultimateTicTacToe.getActiveField(); - final var oldActiveGameViewModel = this.subGameViewModels[oldActiveGameId]; - final var oldActiveGame = this.ultimateTicTacToe.getSubGame(oldActiveGameId); + final var oldActiveGameLocation = this.ultimateTicTacToe.getActiveField(); + final var oldActiveGameViewModel = this.subGameViewModels.get(oldActiveGameLocation); + final var oldActiveGame = this.ultimateTicTacToe.getSubGame(oldActiveGameLocation); oldActiveGameViewModel.deactivate(); - this.ultimateTicTacToe.doPlayerMove(cell); + this.ultimateTicTacToe.doPlayerMove(CellLocation.values[cell]); if (oldActiveGame.isFinished()) { final String winnerText = oldActiveGame.getState().displayText; oldActiveGameViewModel.setFinishPanel(winnerText); } - final String newText = oldActiveGame.getCells()[cell].displayText; + final String newText = oldActiveGame.getCells().get(CellLocation.values[cell]).displayText; oldActiveGameViewModel.setButtonText(cell, newText); prepareNextMove(); } public TicTacToePanelViewModel getSubGameViewModel(int subGameId) { - return this.subGameViewModels[subGameId]; + return this.subGameViewModels.get(CellLocation.values[subGameId]); } public void setGameResultCallback(Consumer gameResultCallback) {