Introduce CellLocation enum in model and viewmodel layer
This commit is contained in:
parent
7736fb5101
commit
2294c95517
@ -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<CellLocation, CellState> getCells();
|
||||
|
||||
List<Integer> getAvailableFields();
|
||||
List<CellLocation> getAvailableFields();
|
||||
|
||||
boolean isFinished();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<CellLocation, CellState> 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<CellLocation, CellState> 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<CellLocation, CellState> getCells() {
|
||||
return cells;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> getAvailableFields() {
|
||||
final LinkedList<Integer> emptyFields = new LinkedList<>();
|
||||
for (int i = 0; i < this.cells.length; i++) {
|
||||
if (this.cells[i] == CellState.EMPTY) {
|
||||
emptyFields.add(i);
|
||||
public List<CellLocation> getAvailableFields() {
|
||||
final LinkedList<CellLocation> emptyFields = new LinkedList<>();
|
||||
for (Map.Entry<CellLocation, CellState> 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<CellState>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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<CellLocation, ITicTacToeGame> subGames;
|
||||
private Map<CellLocation, GameState> 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<GameState>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<CellLocation, TicTacToePanelViewModel> subGameViewModels;
|
||||
private final IUltimateTicTacToe ultimateTicTacToe;
|
||||
private Consumer<String> gameResultCallback;
|
||||
private Consumer<String> 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<String> gameResultCallback) {
|
||||
|
Loading…
Reference in New Issue
Block a user