Created a somewhat generic TurnBasedGame thread. Temporary UI that only works for TicTacToe rn. Added a LocalPlayer with the intent to add more players

This commit is contained in:
2025-11-27 21:44:55 +01:00
parent 0cb025edb9
commit 3b6017b369
9 changed files with 319 additions and 4 deletions

View File

@@ -0,0 +1,41 @@
package org.toop.game;
import org.toop.game.interfaces.IPlayable;
import org.toop.game.interfaces.IPlayableR;
import org.toop.game.records.Move;
import java.util.Arrays;
public abstract class GameR implements IPlayableR {
public static final Integer EMPTY = null; // Constant
private final int rowSize;
private final int columnSize;
private final Integer[] board;
protected GameR(int rowSize, int columnSize) {
assert rowSize > 0 && columnSize > 0;
this.rowSize = rowSize;
this.columnSize = columnSize;
board = new Integer[rowSize * columnSize];
Arrays.fill(board, EMPTY);
}
protected GameR(GameR other) {
rowSize = other.rowSize;
columnSize = other.columnSize;
board = Arrays.copyOf(other.board, other.board.length);
}
public int getRowSize() {return this.rowSize;}
public int getColumnSize() {return this.columnSize;}
public Integer[] getBoard() {return this.board;}
protected void setBoard(int position, int player){this.board[position] = player;}
}

View File

@@ -0,0 +1,31 @@
package org.toop.game;
public abstract class TurnBasedGameR extends GameR {
private final int playerCount; // How many players are playing
private int turn = 0; // What turn it is in the game
protected TurnBasedGameR(int rowSize, int columnSize, int playerCount) {
super(rowSize, columnSize);
this.playerCount = playerCount;
}
protected TurnBasedGameR(TurnBasedGameR other) {
super(other);
playerCount = other.playerCount;
turn = other.turn;
}
public int getPlayerCount(){return this.playerCount;}
protected void nextTurn() {
turn += 1;
}
public int getCurrentTurn() {
return turn % playerCount;
}
protected void setBoard(int position) {
super.setBoard(position, getCurrentTurn());
}
}

View File

@@ -0,0 +1,9 @@
package org.toop.game.interfaces;
import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
public interface IPlayableR {
Integer[] getLegalMoves();
GameState play(int move);
}

View File

@@ -0,0 +1,103 @@
package org.toop.game.tictactoe;
import org.toop.game.TurnBasedGame;
import org.toop.game.TurnBasedGameR;
import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import java.util.ArrayList;
import java.util.Objects;
public final class TicTacToeR extends TurnBasedGameR {
private int movesLeft;
public TicTacToeR() {
super(3, 3, 2);
movesLeft = this.getBoard().length;
}
public TicTacToeR(TicTacToeR other) {
super(other);
movesLeft = other.movesLeft;
}
@Override
public Integer[] getLegalMoves() {
final ArrayList<Integer> legalMoves = new ArrayList<Integer>();
final char currentValue = getCurrentValue();
for (int i = 0; i < this.getBoard().length; i++) {
if (Objects.equals(this.getBoard()[i], EMPTY)) {
legalMoves.add(i);
}
}
return legalMoves.toArray(new Integer[0]);
}
@Override
public GameState play(int move) {
assert move >= 0 && move < this.getBoard().length;
// TODO: Make sure this move is allowed, maybe on the board side?
this.setBoard(move);
movesLeft--;
nextTurn();
if (checkForWin()) {
return GameState.WIN;
}
if (movesLeft <= 2) {
if (movesLeft <= 0 || checkForEarlyDraw()) {
return GameState.DRAW;
}
}
return GameState.NORMAL;
}
private boolean checkForWin() {
// Horizontal
for (int i = 0; i < 3; i++) {
final int index = i * 3;
if (!Objects.equals(this.getBoard()[index], EMPTY)
&& Objects.equals(this.getBoard()[index], this.getBoard()[index + 1])
&& Objects.equals(this.getBoard()[index], this.getBoard()[index + 2])) {
return true;
}
}
// Vertical
for (int i = 0; i < 3; i++) {
if (!Objects.equals(this.getBoard()[i], EMPTY) && Objects.equals(this.getBoard()[i], this.getBoard()[i + 3]) && Objects.equals(this.getBoard()[i], this.getBoard()[i + 6])) {
return true;
}
}
// B-Slash
if (!Objects.equals(this.getBoard()[0], EMPTY) && Objects.equals(this.getBoard()[0], this.getBoard()[4]) && Objects.equals(this.getBoard()[0], this.getBoard()[8])) {
return true;
}
// F-Slash
return !Objects.equals(this.getBoard()[2], EMPTY) && Objects.equals(this.getBoard()[2], this.getBoard()[4]) && Objects.equals(this.getBoard()[2], this.getBoard()[6]);
}
private boolean checkForEarlyDraw() {
for (final int move : this.getLegalMoves()) {
final TicTacToeR copy = new TicTacToeR(this);
if (copy.play(move) == GameState.WIN || !copy.checkForEarlyDraw()) {
return false;
}
}
return true;
}
private char getCurrentValue() {
return this.getCurrentTurn() == 0 ? 'X' : 'O';
}
}