Moved the Move record into it's own file, seperated from Game

This commit is contained in:
2025-10-29 14:49:43 +01:00
parent 13bac113b7
commit 925c848fda
15 changed files with 99 additions and 87 deletions

View File

@@ -2,12 +2,13 @@ package org.toop.app.canvas;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import org.toop.game.Game; import org.toop.game.Game;
import org.toop.game.records.Move;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer; import java.util.function.Consumer;
public final class ReversiCanvas extends GameCanvas { public final class ReversiCanvas extends GameCanvas {
private Game.Move[] currentlyHighlightedMoves = null; private Move[] currentlyHighlightedMoves = null;
public ReversiCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked, Consumer<Integer> newCellEntered) { public ReversiCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked, Consumer<Integer> newCellEntered) {
super(color, new Color(0f,0.4f,0.2f,1f), width, height, 8, 8, 5, true, onCellClicked, newCellEntered); super(color, new Color(0f,0.4f,0.2f,1f), width, height, 8, 8, 5, true, onCellClicked, newCellEntered);
drawStartingDots(); drawStartingDots();
@@ -41,16 +42,16 @@ public final class ReversiCanvas extends GameCanvas {
currentlyHighlightedMoves = null; currentlyHighlightedMoves = null;
} }
public void drawHighlightDots(Game.Move[] moves){ public void drawHighlightDots(Move[] moves){
if (currentlyHighlightedMoves != null){ if (currentlyHighlightedMoves != null){
for (final Game.Move move : currentlyHighlightedMoves){ for (final Move move : currentlyHighlightedMoves){
Color color = move.value() == 'W'? Color.BLACK: Color.WHITE; Color color = move.value() == 'W'? Color.BLACK: Color.WHITE;
drawInnerDot(color, move.position(), true); drawInnerDot(color, move.position(), true);
} }
} }
currentlyHighlightedMoves = moves; currentlyHighlightedMoves = moves;
if (moves != null) { if (moves != null) {
for (Game.Move move : moves) { for (Move move : moves) {
Color color = move.value() == 'B' ? Color.BLACK : Color.WHITE; Color color = move.value() == 'B' ? Color.BLACK : Color.WHITE;
drawInnerDot(color, move.position(), false); drawInnerDot(color, move.position(), false);
} }

View File

@@ -14,6 +14,7 @@ import org.toop.game.Connect4.Connect4;
import org.toop.game.Connect4.Connect4AI; import org.toop.game.Connect4.Connect4AI;
import org.toop.game.Game; import org.toop.game.Game;
import org.toop.game.enumerators.GameState; import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@@ -25,7 +26,7 @@ public class Connect4Game {
private final int myTurn; private final int myTurn;
private Runnable onGameOver; private Runnable onGameOver;
private final BlockingQueue<Game.Move> moveQueue; private final BlockingQueue<Move> moveQueue;
private final Connect4 game; private final Connect4 game;
private final Connect4AI ai; private final Connect4AI ai;
@@ -41,7 +42,7 @@ public class Connect4Game {
this.information = information; this.information = information;
this.myTurn = myTurn; this.myTurn = myTurn;
this.onGameOver = onGameOver; this.onGameOver = onGameOver;
moveQueue = new LinkedBlockingQueue<Game.Move>(); moveQueue = new LinkedBlockingQueue<Move>();
game = new Connect4(); game = new Connect4();
@@ -69,7 +70,7 @@ public class Connect4Game {
final char value = game.getCurrentTurn() == 0? 'X' : 'O'; final char value = game.getCurrentTurn() == 0? 'X' : 'O';
try { try {
moveQueue.put(new Game.Move(cell%columnSize, value)); moveQueue.put(new Move(cell%columnSize, value));
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} }
} else { } else {
@@ -77,7 +78,7 @@ public class Connect4Game {
final char value = myTurn == 0? 'X' : 'O'; final char value = myTurn == 0? 'X' : 'O';
try { try {
moveQueue.put(new Game.Move(cell%columnSize, value)); moveQueue.put(new Move(cell%columnSize, value));
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} }
} }
@@ -114,14 +115,14 @@ public class Connect4Game {
currentValue, currentValue,
information.players[nextTurn].name); information.players[nextTurn].name);
Game.Move move = null; Move move = null;
if (information.players[currentTurn].isHuman) { if (information.players[currentTurn].isHuman) {
try { try {
final Game.Move wants = moveQueue.take(); final Move wants = moveQueue.take();
final Game.Move[] legalMoves = game.getLegalMoves(); final Move[] legalMoves = game.getLegalMoves();
for (final Game.Move legalMove : legalMoves) { for (final Move legalMove : legalMoves) {
if (legalMove.position() == wants.position() && if (legalMove.position() == wants.position() &&
legalMove.value() == wants.value()) { legalMove.value() == wants.value()) {
move = wants; move = wants;
@@ -182,7 +183,7 @@ public class Connect4Game {
playerChar = myTurn == 0? 'O' : 'X'; playerChar = myTurn == 0? 'O' : 'X';
} }
final Game.Move move = new Game.Move(Integer.parseInt(response.move()), playerChar); final Move move = new Move(Integer.parseInt(response.move()), playerChar);
final GameState state = game.play(move); final GameState state = game.play(move);
if (state != GameState.NORMAL) { if (state != GameState.NORMAL) {
@@ -233,7 +234,7 @@ public class Connect4Game {
position = moveQueue.take().position(); position = moveQueue.take().position();
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} else { } else {
final Game.Move move = ai.findBestMove(game, information.players[0].computerDifficulty); final Move move = ai.findBestMove(game, information.players[0].computerDifficulty);
assert move != null; assert move != null;
position = move.position(); position = move.position();

View File

@@ -11,6 +11,7 @@ import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents; import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.Game; import org.toop.game.Game;
import org.toop.game.enumerators.GameState; import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import org.toop.game.reversi.Reversi; import org.toop.game.reversi.Reversi;
import org.toop.game.reversi.ReversiAI; import org.toop.game.reversi.ReversiAI;
@@ -28,7 +29,7 @@ public final class ReversiGame {
private final int myTurn; private final int myTurn;
private Runnable onGameOver; private Runnable onGameOver;
private final BlockingQueue<Game.Move> moveQueue; private final BlockingQueue<Move> moveQueue;
private final Reversi game; private final Reversi game;
private final ReversiAI ai; private final ReversiAI ai;
@@ -44,7 +45,7 @@ public final class ReversiGame {
this.myTurn = myTurn; this.myTurn = myTurn;
this.onGameOver = onGameOver; this.onGameOver = onGameOver;
moveQueue = new LinkedBlockingQueue<Game.Move>(); moveQueue = new LinkedBlockingQueue<Move>();
game = new Reversi(); game = new Reversi();
ai = new ReversiAI(); ai = new ReversiAI();
@@ -72,7 +73,7 @@ public final class ReversiGame {
final char value = game.getCurrentTurn() == 0? 'B' : 'W'; final char value = game.getCurrentTurn() == 0? 'B' : 'W';
try { try {
moveQueue.put(new Game.Move(cell, value)); moveQueue.put(new Move(cell, value));
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} }
} else { } else {
@@ -80,7 +81,7 @@ public final class ReversiGame {
final char value = myTurn == 0? 'B' : 'W'; final char value = myTurn == 0? 'B' : 'W';
try { try {
moveQueue.put(new Game.Move(cell, value)); moveQueue.put(new Move(cell, value));
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} }
} }
@@ -129,14 +130,14 @@ public final class ReversiGame {
currentValue, currentValue,
information.players[nextTurn].name); information.players[nextTurn].name);
Game.Move move = null; Move move = null;
if (information.players[currentTurn].isHuman) { if (information.players[currentTurn].isHuman) {
try { try {
final Game.Move wants = moveQueue.take(); final Move wants = moveQueue.take();
final Game.Move[] legalMoves = game.getLegalMoves(); final Move[] legalMoves = game.getLegalMoves();
for (final Game.Move legalMove : legalMoves) { for (final Move legalMove : legalMoves) {
if (legalMove.position() == wants.position() && if (legalMove.position() == wants.position() &&
legalMove.value() == wants.value()) { legalMove.value() == wants.value()) {
move = wants; move = wants;
@@ -192,7 +193,7 @@ public final class ReversiGame {
playerChar = myTurn == 0? 'W' : 'B'; playerChar = myTurn == 0? 'W' : 'B';
} }
final Game.Move move = new Game.Move(Integer.parseInt(response.move()), playerChar); final Move move = new Move(Integer.parseInt(response.move()), playerChar);
final GameState state = game.play(move); final GameState state = game.play(move);
if (state != GameState.NORMAL) { if (state != GameState.NORMAL) {
@@ -236,7 +237,7 @@ public final class ReversiGame {
position = moveQueue.take().position(); position = moveQueue.take().position();
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} else { } else {
final Game.Move move = ai.findBestMove(game, information.players[0].computerDifficulty); final Move move = ai.findBestMove(game, information.players[0].computerDifficulty);
assert move != null; assert move != null;
position = move.position(); position = move.position();
@@ -266,13 +267,13 @@ public final class ReversiGame {
} }
} }
final Game.Move[] flipped = game.getMostRecentlyFlippedPieces(); final Move[] flipped = game.getMostRecentlyFlippedPieces();
final SequentialTransition animation = new SequentialTransition(); final SequentialTransition animation = new SequentialTransition();
isPaused.set(true); isPaused.set(true);
if (animate && flipped != null) { if (animate && flipped != null) {
for (final Game.Move flip : flipped) { for (final Move flip : flipped) {
canvas.clear(flip.position()); canvas.clear(flip.position());
final Color from = flip.value() == 'W' ? Color.BLACK : Color.WHITE; final Color from = flip.value() == 'W' ? Color.BLACK : Color.WHITE;
@@ -287,9 +288,9 @@ public final class ReversiGame {
animation.setOnFinished(_ -> { animation.setOnFinished(_ -> {
isPaused.set(false); isPaused.set(false);
final Game.Move[] legalMoves = game.getLegalMoves(); final Move[] legalMoves = game.getLegalMoves();
for (final Game.Move legalMove : legalMoves) { for (final Move legalMove : legalMoves) {
canvas.drawLegalPosition(legalMove.position(), game.getCurrentPlayer()); canvas.drawLegalPosition(legalMove.position(), game.getCurrentPlayer());
} }
}); });
@@ -308,9 +309,9 @@ public final class ReversiGame {
} }
private void highlightCells(int cellEntered) { private void highlightCells(int cellEntered) {
Game.Move[] legalMoves = game.getLegalMoves(); Move[] legalMoves = game.getLegalMoves();
boolean isLegalMove = false; boolean isLegalMove = false;
for (Game.Move move : legalMoves) { for (Move move : legalMoves) {
if (move.position() == cellEntered){ if (move.position() == cellEntered){
isLegalMove = true; isLegalMove = true;
break; break;
@@ -318,7 +319,7 @@ public final class ReversiGame {
} }
if (cellEntered >= 0){ if (cellEntered >= 0){
Game.Move[] moves = null; Move[] moves = null;
if (isLegalMove) { if (isLegalMove) {
moves = game.getFlipsForPotentialMove( moves = game.getFlipsForPotentialMove(
new Point(cellEntered%game.columnSize,cellEntered/game.rowSize), new Point(cellEntered%game.columnSize,cellEntered/game.rowSize),

View File

@@ -10,6 +10,7 @@ import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents; import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.Game; import org.toop.game.Game;
import org.toop.game.enumerators.GameState; import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import org.toop.game.tictactoe.TicTacToe; import org.toop.game.tictactoe.TicTacToe;
import org.toop.game.tictactoe.TicTacToeAI; import org.toop.game.tictactoe.TicTacToeAI;
@@ -26,7 +27,7 @@ public final class TicTacToeGame {
private final int myTurn; private final int myTurn;
private Runnable onGameOver; private Runnable onGameOver;
private final BlockingQueue<Game.Move> moveQueue; private final BlockingQueue<Move> moveQueue;
private final TicTacToe game; private final TicTacToe game;
private final TicTacToeAI ai; private final TicTacToeAI ai;
@@ -41,7 +42,7 @@ public final class TicTacToeGame {
this.myTurn = myTurn; this.myTurn = myTurn;
this.onGameOver = onGameOver; this.onGameOver = onGameOver;
moveQueue = new LinkedBlockingQueue<Game.Move>(); moveQueue = new LinkedBlockingQueue<Move>();
game = new TicTacToe(); game = new TicTacToe();
ai = new TicTacToeAI(); ai = new TicTacToeAI();
@@ -68,7 +69,7 @@ public final class TicTacToeGame {
final char value = game.getCurrentTurn() == 0? 'X' : 'O'; final char value = game.getCurrentTurn() == 0? 'X' : 'O';
try { try {
moveQueue.put(new Game.Move(cell, value)); moveQueue.put(new Move(cell, value));
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} }
} else { } else {
@@ -76,7 +77,7 @@ public final class TicTacToeGame {
final char value = myTurn == 0? 'X' : 'O'; final char value = myTurn == 0? 'X' : 'O';
try { try {
moveQueue.put(new Game.Move(cell, value)); moveQueue.put(new Move(cell, value));
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} }
} }
@@ -112,14 +113,14 @@ public final class TicTacToeGame {
currentValue, currentValue,
information.players[nextTurn].name); information.players[nextTurn].name);
Game.Move move = null; Move move = null;
if (information.players[currentTurn].isHuman) { if (information.players[currentTurn].isHuman) {
try { try {
final Game.Move wants = moveQueue.take(); final Move wants = moveQueue.take();
final Game.Move[] legalMoves = game.getLegalMoves(); final Move[] legalMoves = game.getLegalMoves();
for (final Game.Move legalMove : legalMoves) { for (final Move legalMove : legalMoves) {
if (legalMove.position() == wants.position() && if (legalMove.position() == wants.position() &&
legalMove.value() == wants.value()) { legalMove.value() == wants.value()) {
move = wants; move = wants;
@@ -179,7 +180,7 @@ public final class TicTacToeGame {
playerChar = myTurn == 0? 'O' : 'X'; playerChar = myTurn == 0? 'O' : 'X';
} }
final Game.Move move = new Game.Move(Integer.parseInt(response.move()), playerChar); final Move move = new Move(Integer.parseInt(response.move()), playerChar);
final GameState state = game.play(move); final GameState state = game.play(move);
if (state != GameState.NORMAL) { if (state != GameState.NORMAL) {
@@ -230,7 +231,7 @@ public final class TicTacToeGame {
position = moveQueue.take().position(); position = moveQueue.take().position();
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} else { } else {
final Game.Move move; final Move move;
move = ai.findBestMove(game, information.players[0].computerDifficulty); move = ai.findBestMove(game, information.players[0].computerDifficulty);
assert move != null; assert move != null;
position = move.position(); position = move.position();

View File

@@ -1,5 +1,7 @@
package org.toop.game; package org.toop.game;
import org.toop.game.records.Move;
public abstract class AI<T extends Game> { public abstract class AI<T extends Game> {
public abstract Game.Move findBestMove(T game, int depth); public abstract Move findBestMove(T game, int depth);
} }

View File

@@ -2,6 +2,7 @@ package org.toop.game.Connect4;
import org.toop.game.TurnBasedGame; import org.toop.game.TurnBasedGame;
import org.toop.game.enumerators.GameState; import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import java.util.ArrayList; import java.util.ArrayList;

View File

@@ -1,26 +1,26 @@
package org.toop.game.Connect4; package org.toop.game.Connect4;
import org.toop.game.AI; import org.toop.game.AI;
import org.toop.game.Game;
import org.toop.game.enumerators.GameState; import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
public class Connect4AI extends AI<Connect4> { public class Connect4AI extends AI<Connect4> {
public Game.Move findBestMove(Connect4 game, int depth) { public Move findBestMove(Connect4 game, int depth) {
assert game != null; assert game != null;
assert depth >= 0; assert depth >= 0;
final Game.Move[] legalMoves = game.getLegalMoves(); final Move[] legalMoves = game.getLegalMoves();
if (legalMoves.length <= 0) { if (legalMoves.length <= 0) {
return null; return null;
} }
int bestScore = -depth; int bestScore = -depth;
Game.Move bestMove = null; Move bestMove = null;
for (final Game.Move move : legalMoves) { for (final Move move : legalMoves) {
final int score = getMoveScore(game, depth, move, true); final int score = getMoveScore(game, depth, move, true);
if (score > bestScore) { if (score > bestScore) {
@@ -32,7 +32,7 @@ public class Connect4AI extends AI<Connect4> {
return bestMove != null? bestMove : legalMoves[(int)(Math.random() * legalMoves.length)]; return bestMove != null? bestMove : legalMoves[(int)(Math.random() * legalMoves.length)];
} }
private int getMoveScore(Connect4 game, int depth, Game.Move move, boolean maximizing) { private int getMoveScore(Connect4 game, int depth, Move move, boolean maximizing) {
final Connect4 copy = new Connect4(game); final Connect4 copy = new Connect4(game);
final GameState state = copy.play(move); final GameState state = copy.play(move);
@@ -45,10 +45,10 @@ public class Connect4AI extends AI<Connect4> {
return 0; return 0;
} }
final Game.Move[] legalMoves = copy.getLegalMoves(); final Move[] legalMoves = copy.getLegalMoves();
int score = maximizing? depth + 1 : -depth - 1; int score = maximizing? depth + 1 : -depth - 1;
for (final Game.Move next : legalMoves) { for (final Move next : legalMoves) {
if (maximizing) { if (maximizing) {
score = Math.min(score, getMoveScore(copy, depth - 1, next, false)); score = Math.min(score, getMoveScore(copy, depth - 1, next, false));
} else { } else {

View File

@@ -5,7 +5,6 @@ import org.toop.game.interfaces.Playable;
import java.util.Arrays; import java.util.Arrays;
public abstract class Game implements Playable { public abstract class Game implements Playable {
public record Move(int position, char value) {}
public static final char EMPTY = (char)0; public static final char EMPTY = (char)0;

View File

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

View File

@@ -0,0 +1,3 @@
package org.toop.game.records;
public record Move(int position, char value) {}

View File

@@ -3,6 +3,7 @@ package org.toop.game.reversi;
import org.toop.game.Game; import org.toop.game.Game;
import org.toop.game.TurnBasedGame; import org.toop.game.TurnBasedGame;
import org.toop.game.enumerators.GameState; import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import java.awt.*; import java.awt.*;
import java.util.ArrayList; import java.util.ArrayList;

View File

@@ -1,12 +1,12 @@
package org.toop.game.reversi; package org.toop.game.reversi;
import org.toop.game.AI; import org.toop.game.AI;
import org.toop.game.Game; import org.toop.game.records.Move;
public final class ReversiAI extends AI<Reversi> { public final class ReversiAI extends AI<Reversi> {
@Override @Override
public Game.Move findBestMove(Reversi game, int depth) { public Move findBestMove(Reversi game, int depth) {
Game.Move[] moves = game.getLegalMoves(); Move[] moves = game.getLegalMoves();
int inty = (int)(Math.random() * moves.length-.5f); int inty = (int)(Math.random() * moves.length-.5f);
return moves[inty]; return moves[inty];
} }

View File

@@ -3,6 +3,7 @@ package org.toop.game.tictactoe;
import java.util.ArrayList; import java.util.ArrayList;
import org.toop.game.TurnBasedGame; import org.toop.game.TurnBasedGame;
import org.toop.game.enumerators.GameState; import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
public final class TicTacToe extends TurnBasedGame { public final class TicTacToe extends TurnBasedGame {
private int movesLeft; private int movesLeft;

View File

@@ -1,16 +1,16 @@
package org.toop.game.tictactoe; package org.toop.game.tictactoe;
import org.toop.game.AI; import org.toop.game.AI;
import org.toop.game.Game;
import org.toop.game.enumerators.GameState; import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
public final class TicTacToeAI extends AI<TicTacToe> { public final class TicTacToeAI extends AI<TicTacToe> {
@Override @Override
public Game.Move findBestMove(TicTacToe game, int depth) { public Move findBestMove(TicTacToe game, int depth) {
assert game != null; assert game != null;
assert depth >= 0; assert depth >= 0;
final Game.Move[] legalMoves = game.getLegalMoves(); final Move[] legalMoves = game.getLegalMoves();
if (legalMoves.length == 0) { if (legalMoves.length == 0) {
return null; return null;
@@ -26,9 +26,9 @@ public final class TicTacToeAI extends AI<TicTacToe> {
} }
int bestScore = -depth; int bestScore = -depth;
Game.Move bestMove = null; Move bestMove = null;
for (final Game.Move move : legalMoves) { for (final Move move : legalMoves) {
final int score = getMoveScore(game, depth, move, true); final int score = getMoveScore(game, depth, move, true);
if (score > bestScore) { if (score > bestScore) {
@@ -39,15 +39,15 @@ public final class TicTacToeAI extends AI<TicTacToe> {
return bestMove != null? bestMove : legalMoves[(int)(Math.random() * legalMoves.length)]; return bestMove != null? bestMove : legalMoves[(int)(Math.random() * legalMoves.length)];
} }
public Game.Move findWorstMove(TicTacToe game, int depth){ public Move findWorstMove(TicTacToe game, int depth){
Game.Move[] legalMoves = game.getLegalMoves(); Move[] legalMoves = game.getLegalMoves();
int bestScore = -depth; int bestScore = -depth;
Game.Move bestMove = null; Move bestMove = null;
for (final Game.Move move : legalMoves) { for (final Move move : legalMoves) {
final int score = getMoveScore(game, depth, move, false); final int score = getMoveScore(game, depth, move, false);
if (score > bestScore) { if (score > bestScore) {
@@ -58,7 +58,7 @@ public final class TicTacToeAI extends AI<TicTacToe> {
return bestMove; return bestMove;
} }
private int getMoveScore(TicTacToe game, int depth, Game.Move move, boolean maximizing) { private int getMoveScore(TicTacToe game, int depth, Move move, boolean maximizing) {
final TicTacToe copy = new TicTacToe(game); final TicTacToe copy = new TicTacToe(game);
final GameState state = copy.play(move); final GameState state = copy.play(move);
@@ -71,10 +71,10 @@ public final class TicTacToeAI extends AI<TicTacToe> {
return 0; return 0;
} }
final Game.Move[] legalMoves = copy.getLegalMoves(); final Move[] legalMoves = copy.getLegalMoves();
int score = maximizing? depth + 1 : -depth - 1; int score = maximizing? depth + 1 : -depth - 1;
for (final Game.Move next : legalMoves) { for (final Move next : legalMoves) {
if (maximizing) { if (maximizing) {
score = Math.min(score, getMoveScore(copy, depth - 1, next, false)); score = Math.min(score, getMoveScore(copy, depth - 1, next, false));
} else { } else {

View File

@@ -6,6 +6,7 @@ import java.util.Set;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.toop.game.Game; import org.toop.game.Game;
import org.toop.game.records.Move;
class TicTacToeAITest { class TicTacToeAITest {
private TicTacToe game; private TicTacToe game;
@@ -22,12 +23,12 @@ class TicTacToeAITest {
// X X - // X X -
// O O - // O O -
// - - - // - - -
game.play(new Game.Move(0, 'X')); game.play(new Move(0, 'X'));
game.play(new Game.Move(3, 'O')); game.play(new Move(3, 'O'));
game.play(new Game.Move(1, 'X')); game.play(new Move(1, 'X'));
game.play(new Game.Move(4, 'O')); game.play(new Move(4, 'O'));
final Game.Move move = ai.findBestMove(game, 1); final Move move = ai.findBestMove(game, 1);
assertNotNull(move); assertNotNull(move);
assertEquals('X', move.value()); assertEquals('X', move.value());
@@ -39,11 +40,11 @@ class TicTacToeAITest {
// - - - // - - -
// O - - // O - -
// X X - // X X -
game.play(new Game.Move(6, 'X')); game.play(new Move(6, 'X'));
game.play(new Game.Move(3, 'O')); game.play(new Move(3, 'O'));
game.play(new Game.Move(7, 'X')); game.play(new Move(7, 'X'));
final Game.Move move = ai.findBestMove(game, 1); final Move move = ai.findBestMove(game, 1);
assertNotNull(move); assertNotNull(move);
assertEquals('O', move.value()); assertEquals('O', move.value());
@@ -52,7 +53,7 @@ class TicTacToeAITest {
@Test @Test
void testBestMove_preferCornerOnEmpty() { void testBestMove_preferCornerOnEmpty() {
final Game.Move move = ai.findBestMove(game, 0); final Move move = ai.findBestMove(game, 0);
assertNotNull(move); assertNotNull(move);
assertEquals('X', move.value()); assertEquals('X', move.value());
@@ -64,15 +65,15 @@ class TicTacToeAITest {
// O X - // O X -
// - O X // - O X
// X O X // X O X
game.play(new Game.Move(1, 'X')); game.play(new Move(1, 'X'));
game.play(new Game.Move(0, 'O')); game.play(new Move(0, 'O'));
game.play(new Game.Move(5, 'X')); game.play(new Move(5, 'X'));
game.play(new Game.Move(4, 'O')); game.play(new Move(4, 'O'));
game.play(new Game.Move(6, 'X')); game.play(new Move(6, 'X'));
game.play(new Game.Move(7, 'O')); game.play(new Move(7, 'O'));
game.play(new Game.Move(8, 'X')); game.play(new Move(8, 'X'));
final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length); final Move move = ai.findBestMove(game, game.getLegalMoves().length);
assertNotNull(move); assertNotNull(move);
assertEquals('O', move.value()); assertEquals('O', move.value());