commit 85940005630c4ab95769fb3b688e23e28cb88f2e
parent 344c921054e2cf4bf965e3766a766e895f6082fc
Author: Friedel Schon <[email protected]>
Date: Fri, 3 Feb 2023 00:53:29 +0100
tournament idk
Diffstat:
9 files changed, 700 insertions(+), 623 deletions(-)
diff --git a/data/temp/NBLDDMO.csv b/data/temp/NBLDDMO.csv
@@ -0,0 +1,2 @@
+Name Ai1, Name Ai2, Game Turns, Draw, Winner Name, Winner Playing As, Total Game Time, Average Time Per Move Ai1, Average Time Per Move Ai2, Moves Ai1, Moves Ai2
+Johs, H1Linear,59,true,H1Linear,white,12617,247,222,(x2 y2); (x4 y2); (x2 y0); (x6 y1); (x2 y5); (x0 y3); (x5 y5); (x1 y1); (x1 y4); (x4 y0); (x0 y1); (x7 y0); (x7 y3); (x1 y5); (x3 y5); (x6 y5); (x7 y5); (x6 y7); (x2 y6); (x0 y7); (x0 y6); (x2 y7); (x4 y7); (x7 y6),(x3 y2); (x5 y4); (x3 y1); (x5 y1); (x3 y0); (x1 y3); (x2 y4); (x0 y2); (x0 y4); (x1 y0); (x5 y0); (x6 y0); (x2 y1); (x0 y0); (x7 y1); (x7 y2); (x6 y2); (x6 y3); (x6 y4); (x0 y5); (x4 y5); (x7 y4); (x5 y6); (x3 y6); (x1 y6); (x6 y6); (x1 y7); (x3 y7); (x5 y7); (x7 y7),
diff --git a/src/main/java/nl/isygameclient/Headless.java b/src/main/java/nl/isygameclient/Headless.java
@@ -2,14 +2,6 @@ package nl.isygameclient;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
-import nl.isygameclient.models.Ai;
-import nl.isygameclient.models.Game;
-import nl.isygameclient.models.Player;
-import nl.isygameclient.models.PlayerManager;
-import nl.isygameclient.models.games.othello.Othello;
-import nl.isygameclient.util.DataSaver;
-import nl.isygameclient.util.Vector2D;
-
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
@@ -20,141 +12,148 @@ import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
+import nl.isygameclient.models.Ai;
+import nl.isygameclient.models.Game;
+import nl.isygameclient.models.Player;
+import nl.isygameclient.models.PlayerManager;
+import nl.isygameclient.models.games.othello.Othello;
+import nl.isygameclient.util.DataSaver;
+import nl.isygameclient.util.Vector2D;
public class Headless {
- private static final String JSON_FILENAME = "heuristics.json";
- private static final int THREAD_POOL_SIZE = 10;
- private static final int ROUNDS_PER_GAME = 100;
-
- private static long calculateAverage(List<Long> values) {
- long sum = values.stream().mapToLong(Long::longValue).sum();
- return sum / values.size();
- }
-
- private static Map<String, Game> createGames(Map<String, int[][]> heuristics) {
- Map<String, Game> games = new HashMap<>();
- for (Map.Entry<String, int[][]> heuristic1 : heuristics.entrySet()) {
- for (Map.Entry<String, int[][]> heuristic2 : heuristics.entrySet()) {
-
- ArrayList<Player> players = new ArrayList<>();
- players.add(new Ai(heuristic1.getKey(), "black", heuristic1.getValue()));
- players.add(new Ai(heuristic2.getKey(), "white", heuristic2.getValue()));
-
- var playerManager = new PlayerManager(0, players);
- Othello othello = new Othello(playerManager);
- games.put(heuristic1.getKey() + ", " + heuristic2.getKey(), othello);
- }
- }
- return games;
- }
-
- private static String buildDataString(
- String gameName, int gameTurns, boolean isDraw, Player winner, long totalGameTime,
- Map<Player, ArrayList<Long>> playersTimePerMoves,
- Map<Player, ArrayList<Vector2D<Integer, Integer>>> playersMovesMade) {
- StringBuilder builder = new StringBuilder();
-
- String joined = String.join(",", gameName, String.valueOf(gameTurns), String.valueOf(isDraw),
- winner.getPlayerName(), winner.getPlayingAs(), String.valueOf(totalGameTime)) + ",";
- builder.append(joined);
-
- playersTimePerMoves.forEach((k, v) -> {
- var average = calculateAverage(v);
- builder.append(average).append(",");
- });
- playersMovesMade.forEach((k, v) -> {
- String listString = v.stream().map(Object::toString)
- .collect(Collectors.joining("; "));
- builder.append(listString).append(",");
- });
- return builder.toString();
- }
-
- private static Map<String, int[][]> loadHeuristics() {
- try {
- Type mapType = new TypeToken<Map<String, int[][]>>() {
- }.getType();
- String inFile = new String(Files.readAllBytes(Paths.get(JSON_FILENAME)));
- return new Gson().fromJson(inFile, mapType);
- } catch (NoSuchFileException e) {
- System.err.println("NO HEURISTICS JSON HAS BEEN PROVIDED.");
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- public static void main(String[] args) {
- var heuristics = loadHeuristics();
- if (heuristics == null) {
- System.err.println("No heuristics in file.");
- return;
- }
-
- var executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
- var games = createGames(heuristics);
- var dataSaver = new DataSaver();
- dataSaver.saveData("Name Ai1, Name Ai2, Game Turns, Draw, Winner Name, Winner Playing As, Total Game Time, Average Time Per Move Ai1, Average Time Per Move Ai2, Moves Ai1, Moves Ai2");
- for (Map.Entry<String, Game> game : games.entrySet()) {
- Runnable simulation = new Simulation(game.getKey(), game.getValue(), ROUNDS_PER_GAME, new ReentrantLock(), dataSaver);
- executor.execute(simulation);
- }
- executor.shutdown();
-
- while (!executor.isTerminated()) {
- }
-
- System.out.println("All simulations completed successfully.");
- }
-
- private record Simulation(String gameName, Game game, int rounds, Lock lock, DataSaver dataSaver) implements Runnable {
- @Override
- public void run() {
- System.out.println("Starting simulation: " + gameName);
-
- var playerManager = game.getPlayerManager();
- for (int i = 0; i < rounds; i++) {
- System.out.println("Start round" + i + " " + gameName);
-
- Map<Player, ArrayList<Long>> playersTimePerMoves = new HashMap<>();
- Map<Player, ArrayList<Vector2D<Integer, Integer>>> PlayersMovesMade = new HashMap<>();
-
- long totalGameTime = 0;
- int gameTurns = 0;
- while (!game.isGameOver()) {
- gameTurns++;
- var currentPlayer = playerManager.getCurrentPlayer();
- if (game.getValidMoves(currentPlayer).isEmpty()) {
- playerManager.nextPlayer();
- continue;
- }
-
- final long startTime = System.currentTimeMillis();
- var move = currentPlayer.onPlayerTurn();
- game.move(currentPlayer, move);
- final long endTime = System.currentTimeMillis();
- final long deltaTime = endTime - startTime;
- totalGameTime += deltaTime;
-
- PlayersMovesMade.computeIfAbsent(currentPlayer, k -> new ArrayList<>()).add(move);
- playersTimePerMoves.computeIfAbsent(currentPlayer, k -> new ArrayList<>()).add(deltaTime);
- }
-
- var winner = game.getWinners().get(0);
- var data = buildDataString(gameName, gameTurns, game.isDraw(), winner, totalGameTime, playersTimePerMoves, PlayersMovesMade);
-
- lock.lock();
- try {
- dataSaver.saveData(data);
- } finally {
- lock.unlock();
- }
- game.restart();
- System.out.println("end round" + i + " " + gameName);
- }
- System.out.println(gameName + " has finished");
- }
- }
+ private static final String JSON_FILENAME = "heuristics.json";
+ private static final int THREAD_POOL_SIZE = 10;
+ private static final int ROUNDS_PER_GAME = 100;
+
+ private static long calculateAverage(List<Long> values) {
+ long sum = values.stream().mapToLong(Long::longValue).sum();
+ return sum / values.size();
+ }
+
+ private static Map<String, Game> createGames(Map<String, int[][]> heuristics) {
+ Map<String, Game> games = new HashMap<>();
+ for (Map.Entry<String, int[][]> heuristic1 : heuristics.entrySet()) {
+ for (Map.Entry<String, int[][]> heuristic2 : heuristics.entrySet()) {
+
+ ArrayList<Player> players = new ArrayList<>();
+ players.add(new Ai(heuristic1.getKey(), "black", heuristic1.getValue()));
+ players.add(new Ai(heuristic2.getKey(), "white", heuristic2.getValue()));
+
+ var playerManager = new PlayerManager(0, players);
+ Othello othello = new Othello(playerManager);
+ games.put(heuristic1.getKey() + ", " + heuristic2.getKey(), othello);
+ }
+ }
+ return games;
+ }
+
+ private static String buildDataString(
+ String gameName, int gameTurns, boolean isDraw, Player winner, long totalGameTime,
+ Map<Player, ArrayList<Long>> playersTimePerMoves,
+ Map<Player, ArrayList<Vector2D<Integer, Integer>>> playersMovesMade) {
+ StringBuilder builder = new StringBuilder();
+
+ String joined = String.join(",", gameName, String.valueOf(gameTurns), String.valueOf(isDraw),
+ winner.getPlayerName(), winner.getPlayingAs(), String.valueOf(totalGameTime)) +
+ ",";
+ builder.append(joined);
+
+ playersTimePerMoves.forEach((k, v) -> {
+ var average = calculateAverage(v);
+ builder.append(average).append(",");
+ });
+ playersMovesMade.forEach((k, v) -> {
+ String listString = v.stream().map(Object::toString).collect(Collectors.joining("; "));
+ builder.append(listString).append(",");
+ });
+ return builder.toString();
+ }
+
+ private static Map<String, int[][]> loadHeuristics() {
+ try {
+ Type mapType = new TypeToken<Map<String, int[][]>>() {
+ }.getType();
+ String inFile = new String(Files.readAllBytes(Paths.get(JSON_FILENAME)));
+ return new Gson().fromJson(inFile, mapType);
+ } catch (NoSuchFileException e) {
+ System.err.println("NO HEURISTICS JSON HAS BEEN PROVIDED.");
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static void main(String[] args) {
+ var heuristics = loadHeuristics();
+ if (heuristics == null) {
+ System.err.println("No heuristics in file.");
+ return;
+ }
+
+ var executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
+ var games = createGames(heuristics);
+ var dataSaver = new DataSaver();
+ dataSaver.saveData("Name Ai1, Name Ai2, Game Turns, Draw, Winner Name, Winner Playing As, Total Game Time, Average Time Per Move Ai1, Average Time Per Move Ai2, Moves Ai1, Moves Ai2");
+ for (Map.Entry<String, Game> game : games.entrySet()) {
+ Runnable simulation = new Simulation(game.getKey(), game.getValue(), ROUNDS_PER_GAME, new ReentrantLock(), dataSaver);
+ executor.execute(simulation);
+ }
+ executor.shutdown();
+
+ while (!executor.isTerminated()) {
+ }
+
+ System.out.println("All simulations completed successfully.");
+ }
+
+ private record Simulation(String gameName, Game game, int rounds, Lock lock, DataSaver dataSaver) implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("Starting simulation: " + gameName);
+
+ var playerManager = game.getPlayerManager();
+ for (int i = 0; i < rounds; i++) {
+ System.out.println("Start round" + i + " " + gameName);
+
+ Map<Player, ArrayList<Long>> playersTimePerMoves = new HashMap<>();
+ Map<Player, ArrayList<Vector2D<Integer, Integer>>> PlayersMovesMade = new HashMap<>();
+
+ long totalGameTime = 0;
+ int gameTurns = 0;
+ while (!game.isGameOver()) {
+ gameTurns++;
+ var currentPlayer = playerManager.getCurrentPlayer();
+ if (game.getValidMoves(currentPlayer).isEmpty()) {
+ playerManager.nextPlayer();
+ continue;
+ }
+
+ final long startTime = System.currentTimeMillis();
+ var move = currentPlayer.onPlayerTurn();
+ game.move(currentPlayer, move);
+ final long endTime = System.currentTimeMillis();
+ final long deltaTime = endTime - startTime;
+ totalGameTime += deltaTime;
+
+ PlayersMovesMade.computeIfAbsent(currentPlayer, k -> new ArrayList<>()).add(move);
+ playersTimePerMoves.computeIfAbsent(currentPlayer, k -> new ArrayList<>()).add(deltaTime);
+ }
+
+ var winner = game.getWinners().get(0);
+ var data = buildDataString(gameName, gameTurns, game.isDraw(), winner, totalGameTime, playersTimePerMoves, PlayersMovesMade);
+
+ lock.lock();
+ try {
+ dataSaver.saveData(data);
+ } finally {
+ lock.unlock();
+ }
+ game.restart();
+ System.out.println("end round" + i + " " + gameName);
+ }
+ System.out.println(gameName + " has finished");
+ }
+ }
}
diff --git a/src/main/java/nl/isygameclient/Tournament.java b/src/main/java/nl/isygameclient/Tournament.java
@@ -0,0 +1,109 @@
+package nl.isygameclient;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import nl.isygameclient.models.Ai;
+import nl.isygameclient.models.Game;
+import nl.isygameclient.models.Player;
+import nl.isygameclient.models.PlayerManager;
+import nl.isygameclient.models.games.othello.Othello;
+import nl.isygameclient.network.Event;
+import nl.isygameclient.network.EventType;
+import nl.isygameclient.network.GameClient;
+import nl.isygameclient.util.Vector2D;
+
+public class Tournament {
+ public static final int[][] HEURISTIC = {
+ { 10, 2, 5, 5, 5, 5, 2, 10 },
+ { 2, 2, 1, 1, 1, 1, 2, 2 },
+ { 5, 1, 1, 1, 1, 1, 1, 5 },
+ { 5, 1, 1, 1, 1, 1, 1, 5 },
+ { 5, 1, 1, 1, 1, 1, 1, 5 },
+ { 5, 1, 1, 1, 1, 1, 1, 5 },
+ { 2, 2, 1, 1, 1, 1, 2, 2 },
+ { 10, 2, 5, 5, 5, 5, 2, 10 }
+ };
+
+ public static void main(String[] args) throws IOException {
+ GameClient client = new GameClient("localhost", 7789);
+
+ client.login("deeznuts" + new Random().nextInt(0, 9));
+
+ Player dummyPlayer = new Player("network", "black") {
+ public Vector2D<Integer, Integer> onPlayerTurn() { return null; }
+ };
+
+ Player ai = new Ai("yannick", "white", HEURISTIC);
+
+ PlayerManager playerManager = new PlayerManager(0, List.of(ai, dummyPlayer));
+ Othello othello = new Othello(playerManager);
+
+ String firstPlayer = null;
+
+ while (true) {
+ Event event = client.event(-1);
+
+ switch (event.type) {
+ case MATCH:
+ @SuppressWarnings("unchecked")
+ var data = (Map<String, String>) event.data;
+
+ String opponent = data.get("opponent");
+ System.out.println("playing against " + opponent);
+
+ firstPlayer = null;
+ playerManager.restart();
+ othello.getBoard().clear();
+
+ break;
+ case YOURTURN:
+ if (firstPlayer == null) {
+ firstPlayer = "self";
+ othello.initializeBoard();
+ }
+ var turn = ai.onPlayerTurn();
+ System.out.println(turn);
+ client.send("move " + (turn.getY() * 8 + turn.getX()));
+ // othello.move(ai, turn);
+
+ break;
+ case MOVE:
+ if (firstPlayer == null) {
+ firstPlayer = "other";
+ playerManager.nextPlayer();
+ othello.initializeBoard();
+ }
+ @SuppressWarnings("unchecked")
+ var moveMap = (Map<String, String>) event.data;
+
+ int move = Integer.parseInt(moveMap.get("move"));
+ System.out.printf("%s did move %dx%d\n", moveMap.get("player"), move / 8, move % 8);
+ othello.move(moveMap.get("player").equals(client.getName()) ? ai : dummyPlayer, new Vector2D<>(move % 8, move / 8));
+
+ break;
+ case WIN:
+ case LOSS:
+ default:
+ for (int y = 0; y < 8; y++) {
+ System.out.print("|");
+ for (int x = 0; x < 8; x++) {
+ Player p = othello.getBoard().get(new Vector2D<Integer, Integer>(x, y));
+ if (p == dummyPlayer) {
+ System.out.print("N|");
+ } else if (p == ai) {
+ System.out.print("A|");
+ } else {
+ System.out.print(" |");
+ }
+ }
+ System.out.println();
+ } // outcome = event;
+ // return false;
+ }
+ }
+ // client.close();
+ }
+}
diff --git a/src/main/java/nl/isygameclient/models/Ai.java b/src/main/java/nl/isygameclient/models/Ai.java
@@ -1,133 +1,109 @@
package nl.isygameclient.models;
-import nl.isygameclient.util.Vector2D;
-
+import java.security.SecureRandom;
import java.util.*;
+import nl.isygameclient.util.Vector2D;
-public class Ai extends Player{
-
- public static final int DEPTH = 5;
-
- private int[][] heuristics;
-
- public Ai(String name, String playingAs, int[][] heuristics) {
- super(name, playingAs);
- this.heuristics = heuristics;
- }
-
- public static <T> T randomMove(Collection<T> possibleMoves) {
- int size = possibleMoves.size();
- int item = new Random().nextInt(size); // In real life, the Random object should be rather more shared than this
- int i = 0;
- for (T obj : possibleMoves)
- if (i == item)
- return obj;
- else
- i++;
- return null;
- }
-
- private double miniMax(Integer depth, boolean isMaximizing) {
- var playerManager = game.getPlayerManager();
- var currentPlayer = playerManager.getCurrentPlayer();
- var possibleMoves = game.getValidMoves(currentPlayer);
-
- if (depth == 0 || game.isGameOver()) {
- return heuristicScore(currentPlayer);
- }
-
- double bestValue = isMaximizing ? 0 : Double.MAX_VALUE;
- for (Vector2D<Integer, Integer> move : possibleMoves) {
- game.move(currentPlayer, move);
- double value = miniMax(depth - 1, !isMaximizing);
- bestValue = isMaximizing ? Math.max(bestValue, value) : Math.min(bestValue, value);
- game.undo();
- }
- return bestValue;
- }
-
- private double miniMaxAlphaBeta(double alpha, double beta, Integer depth, boolean isMaximizing) {
- var playerManager = game.getPlayerManager();
- var currentPlayer = playerManager.getCurrentPlayer();
- var possibleMoves = game.getValidMoves(currentPlayer);
-
- if (depth == 0 || game.isGameOver()) {
- return heuristicScore(currentPlayer);
- }
-
- double bestValue;
- if (isMaximizing) {
- bestValue = Double.MIN_VALUE;
- for (Vector2D<Integer, Integer> move : possibleMoves) {
- game.move(currentPlayer, move);
- double value = miniMaxAlphaBeta(alpha, beta, depth - 1, false);
- game.undo();
-
- bestValue = Math.max(bestValue, value);
- alpha = Math.max(alpha, bestValue);
- if (beta <= alpha) {
- break;
- }
- }
-
- } else {
- bestValue = Double.MAX_VALUE;
- for (Vector2D<Integer, Integer> move : possibleMoves) {
- game.move(currentPlayer, move);
- double value = miniMaxAlphaBeta(alpha, beta, depth - 1, true);
- game.undo();
-
- bestValue = Math.min(bestValue, value);
- beta = Math.min(beta, bestValue);
- if (beta <= alpha) {
- break;
- }
- }
- }
- return bestValue;
- }
-
- @Override
- public Vector2D<Integer, Integer> onPlayerTurn() {
- var manager = game.getPlayerManager();
- var currentPlayer = manager.getCurrentPlayer();
- var possibleMoves = game.getValidMoves(currentPlayer);
-
- double bestValue = Integer.MIN_VALUE;
- var bestMove = randomMove(possibleMoves);
-
- for (Vector2D<Integer, Integer> move : possibleMoves) {
- game.move(currentPlayer, move);
- double moveValue = miniMaxAlphaBeta(Double.MIN_VALUE, Double.MAX_VALUE, DEPTH, false);
- if (moveValue > bestValue) {
- bestValue = moveValue;
- bestMove = move;
- }
- game.undo();
- }
- return bestMove;
- }
-
- public int heuristicScore(Player player) {
- var board = game.getBoard();
- int score = 0;
- for (int y = 0; y < board.getHeight(); y++) {
- for (int x = 0; x < board.getWidth(); x++) {
- if (Objects.equals(player, board.get(new Vector2D<>(x, y)))) {
- score += heuristics[y][x];
- }
- }
- }
- return score;
- }
-
- public void setGame(Game game) {
- this.game = game;
- }
-
- public void setHeuristics(int[][] heuristics) {
- this.heuristics = heuristics;
- }
-
-
+public class Ai extends Player {
+
+ public static final int DEPTH = 5;
+ public static final Random AI_RANDOM = new Random();
+
+ private final int[][] heuristics;
+
+ public Ai(String name, String playingAs, int[][] heuristics) {
+ super(name, playingAs);
+ this.heuristics = heuristics;
+ }
+
+ public static <T> T randomMove(Collection<T> possibleMoves) {
+ int size = possibleMoves.size();
+ int item = AI_RANDOM.nextInt(size); // In real life, the Random object should be rather more shared than this
+ int i = 0;
+ for (T obj : possibleMoves)
+ if (i == item)
+ return obj;
+ else
+ i++;
+ return null;
+ }
+
+ private double miniMaxAlphaBeta(double alpha, double beta, Integer depth, boolean isMaximizing) {
+ var playerManager = game.getPlayerManager();
+ var currentPlayer = playerManager.getCurrentPlayer();
+ var possibleMoves = game.getValidMoves(currentPlayer);
+
+ if (depth == 0 || game.isGameOver()) {
+ return heuristicScore(currentPlayer);
+ }
+
+ double bestValue;
+ if (isMaximizing) {
+ bestValue = Double.MIN_VALUE;
+ for (Vector2D<Integer, Integer> move : possibleMoves) {
+ game.move(currentPlayer, move);
+ double value = miniMaxAlphaBeta(alpha, beta, depth - 1, false);
+ game.undo();
+
+ bestValue = Math.max(bestValue, value);
+ alpha = Math.max(alpha, bestValue);
+ if (beta <= alpha) {
+ break;
+ }
+ }
+
+ } else {
+ bestValue = Double.MAX_VALUE;
+ for (Vector2D<Integer, Integer> move : possibleMoves) {
+ game.move(currentPlayer, move);
+ double value = miniMaxAlphaBeta(alpha, beta, depth - 1, true);
+ game.undo();
+
+ bestValue = Math.min(bestValue, value);
+ beta = Math.min(beta, bestValue);
+ if (beta <= alpha) {
+ break;
+ }
+ }
+ }
+ return bestValue;
+ }
+
+ @Override
+ public Vector2D<Integer, Integer> onPlayerTurn() {
+ var manager = game.getPlayerManager();
+ var currentPlayer = manager.getCurrentPlayer();
+ var possibleMoves = game.getValidMoves(currentPlayer);
+
+ double bestValue = Integer.MIN_VALUE;
+ var bestMove = randomMove(possibleMoves);
+
+ for (Vector2D<Integer, Integer> move : possibleMoves) {
+ game.move(currentPlayer, move);
+ double moveValue = miniMaxAlphaBeta(Double.MIN_VALUE, Double.MAX_VALUE, DEPTH, false);
+ if (moveValue > bestValue) {
+ bestValue = moveValue;
+ bestMove = move;
+ }
+ game.undo();
+ }
+ return bestMove;
+ }
+
+ public int heuristicScore(Player player) {
+ var board = game.getBoard();
+ int score = 0;
+ for (int y = 0; y < board.getHeight(); y++) {
+ for (int x = 0; x < board.getWidth(); x++) {
+ if (Objects.equals(player, board.get(new Vector2D<>(x, y)))) {
+ score += heuristics[y][x];
+ }
+ }
+ }
+ return score;
+ }
+
+ public void setGame(Game game) {
+ this.game = game;
+ }
}
diff --git a/src/main/java/nl/isygameclient/models/PlayerManager.java b/src/main/java/nl/isygameclient/models/PlayerManager.java
@@ -1,67 +1,66 @@
package nl.isygameclient.models;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
public class PlayerManager {
- protected int startingPlayerIndex;
- protected int currentPlayerIndex;
- protected final ArrayList<Player> players;
+ protected int startingPlayerIndex;
+ protected int currentPlayerIndex;
+ protected final List<Player> players;
- public PlayerManager(int startingPlayerIndex, ArrayList<Player> players) {
- this.startingPlayerIndex = startingPlayerIndex;
- this.currentPlayerIndex = startingPlayerIndex;
- this.players = players;
- }
+ public PlayerManager(int startingPlayerIndex, List<Player> players) {
+ this.startingPlayerIndex = startingPlayerIndex;
+ this.currentPlayerIndex = startingPlayerIndex;
+ this.players = players;
+ }
- public void restart() {
- currentPlayerIndex = startingPlayerIndex;
- }
+ public void restart() {
+ currentPlayerIndex = startingPlayerIndex;
+ }
- public void nextPlayer() {
- currentPlayerIndex += 1;
- if (currentPlayerIndex >= players.size()) {
- currentPlayerIndex = 0;
- }
- }
- public void previousPlayer() {
- currentPlayerIndex -= 1;
- if (currentPlayerIndex < 0) {
- currentPlayerIndex = players.size() - 1;
- }
- }
+ public void nextPlayer() {
+ currentPlayerIndex += 1;
+ if (currentPlayerIndex >= players.size()) {
+ currentPlayerIndex = 0;
+ }
+ }
+ public void previousPlayer() {
+ currentPlayerIndex -= 1;
+ if (currentPlayerIndex < 0) {
+ currentPlayerIndex = players.size() - 1;
+ }
+ }
- public boolean isCurrentPlayer(Player player) {
- return Objects.equals(player, getCurrentPlayer());
- }
+ public boolean isCurrentPlayer(Player player) {
+ return Objects.equals(player, getCurrentPlayer());
+ }
- public Player getCurrentPlayer(){
- return players.get(currentPlayerIndex);
- }
+ public Player getCurrentPlayer() {
+ return players.get(currentPlayerIndex);
+ }
- public Player getStartingPlayer() {
- return players.get(startingPlayerIndex);
- }
+ public Player getStartingPlayer() {
+ return players.get(startingPlayerIndex);
+ }
- public ArrayList<Player> getPlayers(){
- return players;
- }
+ public List<Player> getPlayers() {
+ return players;
+ }
- public int getStartingPlayerIndex() {
- return startingPlayerIndex;
- }
+ public int getStartingPlayerIndex() {
+ return startingPlayerIndex;
+ }
- public int getCurrentPlayerIndex() {
- return currentPlayerIndex;
- }
-
- public void setStartingPlayerIndex(int startingPlayerIndex) {
- this.startingPlayerIndex = startingPlayerIndex;
- }
-
- public void setCurrentPlayerIndex(int currentPlayerIndex) {
- this.currentPlayerIndex = currentPlayerIndex;
- }
+ public int getCurrentPlayerIndex() {
+ return currentPlayerIndex;
+ }
+ public void setStartingPlayerIndex(int startingPlayerIndex) {
+ this.startingPlayerIndex = startingPlayerIndex;
+ }
+ public void setCurrentPlayerIndex(int currentPlayerIndex) {
+ this.currentPlayerIndex = currentPlayerIndex;
+ }
}
diff --git a/src/main/java/nl/isygameclient/models/board/Board.java b/src/main/java/nl/isygameclient/models/board/Board.java
@@ -1,65 +1,64 @@
package nl.isygameclient.models.board;
-import nl.isygameclient.models.Player;
-import nl.isygameclient.util.Vector2D;
-
import java.util.Arrays;
-import java.util.List;
-
-public abstract class Board<T> {
+import nl.isygameclient.util.Vector2D;
- protected final int width;
- protected final int height;
+public class Board<T> {
+ protected final int width;
+ protected final int height;
- protected final T[][] field;
+ protected final T[][] field;
- @SuppressWarnings("unchecked")
- public Board(int width, int height) {
- this.width = width;
- this.height = height;
- this.field = (T[][]) new Player[height][width];
- }
+ @SuppressWarnings("unchecked")
+ public Board(int width, int height) {
+ this.width = width;
+ this.height = height;
+ this.field = (T[][]) new Object[height][width];
+ }
- public void set(T value, Vector2D<Integer, Integer> pos) {
- field[pos.getY()][pos.getX()] = value;
- }
+ public void set(T value, Vector2D<Integer, Integer> pos) {
+ field[pos.getY()][pos.getX()] = value;
+ }
- public T get(Vector2D<Integer, Integer> pos) {
- return field[pos.getY()][pos.getX()];
- }
+ public T get(Vector2D<Integer, Integer> pos) {
+ return field[pos.getY()][pos.getX()];
+ }
- public int size() {
- return width * height;
- }
+ public int size() {
+ return width * height;
+ }
- public void clear() {
- Arrays.fill(field, null);
- }
+ public void clear() {
+ for (int y = 0; y < height; y++)
+ for (int x = 0; x < width; x++)
+ field[y][x] = null;
+ }
- public int getWidth() {
- return width;
- }
+ public int getWidth() {
+ return width;
+ }
- public int getHeight() {
- return height;
- }
+ public int getHeight() {
+ return height;
+ }
- public T[][] getField() {
- return field;
- }
+ public T[][] getField() {
+ return field;
+ }
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- for (T[] row : field) {
- builder.append(Arrays.toString(row))
- .append(", \n");
- }
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (T[] row : field) {
+ builder.append(Arrays.toString(row))
+ .append(", \n");
+ }
- return "Board{" +
- "width=" + width +
- ", height=" + height +
- ", field=\n" + builder +
- '}';
- }
+ return "Board{"
+ +
+ "width=" + width +
+ ", height=" + height +
+ ", field=\n" + builder +
+ '}';
+ }
}
diff --git a/src/main/java/nl/isygameclient/models/board/HistoryBoard.java b/src/main/java/nl/isygameclient/models/board/HistoryBoard.java
@@ -1,91 +1,90 @@
package nl.isygameclient.models.board;
-import nl.isygameclient.util.Vector2D;
-
import java.util.*;
+import nl.isygameclient.util.Vector2D;
-public abstract class HistoryBoard<T> extends Board<T> {
- public record Move<T>(T current, T previous) {
- }
-
- public record Change<T>(T player, Map<Vector2D<Integer, Integer>, Move<T>> moves) {
- }
-
- private int index;
- private final Stack<Change<T>> stack;
-
- public HistoryBoard(int width, int height) {
- super(width, height);
- this.stack = new Stack<>();
- this.index = 0;
- }
-
- public List<Change<T>> getHistory() {
- return stack.subList(0, index);
- }
-
- public List<Change<T>> getFuture() {
- return stack.subList(index, stack.size());
- }
-
- public void add(T player, Map<Vector2D<Integer, Integer>, T> changes) {
- for (int i = stack.size(); i > index; i--)
- stack.pop();
-
- Map<Vector2D<Integer, Integer>, Move<T>> moves = new HashMap<>();
- for (Map.Entry<Vector2D<Integer, Integer>, T> entry : changes.entrySet()) {
- var vector = entry.getKey();
- moves.put(entry.getKey(), new Move<>(entry.getValue(), get(vector)));
- }
-
- stack.push(new Change<>(player, moves));
- index++;
-
- for (var move : changes.entrySet()) {
- var vector = move.getKey();
- set(move.getValue(), vector);
- }
- }
-
- public boolean canUndo() {
- return index > 0;
- }
-
- public boolean canRedo() {
- return index < stack.size();
- }
-
- public boolean undo() {
- if (!canUndo())
- return false;
-
- index--;
-
- for (var move : stack.get(index).moves.entrySet()) {
- var vector = move.getKey();
- set(move.getValue().previous, vector);
- }
-
- return true;
- }
-
- public boolean redo() {
- if (!canRedo())
- return false;
-
- for (var move : stack.get(index).moves.entrySet()) {
- var vector = move.getKey();
- set(move.getValue().current, vector);
- }
-
- index++;
- return true;
- }
-
- public void clear() {
- super.clear();
- stack.clear();
- index = 0;
- }
+public class HistoryBoard<T> extends Board<T> {
+ public record Move<T>(T current, T previous) {
+ }
+
+ public record Change<T>(T player, Map<Vector2D<Integer, Integer>, Move<T>> moves) {
+ }
+
+ private int index;
+ private final Stack<Change<T>> stack;
+
+ public HistoryBoard(int width, int height) {
+ super(width, height);
+ this.stack = new Stack<>();
+ this.index = 0;
+ }
+
+ public List<Change<T>> getHistory() {
+ return stack.subList(0, index);
+ }
+
+ public List<Change<T>> getFuture() {
+ return stack.subList(index, stack.size());
+ }
+
+ public void add(T player, Map<Vector2D<Integer, Integer>, T> changes) {
+ for (int i = stack.size(); i > index; i--)
+ stack.pop();
+
+ Map<Vector2D<Integer, Integer>, Move<T>> moves = new HashMap<>();
+ for (Map.Entry<Vector2D<Integer, Integer>, T> entry : changes.entrySet()) {
+ var vector = entry.getKey();
+ moves.put(entry.getKey(), new Move<>(entry.getValue(), get(vector)));
+ }
+
+ stack.push(new Change<>(player, moves));
+ index++;
+
+ for (var move : changes.entrySet()) {
+ var vector = move.getKey();
+ set(move.getValue(), vector);
+ }
+ }
+
+ public boolean canUndo() {
+ return index > 0;
+ }
+
+ public boolean canRedo() {
+ return index < stack.size();
+ }
+
+ public boolean undo() {
+ if (!canUndo())
+ return false;
+
+ index--;
+
+ for (var move : stack.get(index).moves.entrySet()) {
+ var vector = move.getKey();
+ set(move.getValue().previous, vector);
+ }
+
+ return true;
+ }
+
+ public boolean redo() {
+ if (!canRedo())
+ return false;
+
+ for (var move : stack.get(index).moves.entrySet()) {
+ var vector = move.getKey();
+ set(move.getValue().current, vector);
+ }
+
+ index++;
+ return true;
+ }
+
+ public void clear() {
+ super.clear();
+ stack.clear();
+ index = 0;
+ }
}
diff --git a/src/main/java/nl/isygameclient/models/games/othello/Othello.java b/src/main/java/nl/isygameclient/models/games/othello/Othello.java
@@ -1,176 +1,175 @@
package nl.isygameclient.models.games.othello;
+import java.util.*;
+import java.util.stream.Collectors;
import nl.isygameclient.models.Game;
import nl.isygameclient.models.Player;
import nl.isygameclient.models.PlayerManager;
import nl.isygameclient.models.board.HistoryBoard;
import nl.isygameclient.util.Vector2D;
-import java.util.*;
-import java.util.stream.Collectors;
-
public class Othello extends Game {
- public Othello(PlayerManager playerManager) {
- super(playerManager, new HistoryBoard<>(8, 8) {});
- initializeBoard();
- }
-
- @Override
- public void restart() {
- playerManager.restart();
- board = new HistoryBoard<>(8, 8) {};
- initializeBoard();
- }
-
- private void initializeBoard() {
- var players = playerManager.getPlayers();
- board.set(players.get(1), new Vector2D<>(3, 3));
- board.set(players.get(0), new Vector2D<>(3, 4));
- board.set(players.get(0), new Vector2D<>(4, 3));
- board.set(players.get(1), new Vector2D<>(4, 4));
-
- }
-
- @Override
- public boolean isDraw() {
- var scores = getScores();
- return scores.values().stream().distinct().count() == 1;
- }
-
- @Override
- public boolean isGameOver() {
- for (Player player : playerManager.getPlayers()) {
- if (getValidMoves(player).size() > 0){
- return false;
- }
- }
- return true;
- }
-
- @Override
- public boolean isWinner(Player player) {
- Player winner = null;
- int score = 0;
- for (Map.Entry<Player, Integer> entry : getScores().entrySet()) {
- if (entry.getValue() > score) {
- winner = entry.getKey();
- score = entry.getValue();
- }
- }
- return Objects.equals(player, winner);
- }
-
-
- @Override
- public boolean move(Player player, Vector2D<Integer, Integer> pos) {
- if (!isMoveValid(player, pos)) {
- return false;
- }
-
- var flippable = checkNeighbours(player, pos);
-
- Map<Vector2D<Integer, Integer>, Player> changes = flippable.stream().collect(Collectors.toMap(e -> e, e -> player));
- board.add(player, changes);
- playerManager.nextPlayer();
- return true;
- }
-
-
- @Override
- public boolean isMoveValid(Player player, Vector2D<Integer, Integer> pos) {
- if (board.get(pos) != null)
- return false;
-
- var flippable = checkNeighbours(player, pos);
- return !flippable.isEmpty();
- }
-
-
- private List<Vector2D<Integer, Integer>> rangeContains(Player player, Vector2D<Integer, Integer> pos, Vector2D<Integer, Integer> dir) {
- List<Vector2D<Integer, Integer>> flippable = new ArrayList<>();
- int x = pos.getX();
- int y = pos.getY();
- while (x >= 0 && x < board.getWidth() && y >= 0 && y < board.getHeight()) {
- var newPos = new Vector2D<>(x, y);
- flippable.add(newPos);
- if (Objects.equals(board.get(newPos), player)) {
- return flippable;
- }
- x += dir.getX();
- y += dir.getY();
- }
- return null;
- }
-
-
- private Set<Vector2D<Integer, Integer>> checkNeighbours(Player player, Vector2D<Integer, Integer> pos) {
- Set<Vector2D<Integer, Integer>> flippable = new HashSet<>();
- for (int dx = -1; dx <= 1; dx++) {
- if ((pos.getX() + dx) < 0 || (pos.getX() + dx) >= board.getWidth())
- continue;
-
- for (int dy = -1; dy <= 1; dy++) {
- if ((pos.getY() + dy) < 0 || (pos.getY() + dy) >= board.getHeight())
- continue;
-
- if (dx == 0 && dy == 0)
- continue;
-
- var checkPos = board.get(new Vector2D<>(pos.getX() + dx, pos.getY() + dy));
- if (checkPos != null && checkPos != player) {
- List<Vector2D<Integer, Integer>> flip = rangeContains(player, pos, new Vector2D<>(dx, dy));
- if (flip != null)
- flippable.addAll(flip);
- }
- }
- }
- return flippable;
- }
-
- @Override
- public List<Vector2D<Integer, Integer>> getValidMoves(Player player) {
- List<Vector2D<Integer, Integer>> valid = new ArrayList<>();
- for (int y = 0; y < board.getHeight(); y++) {
- for (int x = 0; x < board.getWidth(); x++) {
- if (isMoveValid(player, new Vector2D<>(x, y))) {
- valid.add(new Vector2D<>(x, y));
- }
- }
- }
- return valid;
- }
-
- @Override
- public List<Player> getWinners() {
- ArrayList<Player> winners = new ArrayList<>();
- for (Player player : playerManager.getPlayers()) {
- if (isWinner(player)) {
- winners.add(player);
- }
- }
- return winners;
- }
-
- @Override
- public int getPlayerScore(Player player) {
- int score = 0;
- for (int y = 0; y < board.getHeight(); y++) {
- for (int x = 0; x < board.getWidth(); x++) {
- if (Objects.equals(player, board.get(new Vector2D<>(x, y)))) {
- score += 1;
- }
- }
- }
- return score;
- }
-
- private HashMap<Player, Integer> getScores() {
- HashMap<Player, Integer> scores = new HashMap<>();
- for (Player player : playerManager.getPlayers()) {
- int score = getPlayerScore(player);
- scores.put(player, score);
- }
- return scores;
- }
+ public Othello(PlayerManager playerManager) {
+ super(playerManager, new HistoryBoard<>(8, 8));
+ initializeBoard();
+ }
+
+ @Override
+ public void restart() {
+ playerManager.restart();
+ board.clear();
+ initializeBoard();
+ }
+
+ public void initializeBoard() {
+ board.set(playerManager.getCurrentPlayer(), new Vector2D<>(3, 4));
+ board.set(playerManager.getCurrentPlayer(), new Vector2D<>(4, 3));
+ playerManager.nextPlayer();
+ board.set(playerManager.getCurrentPlayer(), new Vector2D<>(3, 3));
+ board.set(playerManager.getCurrentPlayer(), new Vector2D<>(4, 4));
+ playerManager.nextPlayer();
+ }
+
+ @Override
+ public boolean isDraw() {
+ var scores = getScores();
+ return scores.values().stream().distinct().count() == 1;
+ }
+
+ @Override
+ public boolean isGameOver() {
+ for (Player player : playerManager.getPlayers()) {
+ if (getValidMoves(player).size() > 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isWinner(Player player) {
+ Player winner = null;
+ int score = 0;
+ for (Map.Entry<Player, Integer> entry : getScores().entrySet()) {
+ if (entry.getValue() > score) {
+ winner = entry.getKey();
+ score = entry.getValue();
+ }
+ }
+ return Objects.equals(player, winner);
+ }
+
+
+ @Override
+ public boolean move(Player player, Vector2D<Integer, Integer> pos) {
+ if (!isMoveValid(player, pos)) {
+ return false;
+ }
+
+ var flippable = checkNeighbours(player, pos);
+
+ Map<Vector2D<Integer, Integer>, Player> changes = flippable.stream().collect(Collectors.toMap(e -> e, e -> player));
+ board.add(player, changes);
+ playerManager.nextPlayer();
+ return true;
+ }
+
+
+ @Override
+ public boolean isMoveValid(Player player, Vector2D<Integer, Integer> pos) {
+ if (board.get(pos) != null)
+ return false;
+
+ var flippable = checkNeighbours(player, pos);
+ return !flippable.isEmpty();
+ }
+
+
+ private List<Vector2D<Integer, Integer>> rangeContains(Player player, Vector2D<Integer, Integer> pos, Vector2D<Integer, Integer> dir) {
+ List<Vector2D<Integer, Integer>> flippable = new ArrayList<>();
+ int x = pos.getX();
+ int y = pos.getY();
+ while (x >= 0 && x < board.getWidth() && y >= 0 && y < board.getHeight()) {
+ var newPos = new Vector2D<>(x, y);
+ flippable.add(newPos);
+ if (Objects.equals(board.get(newPos), player)) {
+ return flippable;
+ }
+ x += dir.getX();
+ y += dir.getY();
+ }
+ return null;
+ }
+
+
+ private Set<Vector2D<Integer, Integer>> checkNeighbours(Player player, Vector2D<Integer, Integer> pos) {
+ Set<Vector2D<Integer, Integer>> flippable = new HashSet<>();
+ for (int dx = -1; dx <= 1; dx++) {
+ if ((pos.getX() + dx) < 0 || (pos.getX() + dx) >= board.getWidth())
+ continue;
+
+ for (int dy = -1; dy <= 1; dy++) {
+ if ((pos.getY() + dy) < 0 || (pos.getY() + dy) >= board.getHeight())
+ continue;
+
+ if (dx == 0 && dy == 0)
+ continue;
+
+ var checkPos = board.get(new Vector2D<>(pos.getX() + dx, pos.getY() + dy));
+ if (checkPos != null && checkPos != player) {
+ List<Vector2D<Integer, Integer>> flip = rangeContains(player, pos, new Vector2D<>(dx, dy));
+ if (flip != null)
+ flippable.addAll(flip);
+ }
+ }
+ }
+ return flippable;
+ }
+
+ @Override
+ public List<Vector2D<Integer, Integer>> getValidMoves(Player player) {
+ List<Vector2D<Integer, Integer>> valid = new ArrayList<>();
+ for (int y = 0; y < board.getHeight(); y++) {
+ for (int x = 0; x < board.getWidth(); x++) {
+ if (isMoveValid(player, new Vector2D<>(x, y))) {
+ valid.add(new Vector2D<>(x, y));
+ }
+ }
+ }
+ return valid;
+ }
+
+ @Override
+ public List<Player> getWinners() {
+ ArrayList<Player> winners = new ArrayList<>();
+ for (Player player : playerManager.getPlayers()) {
+ if (isWinner(player)) {
+ winners.add(player);
+ }
+ }
+ return winners;
+ }
+
+ @Override
+ public int getPlayerScore(Player player) {
+ int score = 0;
+ for (int y = 0; y < board.getHeight(); y++) {
+ for (int x = 0; x < board.getWidth(); x++) {
+ if (Objects.equals(player, board.get(new Vector2D<>(x, y)))) {
+ score += 1;
+ }
+ }
+ }
+ return score;
+ }
+
+ private HashMap<Player, Integer> getScores() {
+ HashMap<Player, Integer> scores = new HashMap<>();
+ for (Player player : playerManager.getPlayers()) {
+ int score = getPlayerScore(player);
+ scores.put(player, score);
+ }
+ return scores;
+ }
}
diff --git a/src/main/java/nl/isygameclient/network/GameClient.java b/src/main/java/nl/isygameclient/network/GameClient.java
@@ -1,11 +1,6 @@
package nl.isygameclient.network;
import java.io.IOException;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Random;