mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 19:04:49 +00:00
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:
24
app/src/main/java/org/toop/app/game/LocalPlayer.java
Normal file
24
app/src/main/java/org/toop/app/game/LocalPlayer.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package org.toop.app.game;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class LocalPlayer extends Player{
|
||||
private BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
|
||||
|
||||
public LocalPlayer() {}
|
||||
|
||||
@Override
|
||||
public int getMove() {
|
||||
try {
|
||||
return queue.take();
|
||||
}catch (InterruptedException e){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void enqueueMove(int move) {
|
||||
System.out.println(move);
|
||||
queue.offer(move);
|
||||
}
|
||||
}
|
||||
8
app/src/main/java/org/toop/app/game/MoveBehaviour.java
Normal file
8
app/src/main/java/org/toop/app/game/MoveBehaviour.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package org.toop.app.game;
|
||||
|
||||
import org.toop.game.records.Move;
|
||||
|
||||
public interface MoveBehaviour
|
||||
{
|
||||
int getMove();
|
||||
}
|
||||
6
app/src/main/java/org/toop/app/game/Player.java
Normal file
6
app/src/main/java/org/toop/app/game/Player.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package org.toop.app.game;
|
||||
|
||||
import org.toop.game.records.Move;
|
||||
|
||||
public abstract class Player implements MoveBehaviour{
|
||||
}
|
||||
92
app/src/main/java/org/toop/app/game/TurnBasedGameThread.java
Normal file
92
app/src/main/java/org/toop/app/game/TurnBasedGameThread.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package org.toop.app.game;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.toop.app.App;
|
||||
import org.toop.app.canvas.GameCanvas;
|
||||
import org.toop.app.canvas.TicTacToeCanvas;
|
||||
import org.toop.app.widget.WidgetContainer;
|
||||
import org.toop.app.widget.view.GameView;
|
||||
import org.toop.game.TurnBasedGameR;
|
||||
import org.toop.game.enumerators.GameState;
|
||||
import org.toop.game.records.Move;
|
||||
import org.toop.game.tictactoe.TicTacToe;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class TurnBasedGameThread implements Runnable {
|
||||
private final Player[] players; // List of players, can't be changed.
|
||||
private final TurnBasedGameR game; // Reference to game instance
|
||||
|
||||
private final AtomicBoolean isRunning = new AtomicBoolean();
|
||||
|
||||
// TODO: Seperate this from game Thread
|
||||
private final GameView primary = new GameView(null, null, null);
|
||||
private final TicTacToeCanvas canvas;
|
||||
|
||||
public TurnBasedGameThread(Player[] players, TurnBasedGameR game) {
|
||||
// Make sure player list matches expected size
|
||||
if (players.length != game.getPlayerCount()){
|
||||
throw new IllegalArgumentException("players and game's players must have same length");
|
||||
}
|
||||
|
||||
this.players = players;
|
||||
this.game = game;
|
||||
|
||||
Thread thread = new Thread(this::run);
|
||||
thread.start();
|
||||
|
||||
// UI SHIZ TO MOVE
|
||||
canvas = new TicTacToeCanvas(Color.GRAY,
|
||||
(App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3,(c) -> {if (players[game.getCurrentTurn()] instanceof LocalPlayer lp) {lp.enqueueMove(c);}});
|
||||
primary.add(Pos.CENTER, canvas.getCanvas());
|
||||
WidgetContainer.getCurrentView().transitionNext(primary);
|
||||
|
||||
}
|
||||
|
||||
// Move to UI shiz
|
||||
private void drawMove(int move) {
|
||||
if (game.getCurrentTurn() == 1) canvas.drawX(Color.RED, move);
|
||||
else canvas.drawO(Color.BLUE, move);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
isRunning.set(true);
|
||||
|
||||
// Game logic loop
|
||||
while(isRunning.get()) {
|
||||
|
||||
// Get current player
|
||||
Player currentPlayer = players[game.getCurrentTurn()];
|
||||
|
||||
// Get this player's valid moves
|
||||
Integer[] validMoves = game.getLegalMoves();
|
||||
|
||||
// Get player's move, reask if Move is invalid
|
||||
// TODO: Limit amount of retries?
|
||||
int move = currentPlayer.getMove();
|
||||
while (!Arrays.asList(validMoves).contains(move)) {
|
||||
System.out.println("Invalid move");;
|
||||
move = currentPlayer.getMove();
|
||||
}
|
||||
|
||||
// Make move
|
||||
GameState state = game.play(move);
|
||||
drawMove(move);
|
||||
|
||||
if (state != GameState.NORMAL) {
|
||||
if (state == GameState.WIN) {
|
||||
// Someone won
|
||||
} else if (state == GameState.DRAW) {
|
||||
// THere was a draw
|
||||
}
|
||||
isRunning.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUI(){
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
package org.toop.app.widget.view;
|
||||
|
||||
import org.toop.app.GameInformation;
|
||||
import org.toop.app.game.Connect4Game;
|
||||
import org.toop.app.game.ReversiGame;
|
||||
import org.toop.app.game.TicTacToeGameThread;
|
||||
import org.toop.app.game.*;
|
||||
import org.toop.app.widget.Primitive;
|
||||
import org.toop.app.widget.complex.PlayerInfoWidget;
|
||||
import org.toop.app.widget.complex.ViewWidget;
|
||||
import org.toop.app.widget.popup.ErrorPopup;
|
||||
import org.toop.game.tictactoe.TicTacToe;
|
||||
import org.toop.game.tictactoe.TicTacToeAI;
|
||||
import org.toop.game.tictactoe.TicTacToeR;
|
||||
import org.toop.local.AppContext;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
@@ -32,7 +33,7 @@ public class LocalMultiplayerView extends ViewWidget {
|
||||
}
|
||||
|
||||
switch (information.type) {
|
||||
case TICTACTOE -> new TicTacToeGameThread(information);
|
||||
case TICTACTOE -> new TurnBasedGameThread(new Player[]{new LocalPlayer(), new LocalPlayer()}, new TicTacToeR());
|
||||
case REVERSI -> new ReversiGame(information);
|
||||
case CONNECT4 -> new Connect4Game(information);
|
||||
// case BATTLESHIP -> new BattleshipGame(information);
|
||||
|
||||
41
game/src/main/java/org/toop/game/GameR.java
Normal file
41
game/src/main/java/org/toop/game/GameR.java
Normal 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;}
|
||||
|
||||
}
|
||||
31
game/src/main/java/org/toop/game/TurnBasedGameR.java
Normal file
31
game/src/main/java/org/toop/game/TurnBasedGameR.java
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
103
game/src/main/java/org/toop/game/tictactoe/TicTacToeR.java
Normal file
103
game/src/main/java/org/toop/game/tictactoe/TicTacToeR.java
Normal 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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user