mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
Merge remote-tracking branch 'origin/Development' into Reversi
# Conflicts: # app/src/main/java/org/toop/app/canvas/GameCanvas.java # app/src/main/java/org/toop/app/layer/layers/MainLayer.java # game/src/main/java/org/toop/game/Game.java # game/src/main/java/org/toop/game/othello/Othello.java # game/src/main/java/org/toop/game/othello/OthelloAI.java
This commit is contained in:
100
game/pom.xml
100
game/pom.xml
@@ -1,8 +1,13 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.toop</groupId>
|
||||
<artifactId>pism_game</artifactId>
|
||||
<parent>
|
||||
<groupId>org.toop</groupId>
|
||||
<artifactId>pism</artifactId>
|
||||
<version>0.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>game</artifactId>
|
||||
<version>0.1</version>
|
||||
|
||||
<properties>
|
||||
@@ -83,39 +88,72 @@
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>2.0.17</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_core</artifactId>
|
||||
<version>2.42.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
<version>2.42.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.14.1</version>
|
||||
<configuration>
|
||||
<source>25</source>
|
||||
<target>25</target>
|
||||
<release>25</release>
|
||||
<encoding>UTF-8</encoding>
|
||||
<!-- <compilerArgs>-->
|
||||
<!-- <arg>-XDcompilePolicy=simple</arg>-->
|
||||
<!-- <arg>--should-stop=ifError=FLOW</arg>-->
|
||||
<!-- <arg>-Xplugin:ErrorProne</arg>-->
|
||||
<!-- </compilerArgs>-->
|
||||
<!-- <annotationProcessorPaths>-->
|
||||
<!-- <path>-->
|
||||
<!-- <groupId>com.google.errorprone</groupId>-->
|
||||
<!-- <artifactId>error_prone_core</artifactId>-->
|
||||
<!-- <version>2.42.0</version>-->
|
||||
<!-- </path>-->
|
||||
<!-- <!– Other annotation processors go here.-->
|
||||
|
||||
<!-- If 'annotationProcessorPaths' is set, processors will no longer be-->
|
||||
<!-- discovered on the regular -classpath; see also 'Using Error Prone-->
|
||||
<!-- together with other annotation processors' below. –>-->
|
||||
<!-- </annotationProcessorPaths>-->
|
||||
<!-- <fork>true</fork>-->
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.14.1</version>
|
||||
<configuration>
|
||||
<showWarnings>true</showWarnings>
|
||||
<fork>true</fork>
|
||||
<executable>${java.home}/bin/javac</executable>
|
||||
<source>25</source>
|
||||
<target>25</target>
|
||||
<release>25</release>
|
||||
<encoding>UTF-8</encoding>
|
||||
<compilerArgs>
|
||||
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
||||
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
|
||||
<arg>
|
||||
-Xplugin:ErrorProne
|
||||
</arg>
|
||||
<!-- TODO-->
|
||||
<!-- -Xep:RestrictedApi:ERROR \-->
|
||||
<!-- -XepOpt:RestrictedApi:annotation=org.toop.annotations.TestsOnly \-->
|
||||
<!-- -XepOpt:RestrictedApi:allowlistRegex=(?s).*/src/test/java/.*|.*test\.java \-->
|
||||
<!-- -XepOpt:RestrictedApi:message=This API is marked @TestsOnly and shouldn't be normally used.-->
|
||||
<arg>-XDcompilePolicy=simple</arg>
|
||||
<arg>--should-stop=ifError=FLOW</arg>
|
||||
</compilerArgs>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_core</artifactId>
|
||||
<version>2.42.0</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_core</artifactId>
|
||||
<version>2.42.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
|
||||
@@ -3,36 +3,39 @@ package org.toop.game;
|
||||
import java.util.Arrays;
|
||||
|
||||
public abstract class Game {
|
||||
public enum State {
|
||||
NORMAL, DRAW, WIN,
|
||||
}
|
||||
public enum State {
|
||||
NORMAL,
|
||||
DRAW,
|
||||
WIN,
|
||||
}
|
||||
|
||||
public record Move(int position, char value) {}
|
||||
public record Move(int position, char value) {}
|
||||
|
||||
public record Score(int player1Score, int player2Score) {}
|
||||
|
||||
public static final char EMPTY = (char)0;
|
||||
|
||||
public final int rowSize;
|
||||
public final int columnSize;
|
||||
public final char[] board;
|
||||
public final int rowSize;
|
||||
public final int columnSize;
|
||||
public final char[] board;
|
||||
|
||||
protected Game(int rowSize, int columnSize) {
|
||||
assert rowSize > 0 && columnSize > 0;
|
||||
protected Game(int rowSize, int columnSize) {
|
||||
assert rowSize > 0 && columnSize > 0;
|
||||
|
||||
this.rowSize = rowSize;
|
||||
this.columnSize = columnSize;
|
||||
this.rowSize = rowSize;
|
||||
this.columnSize = columnSize;
|
||||
|
||||
board = new char[rowSize * columnSize];
|
||||
Arrays.fill(board, EMPTY);
|
||||
}
|
||||
board = new char[rowSize * columnSize];
|
||||
Arrays.fill(board, EMPTY);
|
||||
}
|
||||
|
||||
protected Game(Game other) {
|
||||
rowSize = other.rowSize;
|
||||
columnSize = other.columnSize;
|
||||
board = Arrays.copyOf(other.board, other.board.length);
|
||||
}
|
||||
protected Game(Game other) {
|
||||
rowSize = other.rowSize;
|
||||
columnSize = other.columnSize;
|
||||
board = Arrays.copyOf(other.board, other.board.length);
|
||||
}
|
||||
|
||||
public abstract Move[] getLegalMoves();
|
||||
public abstract State play(Move move);
|
||||
}
|
||||
public abstract Move[] getLegalMoves();
|
||||
|
||||
public abstract State play(Move move);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
package org.toop.game;
|
||||
|
||||
public abstract class TurnBasedGame extends Game {
|
||||
public final int turns;
|
||||
public final int turns;
|
||||
|
||||
protected int currentTurn;
|
||||
protected int currentTurn;
|
||||
|
||||
protected TurnBasedGame(int rowSize, int columnSize, int turns) {
|
||||
super(rowSize, columnSize);
|
||||
protected TurnBasedGame(int rowSize, int columnSize, int turns) {
|
||||
super(rowSize, columnSize);
|
||||
assert turns >= 2;
|
||||
this.turns = turns;
|
||||
}
|
||||
}
|
||||
|
||||
protected TurnBasedGame(TurnBasedGame other) {
|
||||
super(other);
|
||||
turns = other.turns;
|
||||
currentTurn = other.currentTurn;
|
||||
}
|
||||
protected TurnBasedGame(TurnBasedGame other) {
|
||||
super(other);
|
||||
turns = other.turns;
|
||||
currentTurn = other.currentTurn;
|
||||
}
|
||||
|
||||
protected void nextTurn() {
|
||||
currentTurn = (currentTurn + 1) % turns;
|
||||
}
|
||||
protected void nextTurn() {
|
||||
currentTurn = (currentTurn + 1) % turns;
|
||||
}
|
||||
|
||||
public int getCurrentTurn() { return currentTurn; }
|
||||
}
|
||||
public int getCurrentTurn() {
|
||||
return currentTurn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package org.toop.game.tictactoe;
|
||||
|
||||
import org.toop.game.TurnBasedGame;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.toop.game.TurnBasedGame;
|
||||
|
||||
public final class TicTacToe extends TurnBasedGame {
|
||||
private int movesLeft;
|
||||
@@ -19,8 +18,8 @@ public final class TicTacToe extends TurnBasedGame {
|
||||
|
||||
@Override
|
||||
public Move[] getLegalMoves() {
|
||||
final ArrayList<Move> legalMoves = new ArrayList<>();
|
||||
final char currentValue = getCurrentValue();
|
||||
final ArrayList<Move> legalMoves = new ArrayList<>();
|
||||
final char currentValue = getCurrentValue();
|
||||
|
||||
for (int i = 0; i < board.length; i++) {
|
||||
if (board[i] == EMPTY) {
|
||||
@@ -44,12 +43,12 @@ public final class TicTacToe extends TurnBasedGame {
|
||||
return State.WIN;
|
||||
}
|
||||
|
||||
nextTurn();
|
||||
nextTurn();
|
||||
|
||||
if (movesLeft <= 2) {
|
||||
if (movesLeft <= 0 || checkForEarlyDraw(this)) {
|
||||
return State.DRAW;
|
||||
}
|
||||
if (movesLeft <= 0 || checkForEarlyDraw(this)) {
|
||||
return State.DRAW;
|
||||
}
|
||||
}
|
||||
|
||||
return State.NORMAL;
|
||||
@@ -60,7 +59,9 @@ public final class TicTacToe extends TurnBasedGame {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final int index = i * 3;
|
||||
|
||||
if (board[index] != EMPTY && board[index] == board[index + 1] && board[index] == board[index + 2]) {
|
||||
if (board[index] != EMPTY
|
||||
&& board[index] == board[index + 1]
|
||||
&& board[index] == board[index + 2]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +84,7 @@ public final class TicTacToe extends TurnBasedGame {
|
||||
|
||||
private boolean checkForEarlyDraw(TicTacToe game) {
|
||||
for (final Move move : game.getLegalMoves()) {
|
||||
final TicTacToe copy = new TicTacToe(game);
|
||||
final TicTacToe copy = new TicTacToe(game);
|
||||
|
||||
if (copy.play(move) == State.WIN || !checkForEarlyDraw(copy)) {
|
||||
return false;
|
||||
@@ -93,7 +94,7 @@ public final class TicTacToe extends TurnBasedGame {
|
||||
return true;
|
||||
}
|
||||
|
||||
private char getCurrentValue() {
|
||||
return currentTurn == 0? 'X' : 'O';
|
||||
}
|
||||
}
|
||||
private char getCurrentValue() {
|
||||
return currentTurn == 0 ? 'X' : 'O';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +1,81 @@
|
||||
package org.toop.game.tictactoe;
|
||||
|
||||
import org.toop.game.Game;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.toop.game.Game;
|
||||
|
||||
class TicTacToeAITest {
|
||||
private TicTacToe game;
|
||||
private TicTacToeAI ai;
|
||||
private TicTacToe game;
|
||||
private TicTacToeAI ai;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
game = new TicTacToe();
|
||||
ai = new TicTacToeAI();
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
game = new TicTacToe();
|
||||
ai = new TicTacToeAI();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBestMove_returnWinningMoveWithDepth1() {
|
||||
// X X -
|
||||
// O O -
|
||||
// - - -
|
||||
game.play(new Game.Move(0, 'X'));
|
||||
game.play(new Game.Move(3, 'O'));
|
||||
game.play(new Game.Move(1, 'X'));
|
||||
game.play(new Game.Move(4, 'O'));
|
||||
@Test
|
||||
void testBestMove_returnWinningMoveWithDepth1() {
|
||||
// X X -
|
||||
// O O -
|
||||
// - - -
|
||||
game.play(new Game.Move(0, 'X'));
|
||||
game.play(new Game.Move(3, 'O'));
|
||||
game.play(new Game.Move(1, 'X'));
|
||||
game.play(new Game.Move(4, 'O'));
|
||||
|
||||
final Game.Move move = ai.findBestMove(game, 1);
|
||||
final Game.Move move = ai.findBestMove(game, 1);
|
||||
|
||||
assertNotNull(move);
|
||||
assertEquals('X', move.value());
|
||||
assertEquals(2, move.position());
|
||||
}
|
||||
assertNotNull(move);
|
||||
assertEquals('X', move.value());
|
||||
assertEquals(2, move.position());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBestMove_blockOpponentWinDepth1() {
|
||||
// - - -
|
||||
// O - -
|
||||
// X X -
|
||||
game.play(new Game.Move(6, 'X'));
|
||||
game.play(new Game.Move(3, 'O'));
|
||||
game.play(new Game.Move(7, 'X'));
|
||||
@Test
|
||||
void testBestMove_blockOpponentWinDepth1() {
|
||||
// - - -
|
||||
// O - -
|
||||
// X X -
|
||||
game.play(new Game.Move(6, 'X'));
|
||||
game.play(new Game.Move(3, 'O'));
|
||||
game.play(new Game.Move(7, 'X'));
|
||||
|
||||
final Game.Move move = ai.findBestMove(game, 1);
|
||||
final Game.Move move = ai.findBestMove(game, 1);
|
||||
|
||||
assertNotNull(move);
|
||||
assertEquals('O', move.value());
|
||||
assertEquals(8, move.position());
|
||||
}
|
||||
assertNotNull(move);
|
||||
assertEquals('O', move.value());
|
||||
assertEquals(8, move.position());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBestMove_preferCornerOnEmpty() {
|
||||
final Game.Move move = ai.findBestMove(game, 0);
|
||||
@Test
|
||||
void testBestMove_preferCornerOnEmpty() {
|
||||
final Game.Move move = ai.findBestMove(game, 0);
|
||||
|
||||
assertNotNull(move);
|
||||
assertEquals('X', move.value());
|
||||
assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
|
||||
}
|
||||
assertNotNull(move);
|
||||
assertEquals('X', move.value());
|
||||
assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBestMove_findBestMoveDraw() {
|
||||
// O X -
|
||||
// - O X
|
||||
// X O X
|
||||
game.play(new Game.Move(1, 'X'));
|
||||
game.play(new Game.Move(0, 'O'));
|
||||
game.play(new Game.Move(5, 'X'));
|
||||
game.play(new Game.Move(4, 'O'));
|
||||
game.play(new Game.Move(6, 'X'));
|
||||
game.play(new Game.Move(7, 'O'));
|
||||
game.play(new Game.Move(8, 'X'));
|
||||
@Test
|
||||
void testBestMove_findBestMoveDraw() {
|
||||
// O X -
|
||||
// - O X
|
||||
// X O X
|
||||
game.play(new Game.Move(1, 'X'));
|
||||
game.play(new Game.Move(0, 'O'));
|
||||
game.play(new Game.Move(5, 'X'));
|
||||
game.play(new Game.Move(4, 'O'));
|
||||
game.play(new Game.Move(6, 'X'));
|
||||
game.play(new Game.Move(7, 'O'));
|
||||
game.play(new Game.Move(8, 'X'));
|
||||
|
||||
final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
|
||||
final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
|
||||
|
||||
assertNotNull(move);
|
||||
assertEquals('O', move.value());
|
||||
assertEquals(2, move.position());
|
||||
}
|
||||
}
|
||||
assertNotNull(move);
|
||||
assertEquals('O', move.value());
|
||||
assertEquals(2, move.position());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user