mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
Moved the Move record into it's own file, seperated from Game
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
3
game/src/main/java/org/toop/game/records/Move.java
Normal file
3
game/src/main/java/org/toop/game/records/Move.java
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package org.toop.game.records;
|
||||||
|
|
||||||
|
public record Move(int position, char value) {}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
Reference in New Issue
Block a user