Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
FyloZ | 48f2b72c40 | |
FyloZ | 7416df8a17 | |
FyloZ | eb0146f10a | |
william | c3cd91b199 | |
william | 648c325649 | |
william | 5f7d74e0ae | |
FyloZ | abf7f5bfd8 | |
william | dbc494042b | |
william | 02b49c9437 | |
william | bd0fc7edfd |
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -8,7 +8,7 @@
|
|||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
|
@ -4,11 +4,20 @@
|
|||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="41395b4b-3252-492c-a869-5f4dab107186" name="Changes" comment="Small fixes">
|
||||
<list default="true" id="41395b4b-3252-492c-a869-5f4dab107186" name="Changes" comment="Limite la longueur d'un coup sous les 5 secondes">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Client.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Client.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MiniMax.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MiniMax.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/PusherBoard.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/PusherBoard.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/game/PusherGame.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/game/PusherGame.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/PawnUtils.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/PawnUtils.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/AttackStrategy.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/AttackStrategy.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/DefenseStrategy.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/DefenseStrategy.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/MasterStrategy.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/MasterStrategy.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/RandomStrategy.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/RandomStrategy.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/Strategy.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/Strategy.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/WinningStrategy.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/WinningStrategy.java" afterDir="false" />
|
||||
</list>
|
||||
<list id="98b8a79f-2f53-42bf-96da-7af322697a0d" name="Changes by acastonguay" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
|
@ -19,6 +28,7 @@
|
|||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Interface" />
|
||||
<option value="Class" />
|
||||
</list>
|
||||
</option>
|
||||
|
@ -26,9 +36,10 @@
|
|||
<component name="Git.Settings">
|
||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
||||
<map>
|
||||
<entry key="$PROJECT_DIR$" value="alexis" />
|
||||
<entry key="$PROJECT_DIR$" value="master" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="MacroExpansionManager">
|
||||
<option name="directoryName" value="x0367gi2" />
|
||||
|
@ -36,12 +47,8 @@
|
|||
<component name="MarkdownSettingsMigration">
|
||||
<option name="stateVersion" value="1" />
|
||||
</component>
|
||||
<component name="MavenImportPreferences">
|
||||
<option name="importingSettings">
|
||||
<MavenImportingSettings>
|
||||
<option name="workspaceImportEnabled" value="true" />
|
||||
</MavenImportingSettings>
|
||||
</option>
|
||||
<component name="ProblemsViewState">
|
||||
<option name="selectedTabId" value="CurrentFile" />
|
||||
</component>
|
||||
<component name="ProjectId" id="2NFN0RGJNNJqiDmt34ixVwkIwEm" />
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
|
||||
|
@ -57,12 +64,17 @@
|
|||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||
"codeWithMe.voiceChat.enabledByDefault": "false",
|
||||
"git-widget-placeholder": "william",
|
||||
"last_opened_file_path": "/home/william/Dev/Projects",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"project.structure.last.edited": "Libraries",
|
||||
"project.structure.proportion": "0.15",
|
||||
"project.structure.side.proportion": "0.2",
|
||||
"settings.editor.selected.configurable": "reference.projectsettings.compiler.javacompiler",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
|
@ -72,7 +84,7 @@
|
|||
<module name="Lab4" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="laboratoire4.*" />
|
||||
<option name="PATTERN" value="laboratoire4.pawns.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
|
@ -85,7 +97,7 @@
|
|||
<module name="Lab4" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="laboratoire4.*" />
|
||||
<option name="PATTERN" value="laboratoire4.pawns.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
|
@ -98,7 +110,7 @@
|
|||
<module name="Lab4" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="laboratoire4.*" />
|
||||
<option name="PATTERN" value="laboratoire4.pawns.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
|
@ -128,6 +140,19 @@
|
|||
<workItem from="1679344371350" duration="4138000" />
|
||||
<workItem from="1679365557789" duration="21000" />
|
||||
<workItem from="1679424430928" duration="11764000" />
|
||||
<workItem from="1679593788411" duration="10753000" />
|
||||
<workItem from="1679672719638" duration="8340000" />
|
||||
<workItem from="1679779362814" duration="5912000" />
|
||||
<workItem from="1679787823160" duration="2189000" />
|
||||
<workItem from="1680198283785" duration="8256000" />
|
||||
<workItem from="1680580123067" duration="326000" />
|
||||
<workItem from="1680750346503" duration="2118000" />
|
||||
<workItem from="1680821970713" duration="10363000" />
|
||||
<workItem from="1680914341116" duration="19656000" />
|
||||
<workItem from="1681078101864" duration="10646000" />
|
||||
<workItem from="1681099446755" duration="28000" />
|
||||
<workItem from="1681099484843" duration="9035000" />
|
||||
<workItem from="1681145055287" duration="4569000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="MiniMax">
|
||||
<created>1679263366439</created>
|
||||
|
@ -150,7 +175,49 @@
|
|||
<option name="project" value="LOCAL" />
|
||||
<updated>1679490100944</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="4" />
|
||||
<task id="LOCAL-00004" summary="Fixes?">
|
||||
<created>1679682974611</created>
|
||||
<option name="number" value="00004" />
|
||||
<option name="presentableId" value="LOCAL-00004" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1679682974611</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00005" summary="Fixes?">
|
||||
<created>1679786894920</created>
|
||||
<option name="number" value="00005" />
|
||||
<option name="presentableId" value="LOCAL-00005" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1679786894920</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00006" summary="Plus d'heuristiques">
|
||||
<created>1680580135038</created>
|
||||
<option name="number" value="00006" />
|
||||
<option name="presentableId" value="LOCAL-00006" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1680580135038</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00007" summary="Beaucoup de changements">
|
||||
<created>1681061194018</created>
|
||||
<option name="number" value="00007" />
|
||||
<option name="presentableId" value="LOCAL-00007" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1681061194019</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00008" summary="Gitignore">
|
||||
<created>1681237323963</created>
|
||||
<option name="number" value="00008" />
|
||||
<option name="presentableId" value="LOCAL-00008" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1681237323963</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00009" summary="Limite la longueur d'un coup sous les 5 secondes">
|
||||
<created>1681238632769</created>
|
||||
<option name="number" value="00009" />
|
||||
<option name="presentableId" value="LOCAL-00009" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1681238632769</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="10" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
|
@ -160,17 +227,11 @@
|
|||
<MESSAGE value="MiniMax" />
|
||||
<MESSAGE value="Movement logic" />
|
||||
<MESSAGE value="Small fixes" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Small fixes" />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
<breakpoints>
|
||||
<line-breakpoint enabled="true" type="java-line">
|
||||
<url>file://$PROJECT_DIR$/src/main/java/laboratoire4/PusherBoard.java</url>
|
||||
<line>60</line>
|
||||
<option name="timeStamp" value="7" />
|
||||
</line-breakpoint>
|
||||
</breakpoints>
|
||||
</breakpoint-manager>
|
||||
<MESSAGE value="Fixes?" />
|
||||
<MESSAGE value="Plus d'heuristiques" />
|
||||
<MESSAGE value="Beaucoup de changements" />
|
||||
<MESSAGE value="Gitignore" />
|
||||
<MESSAGE value="Limite la longueur d'un coup sous les 5 secondes" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Limite la longueur d'un coup sous les 5 secondes" />
|
||||
</component>
|
||||
</project>
|
19
pom.xml
19
pom.xml
|
@ -9,9 +9,24 @@
|
|||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>laboratoire4.Main</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package laboratoire4;
|
||||
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
|
||||
public class Action<T extends IPawn> {
|
||||
private final T pawn;
|
||||
private final PawnMovement movement;
|
||||
|
||||
public Action(T pawn, PawnMovement movement) {
|
||||
this.pawn = pawn;
|
||||
this.movement = movement;
|
||||
}
|
||||
|
||||
public T getPawn() {
|
||||
return pawn;
|
||||
}
|
||||
|
||||
public PawnMovement getMovement() {
|
||||
return movement;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package laboratoire4;
|
||||
|
||||
import laboratoire4.game.PusherGame;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -9,7 +11,7 @@ public class Client {
|
|||
private final Socket socket;
|
||||
private final BufferedInputStream input;
|
||||
private final BufferedOutputStream output;
|
||||
private PusherBoard board;
|
||||
private PusherGame board;
|
||||
|
||||
public Client(String host, int port) throws IOException {
|
||||
socket = new Socket(host, port);
|
||||
|
@ -60,7 +62,7 @@ public class Client {
|
|||
String s = new String(aBuffer).trim();
|
||||
String[] boardValues = s.split(" ");
|
||||
|
||||
board = new PusherBoard(player, boardValues);
|
||||
board = new PusherGame(player, boardValues);
|
||||
|
||||
return player == Player.RED;
|
||||
}
|
||||
|
@ -76,24 +78,27 @@ public class Client {
|
|||
board.move(previousMove);
|
||||
}
|
||||
|
||||
// Thread.sleep(1000);
|
||||
Thread.sleep(200);
|
||||
|
||||
String nextMove = board.runNextMove();
|
||||
System.out.println("Prochain mouvement: " + nextMove);
|
||||
System.out.printf("Prochain mouvement: %s\n\n", nextMove);
|
||||
|
||||
output.write(nextMove.getBytes(), 0, nextMove.length());
|
||||
output.flush();
|
||||
}
|
||||
|
||||
private void handleInvalidMove() throws InterruptedException {
|
||||
private void handleInvalidMove() throws InterruptedException, IOException {
|
||||
System.err.println("Mouvement invalide!");
|
||||
Thread.sleep(500);
|
||||
board.printBoard();
|
||||
|
||||
PusherGame.printBoard(board.getBoard());
|
||||
|
||||
// play();
|
||||
}
|
||||
|
||||
private void stopGame() throws IOException {
|
||||
System.out.println("GG well played!");
|
||||
socket.close();
|
||||
// socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package laboratoire4;
|
||||
|
||||
import laboratoire4.game.PusherGame;
|
||||
import laboratoire4.pawns.Pawn;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class GameTree {
|
||||
private Node root;
|
||||
|
||||
public Node buildTree(PusherBoard board){
|
||||
public Node buildTree(PusherGame board){
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -16,9 +20,9 @@ public class GameTree {
|
|||
static class Node {
|
||||
private final Collection<Node> childs;
|
||||
private final Pawn pawn;
|
||||
private final Pawn.PawnMovement movement;
|
||||
private final PawnMovement movement;
|
||||
|
||||
Node(Collection<Node> childs, Pawn pawn, Pawn.PawnMovement movement) {
|
||||
Node(Collection<Node> childs, Pawn pawn, PawnMovement movement) {
|
||||
this.childs = childs;
|
||||
this.pawn = pawn;
|
||||
this.movement = movement;
|
||||
|
@ -32,7 +36,7 @@ public class GameTree {
|
|||
return pawn;
|
||||
}
|
||||
|
||||
public Pawn.PawnMovement getMovement() {
|
||||
public PawnMovement getMovement() {
|
||||
return movement;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package laboratoire4;
|
||||
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
|
||||
public interface IPawn {
|
||||
boolean isMoveValid(IPawn[][] board, PawnMovement movement);
|
||||
boolean isMoveValid(IPawn[][] board, PawnMovement movement, boolean ignorePlayers);
|
||||
boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayer);
|
||||
boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol);
|
||||
void move(PawnMovement movement);
|
||||
boolean isPusher();
|
||||
int getDirection();
|
||||
Player getPlayer();
|
||||
int getRow();
|
||||
int getCol();
|
||||
void setRow(int row);
|
||||
void setCol(int col);
|
||||
}
|
|
@ -1,9 +1,27 @@
|
|||
package laboratoire4;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws IOException {
|
||||
new Client("localhost", 8888).listen();
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
new Client("localhost", 8888).listen();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.print("Recommencer? (Y/n) ");
|
||||
|
||||
if (scanner.hasNextLine()) {
|
||||
String nextLine = scanner.nextLine();
|
||||
|
||||
if (nextLine.equals("n")) {
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
package laboratoire4;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class MiniMax {
|
||||
private static final int MAX_DEPTH = 2;
|
||||
private static Random random = new Random();
|
||||
|
||||
public static MiniMaxResult miniMax(PusherBoard board) {
|
||||
return miniMax(board, true, 0, 0);
|
||||
}
|
||||
|
||||
private static MiniMaxResult miniMax(PusherBoard board, boolean max, int depth, int alphaBeta) {
|
||||
int limScore = max ? Integer.MIN_VALUE : Integer.MAX_VALUE;
|
||||
List<Pawn> pawns = max ?
|
||||
board.getMaxPawns() :
|
||||
board.getMinPawns();
|
||||
// Collections.shuffle(pawns);
|
||||
|
||||
List<MiniMaxResult> results = new ArrayList<>();
|
||||
|
||||
for (Pawn pawn : pawns) {
|
||||
int originalRow = pawn.getRow();
|
||||
int originalCol = pawn.getCol();
|
||||
|
||||
for (Pawn.PawnMovement movement : Pawn.PawnMovement.values()) {
|
||||
if (pawn.isMoveValid(board, movement)) {
|
||||
pawn.move(movement);
|
||||
|
||||
int nextAlphaBeta = max ? Math.max(limScore, alphaBeta) : Math.min(limScore, alphaBeta);
|
||||
|
||||
int score = evaluate(pawn, board, max);
|
||||
if (depth < MAX_DEPTH) {
|
||||
score = miniMax(board, !max, depth + 1, nextAlphaBeta).getScore();
|
||||
}
|
||||
|
||||
if ((max && score > limScore) ||
|
||||
(!max && score < limScore)) {
|
||||
limScore = score;
|
||||
}
|
||||
|
||||
MiniMaxResult result = new MiniMaxResult(limScore, pawn, movement);
|
||||
|
||||
//elagage alphaBeta
|
||||
// if ((max && limScore > alphaBeta) ||
|
||||
// (!max && limScore < alphaBeta)) {
|
||||
// pawn.setRow(originalRow);
|
||||
// pawn.setCol(originalCol);
|
||||
//
|
||||
// return result;
|
||||
// }
|
||||
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
pawn.setRow(originalRow);
|
||||
pawn.setCol(originalCol);
|
||||
}
|
||||
}
|
||||
|
||||
// Choisir aléatoirement
|
||||
int index = random.nextInt(results.size());
|
||||
return results.get(index);
|
||||
}
|
||||
|
||||
private static int evaluate(Pawn pawn, PusherBoard board, boolean max) {
|
||||
int score = didWin(pawn, max) + didCapture(pawn, board, max) + isPushed(pawn);
|
||||
|
||||
int homeRow = pawn.getPlayer() == Player.RED ? 0 : 7;
|
||||
if (pawn.getRow() != homeRow) {
|
||||
score += 50;
|
||||
}
|
||||
|
||||
int direction = max ? 1 : -1;
|
||||
return score * direction;
|
||||
}
|
||||
|
||||
private static int isPushed(Pawn pawn) {
|
||||
if (pawn instanceof Pushed) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int didWin(Pawn pawn, boolean max) {
|
||||
int goal = pawn.getPlayer() == Player.RED ? 7 : 0;
|
||||
|
||||
if (pawn.getRow() == goal) {
|
||||
return max ? 1000 : 100000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int didCapture(Pawn pawn, PusherBoard board, boolean max) {
|
||||
Pawn capturedPawn = board.getBoard()[pawn.getRow()][pawn.getCol()];
|
||||
|
||||
if (capturedPawn != null) {
|
||||
if (capturedPawn.getPlayer() != pawn.getPlayer()) {
|
||||
if (max) {
|
||||
return capturedPawn instanceof Pusher ? 100 : 50;
|
||||
} else {
|
||||
return capturedPawn instanceof Pusher ? 10000 : 5000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static class MiniMaxResult {
|
||||
private final int score;
|
||||
private final Pawn pawn;
|
||||
private final Pawn.PawnMovement movement;
|
||||
|
||||
public MiniMaxResult(int score, Pawn pawn, Pawn.PawnMovement movement) {
|
||||
this.score = score;
|
||||
this.pawn = pawn;
|
||||
this.movement = movement;
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public Pawn getPawn() {
|
||||
return pawn;
|
||||
}
|
||||
|
||||
public Pawn.PawnMovement getMovement() {
|
||||
return movement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MiniMaxResult{" +
|
||||
"score=" + score +
|
||||
", pawn=" + pawn +
|
||||
", movement=" + movement +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
package laboratoire4;
|
||||
|
||||
public abstract class Pawn {
|
||||
protected final Player player;
|
||||
protected int row;
|
||||
protected int col;
|
||||
protected int direction;
|
||||
|
||||
public Pawn(Player player, int row, int col) {
|
||||
this.player = player;
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
this.direction = player == Player.RED ? 1 : -1;
|
||||
}
|
||||
|
||||
public void move(PawnMovement movement) {
|
||||
setRow(row + direction);
|
||||
setCol(col + movement.move);
|
||||
}
|
||||
|
||||
public String getPosition() {
|
||||
char colStr = (char)(col + 65);
|
||||
return String.format("%s%d", colStr, row + 1);
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public int getCol() {
|
||||
return col;
|
||||
}
|
||||
|
||||
public int getRow() {
|
||||
return row;
|
||||
}
|
||||
|
||||
public void setCol(int col) {
|
||||
this.col = col;
|
||||
}
|
||||
|
||||
public void setRow(int row) {
|
||||
this.row = row;
|
||||
}
|
||||
|
||||
public int getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public boolean isMoveValid(PusherBoard game, PawnMovement movement) {
|
||||
Pawn[][] board = game.getBoard();
|
||||
|
||||
int nextRow = row + getDirection();
|
||||
if (nextRow < 0 || nextRow >= board.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int nextCol = col + movement.move;
|
||||
if (nextCol < 0 || nextCol >= board.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isMoveValid(board, movement);
|
||||
}
|
||||
|
||||
protected abstract boolean isMoveValid(Pawn[][] board, PawnMovement movement);
|
||||
|
||||
enum PawnMovement {
|
||||
STRAIGHT(0),
|
||||
LEFT_DIAGONAL(-1),
|
||||
RIGHT_DIAGONAL(1);
|
||||
|
||||
private final int move;
|
||||
|
||||
PawnMovement(int move) {
|
||||
this.move = move;
|
||||
}
|
||||
|
||||
public int getMove() {
|
||||
return move;
|
||||
}
|
||||
|
||||
public static PawnMovement from(int move) {
|
||||
if (move == 0) {
|
||||
return STRAIGHT;
|
||||
}
|
||||
if (move == 1) {
|
||||
return RIGHT_DIAGONAL;
|
||||
}
|
||||
return LEFT_DIAGONAL;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,28 @@
|
|||
package laboratoire4;
|
||||
|
||||
public enum Player {
|
||||
BLACK,
|
||||
RED
|
||||
BLACK(-1, 0, 7),
|
||||
RED(1, 7, 0);
|
||||
|
||||
private final int direction;
|
||||
private final int goal;
|
||||
private final int home;
|
||||
|
||||
Player(int direction, int goal, int home) {
|
||||
this.direction = direction;
|
||||
this.goal = goal;
|
||||
this.home = home;
|
||||
}
|
||||
|
||||
public int getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public int getGoal() {
|
||||
return goal;
|
||||
}
|
||||
|
||||
public int getHome() {
|
||||
return home;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
package laboratoire4;
|
||||
|
||||
public class Pushed extends Pawn {
|
||||
public Pushed(Player player, int row, int col) {
|
||||
super(player, row, col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMoveValid(Pawn[][] board, PawnMovement movement) {
|
||||
Pawn pusher = null;
|
||||
Pawn to = board[row + direction][col + movement.getMove()];
|
||||
|
||||
if (col > 0 && movement == PawnMovement.RIGHT_DIAGONAL) {
|
||||
pusher = board[row - direction][col - 1];
|
||||
} else if (col < board.length - 1 && movement == PawnMovement.LEFT_DIAGONAL) {
|
||||
pusher = board[row - direction][col + 1];
|
||||
} else if (movement == PawnMovement.STRAIGHT) {
|
||||
pusher = board[row - direction][col];
|
||||
}
|
||||
|
||||
boolean pusherValid = pusher instanceof Pusher && pusher.player == player;
|
||||
boolean destinationValid = to == null || (movement != PawnMovement.STRAIGHT && to.player != this.player);
|
||||
return pusherValid && destinationValid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Pushed{" +
|
||||
player +
|
||||
", " + col +
|
||||
", " + row +
|
||||
"} ";
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package laboratoire4;
|
||||
|
||||
public class Pusher extends Pawn {
|
||||
public Pusher(Player player, int row, int col) {
|
||||
super(player, row, col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMoveValid(Pawn[][] board, PawnMovement movement) {
|
||||
Pawn to = board[row + direction][col + movement.getMove()];
|
||||
|
||||
if (to == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return to.player != this.player && movement != PawnMovement.STRAIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Pusher{" +
|
||||
player +
|
||||
", " + col +
|
||||
", " + row +
|
||||
"} ";
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package laboratoire4;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PusherBoard {
|
||||
private final Player player;
|
||||
private Pawn[][] board;
|
||||
|
||||
private final List<Pawn> maxPawns = new ArrayList<>();
|
||||
private final List<Pawn> minPawns = new ArrayList<>();
|
||||
|
||||
public PusherBoard(Player player, String[] boardValues) {
|
||||
this.player = player;
|
||||
|
||||
newGame(boardValues);
|
||||
}
|
||||
|
||||
public void newGame(String[] boardValues) {
|
||||
this.board = new Pawn[8][8];
|
||||
|
||||
int col = 0, row = 0;
|
||||
for (String boardValue : boardValues) {
|
||||
int v = Integer.parseInt(boardValue);
|
||||
if (v != 0) {
|
||||
Player pawnPlayer = (v == 1 || v == 2) ? Player.RED : Player.BLACK;
|
||||
Pawn pawn;
|
||||
if (v % 2 == 0) { // 2 et 4 sont les pushers
|
||||
pawn = new Pusher(pawnPlayer, row, col);
|
||||
} else {
|
||||
pawn = new Pushed(pawnPlayer, row, col);
|
||||
}
|
||||
|
||||
if (pawnPlayer == player) {
|
||||
maxPawns.add(pawn);
|
||||
} else {
|
||||
minPawns.add(pawn);
|
||||
}
|
||||
|
||||
board[row][col] = pawn;
|
||||
}
|
||||
|
||||
col++;
|
||||
if (col == board.length) {
|
||||
col = 0;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String runNextMove() {
|
||||
MiniMax.MiniMaxResult result = MiniMax.miniMax(this);
|
||||
Pawn pawn = result.getPawn();
|
||||
String initialPosition = pawn.getPosition();
|
||||
|
||||
System.out.println(result.getScore());
|
||||
|
||||
move(pawn, result.getMovement());
|
||||
|
||||
|
||||
return initialPosition + "-" + pawn.getPosition();
|
||||
}
|
||||
|
||||
public void move(String move) {
|
||||
//FORMAT ex : D2-D3
|
||||
String[] split = move.trim().split(" - ");
|
||||
move(split[0], split[1]);
|
||||
}
|
||||
|
||||
private void move(Pawn pawn, Pawn.PawnMovement movement) {
|
||||
move(pawn.getCol(), pawn.getRow(), pawn.getCol() + movement.getMove(), pawn.getRow() + pawn.getDirection());
|
||||
}
|
||||
|
||||
public void move(String from, String to) {
|
||||
//FORMAT ex : from {D2}, to {D3}
|
||||
int from_col = (int) from.charAt(0) - 65;
|
||||
int from_row = Integer.parseInt(String.valueOf(from.charAt(1))) - 1;
|
||||
int to_col = (int) to.charAt(0) - 65;
|
||||
int to_row = Integer.parseInt(String.valueOf(to.charAt(1))) - 1;
|
||||
|
||||
move(from_col, from_row, to_col, to_row);
|
||||
}
|
||||
|
||||
public void move(int from_col, int from_row, int to_col, int to_row) {
|
||||
if (!isValid(from_col, from_row, to_col)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pawn pawn = board[from_row][from_col];
|
||||
Pawn destPawn = board[to_row][to_col];
|
||||
|
||||
if (destPawn != null) {
|
||||
if (destPawn.getPlayer() == player) {
|
||||
maxPawns.remove(destPawn);
|
||||
} else {
|
||||
minPawns.remove(destPawn);
|
||||
}
|
||||
}
|
||||
|
||||
board[from_row][from_col] = null;
|
||||
board[to_row][to_col] = pawn;
|
||||
|
||||
pawn.setRow(to_row);
|
||||
pawn.setCol(to_col);
|
||||
}
|
||||
|
||||
|
||||
private boolean isValid(int from_col, int from_row, int to_col) {
|
||||
Pawn pawn = getBoard()[from_row][from_col];
|
||||
Pawn.PawnMovement move = Pawn.PawnMovement.from(to_col - from_col);
|
||||
|
||||
if (pawn == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pawn.isMoveValid(this, move);
|
||||
}
|
||||
|
||||
public Pawn[][] getBoard() {
|
||||
return board;
|
||||
}
|
||||
|
||||
public List<Pawn> getMaxPawns() {
|
||||
return maxPawns;
|
||||
}
|
||||
|
||||
public List<Pawn> getMinPawns() {
|
||||
return minPawns;
|
||||
}
|
||||
|
||||
public void printBoard() {
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
for (int j = 0; j < this.board.length; j++) {
|
||||
if (this.board[i][j] != null) {
|
||||
System.out.print(this.board[i][j] + " | ");
|
||||
} else {
|
||||
System.out.print(" | ");
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println("----------------------------------------------------------------------------------------");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package laboratoire4.game;
|
||||
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.Player;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
|
||||
public interface Game {
|
||||
void move(IPawn pawn, PawnMovement movement);
|
||||
|
||||
IPawn[][] getBoard();
|
||||
|
||||
Player getPlayer();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package laboratoire4.game;
|
||||
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.Player;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class GameUtils {
|
||||
public static <T extends IPawn> Stream<T> getPawnsAsStream(T[][] board) {
|
||||
return Arrays.stream(board)
|
||||
.flatMap(Arrays::stream)
|
||||
.filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
public static <T extends IPawn> Collection<IPawn> getPawns(T[][] board) {
|
||||
return getPawnsAsStream(board).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T extends IPawn> Stream<T> getMaxPawnsAsStream(T[][] board, Player player) {
|
||||
return getPawnsAsStream(board).filter(p -> p.getPlayer() == player);
|
||||
}
|
||||
|
||||
public static <T extends IPawn> Collection<T> getMaxPawns(T[][] board, Player player) {
|
||||
return getMaxPawnsAsStream(board, player).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T extends IPawn> Stream<T> getMinPawnsAsStream(T[][] board, Player player) {
|
||||
return getPawnsAsStream(board).filter(p -> p.getPlayer() != player);
|
||||
}
|
||||
|
||||
public static <T extends IPawn> Collection<T> getMinPawns(T[][] board, Player player) {
|
||||
return getMinPawnsAsStream(board, player).collect(Collectors.toList());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package laboratoire4.game;
|
||||
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.Player;
|
||||
import laboratoire4.pawns.Pawn;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
import laboratoire4.pawns.Pushed;
|
||||
import laboratoire4.pawns.Pusher;
|
||||
import laboratoire4.strategies.EvaluationResult;
|
||||
import laboratoire4.strategies.MasterStrategy;
|
||||
|
||||
public class PusherGame implements Game {
|
||||
private final Player player;
|
||||
private Pawn[][] board;
|
||||
|
||||
public PusherGame(Player player, String[] boardValues) {
|
||||
this.player = player;
|
||||
|
||||
newGame(boardValues);
|
||||
}
|
||||
|
||||
public void newGame(String[] boardValues) {
|
||||
this.board = new Pawn[8][8];
|
||||
|
||||
int col = 0, row = 0;
|
||||
for (String boardValue : boardValues) {
|
||||
int v = Integer.parseInt(boardValue);
|
||||
if (v != 0) {
|
||||
Player pawnPlayer = (v == 1 || v == 2) ? Player.RED : Player.BLACK;
|
||||
Pawn pawn;
|
||||
if (v % 2 == 0) { // 2 et 4 sont les pushers
|
||||
pawn = new Pusher(pawnPlayer, row, col);
|
||||
} else {
|
||||
pawn = new Pushed(pawnPlayer, row, col);
|
||||
}
|
||||
|
||||
board[row][col] = pawn;
|
||||
}
|
||||
|
||||
col++;
|
||||
if (col == board.length) {
|
||||
col = 0;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String runNextMove() {
|
||||
EvaluationResult result = MasterStrategy.getInstance(this).getNextMove();
|
||||
if (result == null) {
|
||||
// Ce n'est pas censé arriver, on réessaie
|
||||
System.err.println("Aucune résultat n'a été retourné!");
|
||||
result = MasterStrategy.getInstance(this).getNextMove();
|
||||
}
|
||||
|
||||
Pawn pawn = board[result.getRow()][result.getCol()];
|
||||
|
||||
String initialPosition = pawn.getPosition();
|
||||
move(pawn, result.getMovement());
|
||||
String nextPosition = pawn.getPosition();
|
||||
|
||||
return initialPosition + "-" + nextPosition;
|
||||
}
|
||||
|
||||
public void move(String move) {
|
||||
String[] split = move.trim().split(" - ");
|
||||
String from = split[0];
|
||||
String to = split[1];
|
||||
|
||||
int fromCol = (int) from.charAt(0) - 65;
|
||||
int fromRow = Integer.parseInt(String.valueOf(from.charAt(1))) - 1;
|
||||
int toCol = (int) to.charAt(0) - 65;
|
||||
|
||||
PawnMovement movement = PawnMovement.from(toCol - fromCol);
|
||||
move(fromRow, fromCol, movement);
|
||||
}
|
||||
|
||||
public void move(int row, int col, PawnMovement movement) {
|
||||
Pawn pawn = board[row][col];
|
||||
if (pawn == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
move(pawn, movement);
|
||||
}
|
||||
|
||||
public void move(IPawn pawn, PawnMovement movement) {
|
||||
int toRow = pawn.getRow() + pawn.getDirection();
|
||||
int toCol = pawn.getCol() + movement.getMove();
|
||||
|
||||
board[pawn.getRow()][pawn.getCol()] = null;
|
||||
board[toRow][toCol] = (Pawn) pawn;
|
||||
pawn.move(movement);
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public Pawn[][] getBoard() {
|
||||
return board;
|
||||
}
|
||||
|
||||
public static void printBoard(Pawn[][] board) {
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
for (int j = 0; j < board.length; j++) {
|
||||
if (board[i][j] != null) {
|
||||
System.out.print(board[i][j] + " | ");
|
||||
} else {
|
||||
System.out.print(" | ");
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println("----------------------------------------------------------------------------------------");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package laboratoire4.game;
|
||||
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.Player;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
import laboratoire4.pawns.SimulatedPawn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SimulatedGame implements Game {
|
||||
private final SimulatedPawn[][] board;
|
||||
private final Player player;
|
||||
|
||||
private final Stack<SimulatedPawn> removedPawns = new Stack<>();
|
||||
private final Stack<SimulatedPawn> previousMovedPawns = new Stack<>();
|
||||
|
||||
public SimulatedGame(IPawn[][] board, Player player) {
|
||||
this.board = asMovingPawns(board);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void move(IPawn pawn, PawnMovement movement) {
|
||||
int toRow = pawn.getRow() + pawn.getDirection();
|
||||
int toCol = pawn.getCol() + movement.getMove();
|
||||
SimulatedPawn capturedPawn = board[toRow][toCol];
|
||||
|
||||
previousMovedPawns.push((SimulatedPawn) pawn);
|
||||
removedPawns.push(capturedPawn);
|
||||
|
||||
board[pawn.getRow()][pawn.getCol()] = null;
|
||||
board[toRow][toCol] = (SimulatedPawn) pawn;
|
||||
pawn.move(movement);
|
||||
}
|
||||
|
||||
public void revertMove() {
|
||||
SimulatedPawn pawn = previousMovedPawns.pop();
|
||||
SimulatedPawn capturedPawn = removedPawns.pop();
|
||||
|
||||
board[pawn.getRow()][pawn.getCol()] = capturedPawn;
|
||||
|
||||
pawn.revertMove();
|
||||
board[pawn.getRow()][pawn.getCol()] = pawn;
|
||||
}
|
||||
|
||||
public SimulatedPawn getNextPawn(IPawn pawn, PawnMovement move) {
|
||||
int nextRow = pawn.getRow() + pawn.getDirection();
|
||||
int nextCol = pawn.getCol() + move.getMove();
|
||||
|
||||
if (nextCol < 0 || nextCol > 7) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return board[nextRow][nextCol];
|
||||
}
|
||||
|
||||
public SimulatedPawn[][] getBoard() {
|
||||
return board;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public boolean hasCaptured() {
|
||||
return removedPawns.peek() != null;
|
||||
}
|
||||
|
||||
|
||||
private Collection<SimulatedPawn> getPawns(Predicate<SimulatedPawn> predicate) {
|
||||
List<SimulatedPawn> pawns = new ArrayList<>();
|
||||
for (SimulatedPawn[] row : board) {
|
||||
for (SimulatedPawn pawn : row) {
|
||||
if (pawn != null && predicate.test(pawn)) {
|
||||
pawns.add(pawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pawns;
|
||||
}
|
||||
|
||||
public static SimulatedPawn[][] asMovingPawns(IPawn[][] board) {
|
||||
SimulatedPawn[][] to = new SimulatedPawn[board.length][board.length];
|
||||
for (int row = 0; row < board.length; row++) {
|
||||
for (int col = 0; col < board.length; col++) {
|
||||
if (board[row][col] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SimulatedPawn pawn = SimulatedPawn.from(board[row][col]);
|
||||
to[row][col] = pawn;
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package laboratoire4.pawns;
|
||||
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.Player;
|
||||
|
||||
public abstract class Pawn implements IPawn {
|
||||
protected final Player player;
|
||||
protected int row;
|
||||
protected int col;
|
||||
|
||||
public Pawn(Player player, int row, int col) {
|
||||
this.player = player;
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
}
|
||||
|
||||
public void move(PawnMovement movement) {
|
||||
setRow(row + player.getDirection());
|
||||
setCol(col + movement.getMove());
|
||||
}
|
||||
|
||||
public String getPosition() {
|
||||
char colStr = (char) (col + 65);
|
||||
return String.format("%s%d", colStr, row + 1);
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public int getCol() {
|
||||
return col;
|
||||
}
|
||||
|
||||
public int getRow() {
|
||||
return row;
|
||||
}
|
||||
|
||||
public void setCol(int col) {
|
||||
this.col = col;
|
||||
}
|
||||
|
||||
public void setRow(int row) {
|
||||
this.row = row;
|
||||
}
|
||||
|
||||
public int getDirection() {
|
||||
return player.getDirection();
|
||||
}
|
||||
|
||||
public boolean isMoveValid(IPawn[][] board, PawnMovement movement) {
|
||||
return isMoveValid(board, movement, false);
|
||||
}
|
||||
|
||||
public boolean isMoveValid(IPawn[][] board, PawnMovement movement, boolean ignorePlayers) {
|
||||
return isMoveValid(board, movement, row, col, ignorePlayers);
|
||||
}
|
||||
|
||||
public boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
|
||||
return isMoveValid(board, movement, fromRow, fromCol, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayers) {
|
||||
int nextRow = fromRow + getDirection();
|
||||
if (nextRow < 0 || nextRow >= board.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int nextCol = fromCol + movement.getMove();
|
||||
if (nextCol < 0 || nextCol >= board.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isMoveReallyValid(board, movement, fromRow, fromCol, ignorePlayers);
|
||||
}
|
||||
|
||||
protected abstract boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayers);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package laboratoire4.pawns;
|
||||
|
||||
public enum PawnMovement {
|
||||
STRAIGHT(0),
|
||||
LEFT_DIAGONAL(-1),
|
||||
RIGHT_DIAGONAL(1);
|
||||
|
||||
private final int move;
|
||||
|
||||
PawnMovement(int move) {
|
||||
this.move = move;
|
||||
}
|
||||
|
||||
public int getMove() {
|
||||
return move;
|
||||
}
|
||||
|
||||
public PawnMovement getOpposite() {
|
||||
return from(move * -1);
|
||||
}
|
||||
|
||||
public static PawnMovement from(int move) {
|
||||
if (move == 0) {
|
||||
return STRAIGHT;
|
||||
}
|
||||
if (move == 1) {
|
||||
return RIGHT_DIAGONAL;
|
||||
}
|
||||
return LEFT_DIAGONAL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
package laboratoire4.pawns;
|
||||
|
||||
import laboratoire4.Action;
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.Player;
|
||||
import laboratoire4.game.Game;
|
||||
import laboratoire4.game.GameUtils;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
|
||||
public class PawnUtils {
|
||||
public static int distanceFromGoal(IPawn pawn) {
|
||||
return Math.abs(pawn.getPlayer().getGoal() - pawn.getRow());
|
||||
}
|
||||
|
||||
public static int distanceFromHome(IPawn pawn) {
|
||||
return Math.abs(pawn.getRow() - pawn.getPlayer().getHome());
|
||||
}
|
||||
|
||||
public static boolean areSamePlayers(IPawn a, IPawn b) {
|
||||
return a != null && b != null && a.getPlayer() == b.getPlayer();
|
||||
}
|
||||
|
||||
public static boolean hasEasyWinPath(IPawn[][] board, IPawn initialPawn) {
|
||||
Queue<Point> positionsToVisit = new ArrayDeque<>();
|
||||
positionsToVisit.add(new Point(initialPawn.getRow(), initialPawn.getCol()));
|
||||
|
||||
while (!positionsToVisit.isEmpty()) {
|
||||
Point pos = positionsToVisit.poll();
|
||||
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
if (!initialPawn.isMoveValid(board, movement, pos.x, pos.y, false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int nextRow = pos.x + initialPawn.getDirection();
|
||||
int nextCol = pos.y + movement.getMove();
|
||||
|
||||
IPawn pawn = board[nextRow][nextCol];
|
||||
if (pawn != null && pawn.getPlayer() != initialPawn.getPlayer() && pawn.isMoveValid(board, movement.getOpposite())) {
|
||||
// On peut se faire capturer en allant là !
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextRow == initialPawn.getPlayer().getGoal()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
positionsToVisit.add(new Point(nextRow, nextCol));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean canBeCaptured(Game game, IPawn pawn) {
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
int nextRow = pawn.getRow() + pawn.getDirection();
|
||||
int nextCol = pawn.getCol() + movement.getMove();
|
||||
|
||||
if (nextRow < 0 || nextRow > 7 || nextCol < 0 || nextCol > 7) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IPawn nearPawn = game.getBoard()[nextRow][nextCol];
|
||||
if (nearPawn == null || PawnUtils.areSamePlayers(pawn, nearPawn)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nearPawn.isMoveValid(game.getBoard(), movement.getOpposite())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean canBeCaptured(Game game, int row, int col, Player player) {
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
int nextRow = row + player.getDirection();
|
||||
int nextCol = col + movement.getMove();
|
||||
|
||||
if (nextRow < 0 || nextRow > 7 || nextCol < 0 || nextCol > 7) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IPawn nearPawn = game.getBoard()[nextRow][nextCol];
|
||||
if (nearPawn == null || player == nearPawn.getPlayer()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nearPawn.isMoveValid(game.getBoard(), movement.getOpposite(), true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean canCapture(Game game, IPawn max, IPawn min) {
|
||||
int rowDistance = Math.abs(min.getRow() - max.getRow());
|
||||
int colDistance = Math.abs(min.getCol() - max.getCol());
|
||||
|
||||
if (colDistance > rowDistance) {
|
||||
// Le pion ne pourra jamais être atteint
|
||||
return false;
|
||||
}
|
||||
|
||||
return PawnUtils.canCapture(game, max, min, max.getRow(), max.getCol(), 0, rowDistance);
|
||||
}
|
||||
|
||||
public static boolean canCapture(Game game, IPawn max, IPawn min, int row, int col, int depth, int maxDepth) {
|
||||
if (min.getRow() == row && min.getCol() == col) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (depth >= maxDepth) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
if (max.isMoveValid(game.getBoard(), movement, row, col, false)) {
|
||||
int nextRow = row + max.getDirection();
|
||||
int nextCol = col + movement.getMove();
|
||||
|
||||
if (canCapture(game, max, min, nextRow, nextCol, depth + 1, maxDepth)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T extends IPawn> Collection<Action<T>> getActions(Game game, boolean max) {
|
||||
return getActions(game, max, true, false);
|
||||
}
|
||||
|
||||
public static <T extends IPawn> Collection<Action<T>> getActions(Game game, boolean max, boolean excludeDefense, boolean excludeDanger) {
|
||||
List<Action<T>> actions = new ArrayList<>();
|
||||
PawnMovement[] movements = PawnMovement.values();
|
||||
|
||||
//noinspection unchecked
|
||||
Collection<T> pawns = (Collection<T>) (max ?
|
||||
GameUtils.getMaxPawns(game.getBoard(), game.getPlayer()) :
|
||||
GameUtils.getMinPawns(game.getBoard(), game.getPlayer()));
|
||||
|
||||
for (T pawn : pawns) {
|
||||
if (excludeDefense && pawn.getRow() == pawn.getPlayer().getHome()) {
|
||||
int col = pawn.getCol();
|
||||
|
||||
// Si possible, on ne bouge pas ces pushers, comme ça on a une défense d'urgence totale
|
||||
if (col == 1 || col == 2 || col == 5 || col == 6) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (PawnMovement movement : movements) {
|
||||
if (pawn.isMoveValid(game.getBoard(), movement)) {
|
||||
if (excludeDanger && PawnUtils.canBeCaptured(game, pawn.getRow() + pawn.getDirection(), pawn.getCol() + movement.getMove(), pawn.getPlayer())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
actions.add(new Action<>(pawn, movement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actions.isEmpty()) {
|
||||
if (excludeDanger && excludeDefense) {
|
||||
return getActions(game, max, true, false);
|
||||
}
|
||||
|
||||
if (excludeDefense) {
|
||||
return getActions(game, max, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.shuffle(actions);
|
||||
return actions;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package laboratoire4.pawns;
|
||||
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.Player;
|
||||
|
||||
public class Pushed extends Pawn {
|
||||
public Pushed(Player player, int row, int col) {
|
||||
super(player, row, col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPusher() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayers) {
|
||||
int direction = getDirection();
|
||||
IPawn pusher = null;
|
||||
IPawn to = board[fromRow + direction][fromCol + movement.getMove()];
|
||||
|
||||
if (fromCol > 0 && movement == PawnMovement.RIGHT_DIAGONAL) {
|
||||
pusher = board[fromRow - direction][fromCol - movement.getMove()];
|
||||
} else if (fromCol < board.length - 1 && movement == PawnMovement.LEFT_DIAGONAL) {
|
||||
pusher = board[fromRow - direction][fromCol - movement.getMove()];
|
||||
} else if (movement == PawnMovement.STRAIGHT) {
|
||||
pusher = board[fromRow - direction][fromCol];
|
||||
}
|
||||
|
||||
boolean pusherValid = pusher != null && pusher.isPusher() && pusher.getPlayer() == player;
|
||||
boolean destinationValid = to == null || (movement != PawnMovement.STRAIGHT && (ignorePlayers || to.getPlayer() != this.player));
|
||||
return pusherValid && destinationValid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Pushed{%s, %s}", player, getPosition());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package laboratoire4.pawns;
|
||||
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.Player;
|
||||
|
||||
public class Pusher extends Pawn {
|
||||
public Pusher(Player player, int row, int col) {
|
||||
super(player, row, col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPusher() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayers) {
|
||||
IPawn to = board[fromRow + getDirection()][fromCol + movement.getMove()];
|
||||
|
||||
if (to == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ignorePlayers && to.getPlayer() == player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return movement != PawnMovement.STRAIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Pusher{%s, %s}", player, getPosition());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package laboratoire4.pawns;
|
||||
|
||||
import laboratoire4.IPawn;
|
||||
|
||||
public interface SimulatedPawn extends IPawn {
|
||||
void revertMove();
|
||||
int getMoveCount();
|
||||
int getOriginalRow();
|
||||
int getOriginalCol();
|
||||
|
||||
static SimulatedPawn from(IPawn pawn) {
|
||||
if (pawn instanceof Pusher) {
|
||||
return new SimulatedPusher(pawn.getPlayer(), pawn.getRow(), pawn.getCol());
|
||||
}
|
||||
|
||||
return new SimulatedPushed(pawn.getPlayer(), pawn.getRow(), pawn.getCol());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package laboratoire4.pawns;
|
||||
|
||||
import laboratoire4.Player;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
public class SimulatedPushed extends Pushed implements SimulatedPawn {
|
||||
private final int originalRow;
|
||||
private final int originalCol;
|
||||
private final Stack<PawnMovement> previousMoves = new Stack<>();
|
||||
private int moveCount = 0;
|
||||
|
||||
public SimulatedPushed(Player player, int row, int col) {
|
||||
super(player, row, col);
|
||||
|
||||
originalRow = row;
|
||||
originalCol = col;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(PawnMovement movement) {
|
||||
super.move(movement);
|
||||
previousMoves.push(movement);
|
||||
moveCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revertMove() {
|
||||
PawnMovement move = previousMoves.pop();
|
||||
setRow(row - getDirection());
|
||||
setCol(col - move.getMove());
|
||||
moveCount--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOriginalRow() {
|
||||
return originalRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOriginalCol() {
|
||||
return originalCol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMoveCount() {
|
||||
return moveCount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package laboratoire4.pawns;
|
||||
|
||||
import laboratoire4.Player;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
public class SimulatedPusher extends Pusher implements SimulatedPawn {
|
||||
private final int originalRow;
|
||||
private final int originalCol;
|
||||
private final Stack<PawnMovement> previousMoves = new Stack<>();
|
||||
private int moveCount = 0;
|
||||
|
||||
public SimulatedPusher(Player player, int row, int col) {
|
||||
super(player, row, col);
|
||||
|
||||
originalRow = row;
|
||||
originalCol = col;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(PawnMovement movement) {
|
||||
previousMoves.push(movement);
|
||||
super.move(movement);
|
||||
moveCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revertMove() {
|
||||
PawnMovement move = previousMoves.pop();
|
||||
setRow(row - getDirection());
|
||||
setCol(col - move.getMove());
|
||||
moveCount--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOriginalRow() {
|
||||
return originalRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOriginalCol() {
|
||||
return originalCol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMoveCount() {
|
||||
return moveCount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package laboratoire4.strategies;
|
||||
|
||||
import laboratoire4.Action;
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.game.Game;
|
||||
import laboratoire4.game.GameUtils;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
import laboratoire4.pawns.PawnUtils;
|
||||
import laboratoire4.pawns.SimulatedPawn;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class AttackStrategy extends MiniMaxStrategy {
|
||||
private static final int ATTACK_DISTANCE_FROM_GOAL = 4;
|
||||
|
||||
private long originalMinPushedCount;
|
||||
private long originalMinPusherCount;
|
||||
private long originalMaxPusherCount;
|
||||
|
||||
protected AttackStrategy(Game game) {
|
||||
super(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationResult getNextMove() {
|
||||
originalMinPushedCount = GameUtils.getMinPawnsAsStream(game.getBoard(), game.getPlayer()).filter(p -> !p.isPusher()).count();
|
||||
originalMinPusherCount = GameUtils.getMinPawnsAsStream(game.getBoard(), game.getPlayer()).filter(IPawn::isPusher).count();
|
||||
originalMaxPusherCount = GameUtils.getMaxPawnsAsStream(game.getBoard(), game.getPlayer())
|
||||
.filter(IPawn::isPusher)
|
||||
.count();
|
||||
|
||||
return super.getNextMove();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int evaluateSimulation() {
|
||||
int score = 0;
|
||||
|
||||
if (getMinPawns().stream().anyMatch(p -> p.getRow() == p.getPlayer().getGoal())) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
// region Pions max perdus
|
||||
Collection<SimulatedPawn> maxPawns = getMaxPawns();
|
||||
long pusherCount = maxPawns.stream().filter(IPawn::isPusher).count();
|
||||
score -= Math.pow((originalMaxPusherCount - pusherCount) * 10, 2);
|
||||
// endregion
|
||||
|
||||
// region Pions min capturés
|
||||
long minPusherCount = getMinPawns().stream().filter(IPawn::isPusher).count();
|
||||
long minPushedCount = getMinPawns().stream().filter(p -> !p.isPusher()).count();
|
||||
score += (originalMinPushedCount - minPushedCount) * 5;
|
||||
score += (originalMinPusherCount - minPusherCount) * 10;
|
||||
// endregion
|
||||
|
||||
for (SimulatedPawn pawn : maxPawns) {
|
||||
int distanceFromGoal = PawnUtils.distanceFromGoal(pawn);
|
||||
if (distanceFromGoal <= ATTACK_DISTANCE_FROM_GOAL) {
|
||||
score += ATTACK_DISTANCE_FROM_GOAL - distanceFromGoal + 1;
|
||||
|
||||
if (PawnUtils.hasEasyWinPath(game.getBoard(), pawn)) {
|
||||
score += Integer.MAX_VALUE / distanceFromGoal;
|
||||
}
|
||||
}
|
||||
|
||||
int row = pawn.getRow();
|
||||
int col = pawn.getCol();
|
||||
|
||||
if (row > 0 && row < 7) {
|
||||
IPawn facingPawn = game.getBoard()[row + pawn.getDirection()][col];
|
||||
if (facingPawn != null && !PawnUtils.areSamePlayers(pawn, facingPawn)) {
|
||||
// On bloque potentiellement un pion adverse, qui ne peut pas nous attaquer
|
||||
score += 10;
|
||||
}
|
||||
}
|
||||
|
||||
// if (col > 0) {
|
||||
// IPawn leftPawn = game.getBoard()[row][col - 1];
|
||||
// if (leftPawn != null && !PawnUtils.areSamePlayers(pawn, leftPawn)) {
|
||||
// score += 5;
|
||||
// }
|
||||
// }
|
||||
// if (col < 7) {
|
||||
// IPawn rightPawn = game.getBoard()[row][col + 1];
|
||||
// if (rightPawn != null && !PawnUtils.areSamePlayers(pawn, rightPawn)) {
|
||||
// score += 5;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
Collection<IPawn> maxPawns = GameUtils.getMaxPawns(game.getBoard(), game.getPlayer());
|
||||
|
||||
int maxWeight = 0;
|
||||
for (IPawn pawn : maxPawns) {
|
||||
int weight = 0;
|
||||
|
||||
int distanceFromGoal = PawnUtils.distanceFromGoal(pawn);
|
||||
if (distanceFromGoal <= 1) {
|
||||
return WEIGHT_MAX;
|
||||
}
|
||||
|
||||
if (distanceFromGoal == 2) {
|
||||
if (PawnUtils.hasEasyWinPath(game.getBoard(), pawn)) {
|
||||
weight = 8;
|
||||
} else {
|
||||
weight = 5;
|
||||
}
|
||||
}
|
||||
|
||||
if (distanceFromGoal > 2 && distanceFromGoal <= ATTACK_DISTANCE_FROM_GOAL) {
|
||||
weight = 2;
|
||||
}
|
||||
|
||||
if (weight > maxWeight) {
|
||||
maxWeight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
return maxWeight;
|
||||
}
|
||||
|
||||
private boolean cantBeCaptured(Action<IPawn> action) {
|
||||
IPawn pawn = action.getPawn();
|
||||
PawnMovement movement = action.getMovement();
|
||||
|
||||
int row = pawn.getRow() + pawn.getDirection();
|
||||
int col = pawn.getCol() + movement.getMove();
|
||||
|
||||
return !PawnUtils.canBeCaptured(game, row, col, pawn.getPlayer());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
package laboratoire4.strategies;
|
||||
|
||||
import laboratoire4.Action;
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.game.Game;
|
||||
import laboratoire4.game.GameUtils;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
import laboratoire4.pawns.PawnUtils;
|
||||
import laboratoire4.pawns.SimulatedPawn;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class DefenseStrategy extends MiniMaxStrategy {
|
||||
private static final int DEFENSE_DISTANCE_FROM_HOME = 2;
|
||||
private static final int PAWN_DEFENSE_DISTANCE = 3;
|
||||
|
||||
private final Map<IPawn, Integer> dangerousPawns;
|
||||
|
||||
public DefenseStrategy(Game game) {
|
||||
super(game);
|
||||
|
||||
dangerousPawns = getDangerousPawns(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationResult getNextMove() {
|
||||
if (dangerousPawns.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return super.getNextMove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
if (dangerousPawns.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Collection<IPawn> minPawns = GameUtils.getMinPawns(game.getBoard(), game.getPlayer());
|
||||
Collection<IPawn> maxPawns = GameUtils.getMaxPawns(game.getBoard(), game.getPlayer());
|
||||
int maxWeight = 0;
|
||||
|
||||
for (IPawn pawn : minPawns) {
|
||||
int weight = 0;
|
||||
|
||||
int distanceFromGoal = PawnUtils.distanceFromGoal(pawn);
|
||||
if (distanceFromGoal <= 2) {
|
||||
if (PawnUtils.hasEasyWinPath(game.getBoard(), pawn)) {
|
||||
if (maxPawns.stream().noneMatch(p -> PawnUtils.canCapture(game, p, pawn))) {
|
||||
// Le pion ne peut pas être arrêté, on doit attaquer !
|
||||
return 0;
|
||||
} else {
|
||||
return WEIGHT_MAX;
|
||||
}
|
||||
} else {
|
||||
weight = 5;
|
||||
}
|
||||
}
|
||||
|
||||
if (distanceFromGoal > 2 && distanceFromGoal <= DEFENSE_DISTANCE_FROM_HOME) {
|
||||
weight = 2;
|
||||
}
|
||||
|
||||
if (weight > maxWeight) {
|
||||
maxWeight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
return maxWeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int evaluateSimulation() {
|
||||
int score = 0;
|
||||
|
||||
Collection<SimulatedPawn> maxPawns = getMaxPawns();
|
||||
Collection<SimulatedPawn> minPawns = getMinPawns();
|
||||
|
||||
for (SimulatedPawn pawn : minPawns) {
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
if (PawnUtils.canBeCaptured(game, pawn.getRow() + pawn.getDirection(), pawn.getCol() + movement.getMove(), pawn.getPlayer())) {
|
||||
// Ce pion ennemi est aussi en danger s'il bouge !
|
||||
score += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IPawn dangerousPawn : dangerousPawns.keySet()) {
|
||||
Optional<SimulatedPawn> simulated = GameUtils.getMinPawnsAsStream(game.getBoard(), game.getPlayer())
|
||||
.filter(p -> p.getOriginalRow() == dangerousPawn.getRow() && p.getOriginalCol() == dangerousPawn.getCol())
|
||||
.findFirst();
|
||||
|
||||
if (!simulated.isPresent()) {
|
||||
// Le pion a été capturé !
|
||||
score += Math.pow(dangerousPawns.get(dangerousPawn), 3);
|
||||
} else {
|
||||
// On est toujours en danger
|
||||
int distance = PawnUtils.distanceFromGoal(simulated.get());
|
||||
score -= Math.pow(DEFENSE_DISTANCE_FROM_HOME - distance + 1, 3);
|
||||
|
||||
for (SimulatedPawn pawn : maxPawns) {
|
||||
if (PawnUtils.distanceFromHome(pawn) <= DEFENSE_DISTANCE_FROM_HOME) { // On ne veut pas que nos attaquants s'approchent des pions ennemis
|
||||
score += PAWN_DEFENSE_DISTANCE - distanceFromCapture(pawn, simulated.get()) * 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (SimulatedPawn pawn : maxPawns) {
|
||||
if (PawnUtils.canBeCaptured(game, pawn)) {
|
||||
score -= 5;
|
||||
}
|
||||
|
||||
// On préfère les groupes de 2 pushers, puisqu'ils ont une meilleure surface de défense
|
||||
if (!pawn.isPusher()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int col = pawn.getCol();
|
||||
int row = pawn.getRow();
|
||||
|
||||
if (col > 0 && PawnUtils.areSamePlayers(pawn, game.getBoard()[row][col - 1])) {
|
||||
score += 5;
|
||||
}
|
||||
if (col < 7 && PawnUtils.areSamePlayers(pawn, game.getBoard()[row][col + 1])) {
|
||||
score += 5;
|
||||
}
|
||||
if (row > 0 && PawnUtils.areSamePlayers(pawn, game.getBoard()[row - 1][col])) {
|
||||
score += 5;
|
||||
}
|
||||
if (row < 7 && PawnUtils.areSamePlayers(pawn, game.getBoard()[row + 1][col])) {
|
||||
score += 5;
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private int distanceFromCapture(IPawn max, IPawn min) {
|
||||
int rowDistance = Math.abs(min.getRow() - max.getRow());
|
||||
int colDistance = Math.abs(min.getCol() - max.getCol());
|
||||
|
||||
if (colDistance > rowDistance) {
|
||||
// Le pion ne pourra jamais être atteint
|
||||
return PAWN_DEFENSE_DISTANCE;
|
||||
}
|
||||
|
||||
if (!PawnUtils.canCapture(game, max, min, max.getRow(), max.getCol(), 0, rowDistance)) {
|
||||
return PAWN_DEFENSE_DISTANCE;
|
||||
}
|
||||
|
||||
return rowDistance;
|
||||
}
|
||||
|
||||
private static Map<IPawn, Integer> getDangerousPawns(Game game) {
|
||||
Map<IPawn, Integer> dangerousPawns = new HashMap<>();
|
||||
|
||||
for (IPawn pawn : GameUtils.getMinPawns(game.getBoard(), game.getPlayer())) {
|
||||
int distance = PawnUtils.distanceFromGoal(pawn);
|
||||
|
||||
if (distance <= DEFENSE_DISTANCE_FROM_HOME) {
|
||||
// Les pions proches de nous qui ne peuvent pas bouger peuvent être ignorés
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
if (pawn.isMoveValid(game.getBoard(), movement)) {
|
||||
dangerousPawns.put(pawn, DEFENSE_DISTANCE_FROM_HOME - distance + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dangerousPawns;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package laboratoire4.strategies;
|
||||
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
|
||||
public class EvaluationResult {
|
||||
private final int row;
|
||||
private final int col;
|
||||
private final PawnMovement movement;
|
||||
|
||||
public EvaluationResult(int row, int col, PawnMovement movement) {
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
this.movement = movement;
|
||||
}
|
||||
|
||||
public int getRow() {
|
||||
return row;
|
||||
}
|
||||
|
||||
public int getCol() {
|
||||
return col;
|
||||
}
|
||||
|
||||
public PawnMovement getMovement() {
|
||||
return movement;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package laboratoire4.strategies;
|
||||
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.game.Game;
|
||||
import laboratoire4.game.GameUtils;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
import laboratoire4.pawns.PawnUtils;
|
||||
|
||||
public class ImmediateDefenseStrategy implements Strategy {
|
||||
private static final int IMM_DEFENSE_DISTANCE_FROM_HOME = 2;
|
||||
|
||||
private final Game game;
|
||||
|
||||
public ImmediateDefenseStrategy(Game game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationResult getNextMove() {
|
||||
// On n'utilise pas la méthode utilitaire, car on veut bouger la ligne de défense si nécessaire
|
||||
for (IPawn pawn : GameUtils.getMaxPawns(game.getBoard(), game.getPlayer())) {
|
||||
if (PawnUtils.distanceFromHome(pawn) > IMM_DEFENSE_DISTANCE_FROM_HOME) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
if (pawn.isMoveValid(game.getBoard(), movement)) {
|
||||
int nextRow = pawn.getRow() + pawn.getDirection();
|
||||
int nextCol = pawn.getCol() + movement.getMove();
|
||||
IPawn nextPawn = game.getBoard()[nextRow][nextCol];
|
||||
|
||||
if (nextPawn == null || PawnUtils.areSamePlayers(pawn, nextPawn)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return new EvaluationResult(pawn.getRow(), pawn.getCol(), movement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
for (IPawn pawn : GameUtils.getMaxPawns(game.getBoard(), game.getPlayer())) {
|
||||
if (PawnUtils.distanceFromHome(pawn) > IMM_DEFENSE_DISTANCE_FROM_HOME) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
if (pawn.isMoveValid(game.getBoard(), movement)) {
|
||||
int nextRow = pawn.getRow() + pawn.getDirection();
|
||||
int nextCol = pawn.getCol() + movement.getMove();
|
||||
IPawn nextPawn = game.getBoard()[nextRow][nextCol];
|
||||
|
||||
if (nextPawn == null || PawnUtils.areSamePlayers(pawn, nextPawn)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return WEIGHT_MAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package laboratoire4.strategies;
|
||||
|
||||
import laboratoire4.game.Game;
|
||||
|
||||
public class MasterStrategy implements Strategy {
|
||||
private static Strategy instance;
|
||||
|
||||
public static Strategy getInstance(Game game) {
|
||||
if (instance == null) {
|
||||
instance = new MasterStrategy(game);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final Game game;
|
||||
|
||||
private MasterStrategy(Game game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationResult getNextMove() {
|
||||
long startMs = System.currentTimeMillis();
|
||||
|
||||
Strategy strategy = getBestStrategy();
|
||||
System.out.println(strategy.getClass().getSimpleName());
|
||||
|
||||
EvaluationResult result = strategy.getNextMove();
|
||||
|
||||
long endMs = System.currentTimeMillis();
|
||||
System.out.printf("Temps: %d ms\n", endMs - startMs);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Strategy getBestStrategy() {
|
||||
Strategy[] strategies = new Strategy[]{
|
||||
new ImmediateDefenseStrategy(game),
|
||||
new WinningStrategy(game),
|
||||
new DefenseStrategy(game),
|
||||
new AttackStrategy(game)
|
||||
};
|
||||
|
||||
int maxWeight = 0;
|
||||
Strategy bestStrategy = null;
|
||||
|
||||
for (Strategy strategy : strategies) {
|
||||
int weight = strategy.getWeight();
|
||||
if (weight == WEIGHT_MAX) {
|
||||
return strategy;
|
||||
}
|
||||
|
||||
if (weight > maxWeight) {
|
||||
maxWeight = weight;
|
||||
bestStrategy = strategy;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxWeight > 0) {
|
||||
return bestStrategy;
|
||||
}
|
||||
|
||||
return new RandomStrategy(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
return WEIGHT_MAX;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
package laboratoire4.strategies;
|
||||
|
||||
import laboratoire4.Action;
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.game.Game;
|
||||
import laboratoire4.game.GameUtils;
|
||||
import laboratoire4.game.SimulatedGame;
|
||||
import laboratoire4.pawns.Pawn;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
import laboratoire4.pawns.PawnUtils;
|
||||
import laboratoire4.pawns.SimulatedPawn;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class MiniMaxStrategy implements Strategy {
|
||||
private static final int MAX_DEPTH = 6;
|
||||
private static final long MAX_TIME_MS = 4800; // Moins de 5 secondes, car le tour n'est pas complètement fini
|
||||
|
||||
protected SimulatedGame game;
|
||||
protected long startTimeMs;
|
||||
|
||||
protected MiniMaxStrategy(Game game) {
|
||||
this.game = new SimulatedGame(game.getBoard(), game.getPlayer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationResult getNextMove() {
|
||||
return miniMax();
|
||||
}
|
||||
|
||||
private EvaluationResult miniMax() {
|
||||
EvaluationResult maxResult = null;
|
||||
int maxScore = Integer.MIN_VALUE;
|
||||
startTimeMs = System.currentTimeMillis();
|
||||
|
||||
for (Action<IPawn> action : getActions(true)) {
|
||||
int score = min(0, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
|
||||
if (maxResult == null || score > maxScore) {
|
||||
IPawn pawn = action.getPawn();
|
||||
maxResult = new EvaluationResult(pawn.getRow(), pawn.getCol(), action.getMovement());
|
||||
}
|
||||
}
|
||||
|
||||
return maxResult;
|
||||
}
|
||||
|
||||
private int max(int depth, int alpha, int beta) {
|
||||
if (depth >= MAX_DEPTH) {
|
||||
return evaluate();
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - startTimeMs > MAX_TIME_MS) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
int maxScore = Integer.MIN_VALUE;
|
||||
|
||||
for (Action<IPawn> action : getActions(true)) {
|
||||
IPawn pawn = action.getPawn();
|
||||
PawnMovement movement = action.getMovement();
|
||||
|
||||
game.move(pawn, movement);
|
||||
int score = min(depth + 1, Math.max(alpha, maxScore), beta);
|
||||
game.revertMove();
|
||||
|
||||
if (score > maxScore) {
|
||||
maxScore = score;
|
||||
}
|
||||
|
||||
if (maxScore >= beta) {
|
||||
return maxScore;
|
||||
}
|
||||
}
|
||||
|
||||
return maxScore;
|
||||
}
|
||||
|
||||
private int min(int depth, int alpha, int beta) {
|
||||
if (depth >= MAX_DEPTH) {
|
||||
return evaluate();
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - startTimeMs > MAX_TIME_MS) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
int minScore = Integer.MAX_VALUE;
|
||||
|
||||
for (Action<IPawn> action : getActions(false)) {
|
||||
IPawn pawn = action.getPawn();
|
||||
PawnMovement movement = action.getMovement();
|
||||
|
||||
game.move(pawn, movement);
|
||||
int score = max(depth + 1, alpha, Math.min(beta, minScore));
|
||||
game.revertMove();
|
||||
|
||||
if (score < minScore) {
|
||||
minScore = score;
|
||||
}
|
||||
|
||||
if (minScore <= alpha) {
|
||||
return minScore;
|
||||
}
|
||||
}
|
||||
|
||||
return minScore;
|
||||
}
|
||||
|
||||
protected Collection<SimulatedPawn> getMaxPawns() {
|
||||
return GameUtils.getMaxPawns(game.getBoard(), game.getPlayer());
|
||||
}
|
||||
|
||||
protected Collection<SimulatedPawn> getMinPawns() {
|
||||
return GameUtils.getMinPawns(game.getBoard(), game.getPlayer());
|
||||
}
|
||||
|
||||
protected Collection<Action<IPawn>> getActions(boolean max) {
|
||||
return PawnUtils.getActions(game, max);
|
||||
}
|
||||
|
||||
private int evaluate() {
|
||||
int score = evaluateSimulation();
|
||||
|
||||
Collection<SimulatedPawn> maxPawns = getMaxPawns();
|
||||
|
||||
if (maxPawns.stream().noneMatch(IPawn::isPusher)) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
if (getMinPawns().stream().anyMatch(p -> p.getRow() == p.getPlayer().getGoal())) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
for (IPawn pawn : maxPawns) {
|
||||
if (pawn.getRow() == pawn.getPlayer().getGoal()) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
if (PawnUtils.canBeCaptured(game, pawn)) {
|
||||
// On peut être capturé
|
||||
score -= 10;
|
||||
}
|
||||
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
if (pawn.isMoveValid(game.getBoard(), movement) && !PawnUtils.canBeCaptured(game, pawn.getRow() + pawn.getDirection(), pawn.getCol() + movement.getMove(), pawn.getPlayer())) {
|
||||
// On ne peut pas être capturé en faisant ce mouvement
|
||||
score += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Lignes ennemies
|
||||
for (IPawn pawn : getMinPawns()) {
|
||||
int row = pawn.getRow();
|
||||
int col = pawn.getCol();
|
||||
|
||||
if (row > 0 && PawnUtils.areSamePlayers(pawn, game.getBoard()[row - 1][col])) {
|
||||
score -= 5;
|
||||
}
|
||||
if (row < 0 && PawnUtils.areSamePlayers(pawn, game.getBoard()[row + 1][col])) {
|
||||
score -= 5;
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
protected abstract int evaluateSimulation();
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package laboratoire4.strategies;
|
||||
|
||||
import laboratoire4.Action;
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.game.Game;
|
||||
import laboratoire4.game.GameUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Au début du jeu, il n'y a pas d'attaques, de défense ou de possibilités de gagner (pas besoin de minimax).
|
||||
* Alors, on avance quelques pions (pas tous) aléatoirement.
|
||||
*/
|
||||
public class RandomStrategy implements Strategy {
|
||||
private static final int PAWN_TO_MOVE = 2;
|
||||
|
||||
private final Random random = new Random();
|
||||
private final Game game;
|
||||
|
||||
public RandomStrategy(Game game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationResult getNextMove() {
|
||||
Collection<IPawn> outsideHomePushers = GameUtils.getMaxPawnsAsStream(game.getBoard(), game.getPlayer())
|
||||
.filter(IPawn::isPusher)
|
||||
.filter(p -> p.getRow() != p.getPlayer().getHome())
|
||||
.filter(p -> p.getRow() != p.getPlayer().getHome() + p.getDirection())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (outsideHomePushers.size() >= PAWN_TO_MOVE) {
|
||||
List<Action<IPawn>> validActions = Strategy.getValidActions(game.getBoard(), outsideHomePushers);
|
||||
if (!validActions.isEmpty()) {
|
||||
return getRandomMove(validActions);
|
||||
}
|
||||
}
|
||||
|
||||
List<Action<IPawn>> validActions = Strategy.getValidActions(game);
|
||||
return getRandomMove(validActions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private EvaluationResult getRandomMove(List<Action<IPawn>> actions) {
|
||||
int randomIndex = random.nextInt(actions.size());
|
||||
Action<IPawn> nextAction = actions.get(randomIndex);
|
||||
IPawn nextPawn = nextAction.getPawn();
|
||||
|
||||
return new EvaluationResult(nextPawn.getRow(), nextPawn.getCol(), nextAction.getMovement());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package laboratoire4.strategies;
|
||||
|
||||
import laboratoire4.Action;
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.game.Game;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public interface Strategy {
|
||||
int WEIGHT_MAX = 10;
|
||||
|
||||
EvaluationResult getNextMove();
|
||||
|
||||
int getWeight();
|
||||
|
||||
static List<Action<IPawn>> getValidActions(Game game) {
|
||||
List<IPawn> maxPawns = Arrays.stream(game.getBoard())
|
||||
.flatMap(Arrays::stream)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(p -> p.getPlayer() == game.getPlayer())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return getValidActions(game.getBoard(), maxPawns);
|
||||
}
|
||||
|
||||
static List<Action<IPawn>> getValidActions(IPawn[][] board, Collection<IPawn> pawns) {
|
||||
return getValidActions(board, pawns, true);
|
||||
}
|
||||
|
||||
static List<Action<IPawn>> getValidActions(IPawn[][] board, Collection<IPawn> pawns, boolean excludeDefense) {
|
||||
List<Action<IPawn>> validActions = new ArrayList<>();
|
||||
|
||||
for (IPawn pawn : pawns) {
|
||||
if (excludeDefense && pawn.getRow() == pawn.getPlayer().getHome()) {
|
||||
int col = pawn.getCol();
|
||||
|
||||
// Si possible, on ne bouge pas ces pushers, comme ça on a une défense d'urgence totale
|
||||
if (col == 1 || col == 2 || col == 5 || col == 6) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
if (pawn.isMoveValid(board, movement)) {
|
||||
validActions.add(new Action<>(pawn, movement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (excludeDefense && validActions.isEmpty()) {
|
||||
return getValidActions(board, pawns, false);
|
||||
}
|
||||
|
||||
return validActions;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package laboratoire4.strategies;
|
||||
|
||||
import laboratoire4.Action;
|
||||
import laboratoire4.IPawn;
|
||||
import laboratoire4.game.Game;
|
||||
import laboratoire4.game.GameUtils;
|
||||
import laboratoire4.pawns.PawnMovement;
|
||||
import laboratoire4.pawns.PawnUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WinningStrategy implements Strategy {
|
||||
private Action<IPawn> winningAction;
|
||||
|
||||
public WinningStrategy(Game game) {
|
||||
winningAction = findImmediateWinningAction(game);
|
||||
if (winningAction == null) {
|
||||
// On cherche un chemin où on peut gagner quoi qu'il arrive
|
||||
winningAction = findWinningPath(game);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationResult getNextMove() {
|
||||
if (winningAction == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IPawn winningPawn = winningAction.getPawn();
|
||||
return new EvaluationResult(winningPawn.getRow(), winningPawn.getCol(), winningAction.getMovement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
if (winningAction != null) {
|
||||
return WEIGHT_MAX;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static Action<IPawn> findImmediateWinningAction(Game game) {
|
||||
Collection<IPawn> nearPawns = GameUtils.getMaxPawnsAsStream(game.getBoard(), game.getPlayer())
|
||||
.filter(p -> p.getRow() + p.getDirection() == p.getPlayer().getGoal())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (IPawn pawn : nearPawns) {
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
if (pawn.isMoveValid(game.getBoard(), movement)) {
|
||||
return new Action<>(pawn, movement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Action<IPawn> findWinningPath(Game game) {
|
||||
for (IPawn pawn : GameUtils.getMaxPawns(game.getBoard(), game.getPlayer())) {
|
||||
if (PawnUtils.distanceFromGoal(pawn) > 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
int nextRow = pawn.getRow() + pawn.getDirection();
|
||||
int nextCol = pawn.getCol() + movement.getMove();
|
||||
|
||||
if (pawn.isMoveValid(game.getBoard(), movement) &&
|
||||
!PawnUtils.canBeCaptured(game, nextRow, nextCol, game.getPlayer()) &&
|
||||
hasWinningPath(game, pawn, nextRow, nextCol)) {
|
||||
return new Action<>(pawn, movement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean hasWinningPath(Game game, IPawn initialPawn, int row, int col) {
|
||||
if (row == initialPawn.getPlayer().getGoal()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (PawnMovement movement : PawnMovement.values()) {
|
||||
int nextRow = row + initialPawn.getDirection();
|
||||
int nextCol = col + movement.getMove();
|
||||
|
||||
if (!initialPawn.isMoveValid(game.getBoard(), movement, row, col)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IPawn nextPawn = game.getBoard()[nextRow][nextCol];
|
||||
if (nextPawn == null && hasWinningPath(game, initialPawn, nextRow, nextCol)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nextPawn != null && !PawnUtils.areSamePlayers(initialPawn, nextPawn) &&
|
||||
!PawnUtils.canBeCaptured(game, row, col, initialPawn.getPlayer()) &&
|
||||
hasWinningPath(game, initialPawn, nextRow, nextCol)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue