Beaucoup de changements
This commit is contained in:
parent
5f7d74e0ae
commit
648c325649
|
@ -4,15 +4,36 @@
|
||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="41395b4b-3252-492c-a869-5f4dab107186" name="Changes" comment="Fixes?">
|
<list default="true" id="41395b4b-3252-492c-a869-5f4dab107186" name="Changes" comment="Plus d'heuristiques">
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Action.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/game/Game.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/game/GameUtils.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/PawnMovement.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/PawnUtils.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/AttackStrategy.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/DefenseStrategy.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/EvaluationResult.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/MasterStrategy.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/StartingStrategy.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/Strategy.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/strategies/WinningStrategy.java" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.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/BoardEvaluator.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/BoardEvaluator.java" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/BoardEvaluator.java" beforeDir="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/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/GameTree.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/GameTree.java" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingBoard.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingBoard.java" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/IPawn.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/IPawn.java" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPawn.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPawn.java" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MiniMax.java" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPushed.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPushed.java" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MiniMaxResult.java" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPusher.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPusher.java" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingBoard.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/game/SimulatedGame.java" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPawn.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/SimulatedPawn.java" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPushed.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/SimulatedPushed.java" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPusher.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/SimulatedPusher.java" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Pawn.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/Pawn.java" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Pushed.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/Pushed.java" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Pusher.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/pawns/Pusher.java" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/PusherBoard.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/game/PusherGame.java" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<list id="98b8a79f-2f53-42bf-96da-7af322697a0d" name="Changes by acastonguay" comment="" />
|
<list id="98b8a79f-2f53-42bf-96da-7af322697a0d" name="Changes by acastonguay" comment="" />
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
@ -74,7 +95,7 @@
|
||||||
<module name="Lab4" />
|
<module name="Lab4" />
|
||||||
<extension name="coverage">
|
<extension name="coverage">
|
||||||
<pattern>
|
<pattern>
|
||||||
<option name="PATTERN" value="laboratoire4.*" />
|
<option name="PATTERN" value="laboratoire4.pawns.*" />
|
||||||
<option name="ENABLED" value="true" />
|
<option name="ENABLED" value="true" />
|
||||||
</pattern>
|
</pattern>
|
||||||
</extension>
|
</extension>
|
||||||
|
@ -87,7 +108,7 @@
|
||||||
<module name="Lab4" />
|
<module name="Lab4" />
|
||||||
<extension name="coverage">
|
<extension name="coverage">
|
||||||
<pattern>
|
<pattern>
|
||||||
<option name="PATTERN" value="laboratoire4.*" />
|
<option name="PATTERN" value="laboratoire4.pawns.*" />
|
||||||
<option name="ENABLED" value="true" />
|
<option name="ENABLED" value="true" />
|
||||||
</pattern>
|
</pattern>
|
||||||
</extension>
|
</extension>
|
||||||
|
@ -100,7 +121,7 @@
|
||||||
<module name="Lab4" />
|
<module name="Lab4" />
|
||||||
<extension name="coverage">
|
<extension name="coverage">
|
||||||
<pattern>
|
<pattern>
|
||||||
<option name="PATTERN" value="laboratoire4.*" />
|
<option name="PATTERN" value="laboratoire4.pawns.*" />
|
||||||
<option name="ENABLED" value="true" />
|
<option name="ENABLED" value="true" />
|
||||||
</pattern>
|
</pattern>
|
||||||
</extension>
|
</extension>
|
||||||
|
@ -135,6 +156,10 @@
|
||||||
<workItem from="1679779362814" duration="5912000" />
|
<workItem from="1679779362814" duration="5912000" />
|
||||||
<workItem from="1679787823160" duration="2189000" />
|
<workItem from="1679787823160" duration="2189000" />
|
||||||
<workItem from="1680198283785" duration="8256000" />
|
<workItem from="1680198283785" duration="8256000" />
|
||||||
|
<workItem from="1680580123067" duration="326000" />
|
||||||
|
<workItem from="1680750346503" duration="2118000" />
|
||||||
|
<workItem from="1680821970713" duration="10363000" />
|
||||||
|
<workItem from="1680914341116" duration="16558000" />
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="MiniMax">
|
<task id="LOCAL-00001" summary="MiniMax">
|
||||||
<created>1679263366439</created>
|
<created>1679263366439</created>
|
||||||
|
@ -171,7 +196,14 @@
|
||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1679786894920</updated>
|
<updated>1679786894920</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="6" />
|
<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>
|
||||||
|
<option name="localTasksCounter" value="7" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
@ -182,7 +214,8 @@
|
||||||
<MESSAGE value="Movement logic" />
|
<MESSAGE value="Movement logic" />
|
||||||
<MESSAGE value="Small fixes" />
|
<MESSAGE value="Small fixes" />
|
||||||
<MESSAGE value="Fixes?" />
|
<MESSAGE value="Fixes?" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Fixes?" />
|
<MESSAGE value="Plus d'heuristiques" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="Plus d'heuristiques" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XDebuggerManager">
|
<component name="XDebuggerManager">
|
||||||
<breakpoint-manager>
|
<breakpoint-manager>
|
||||||
|
|
|
@ -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,230 +0,0 @@
|
||||||
package laboratoire4;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public class BoardEvaluator {
|
|
||||||
public static int evaluate(MovingBoard game) {
|
|
||||||
Collection<MovingPawn> maxPawns = game.getMaxPawns();
|
|
||||||
Collection<MovingPawn> minPawns = game.getMinPawns();
|
|
||||||
|
|
||||||
return evaluatePlayer(game, maxPawns, minPawns, true) - evaluatePlayer(game, minPawns, maxPawns, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
//region Full Board
|
|
||||||
private static int evaluatePlayer(MovingBoard game, Collection<MovingPawn> maxPawns, Collection<MovingPawn> minPawns, boolean max) {
|
|
||||||
if (minPawns.stream().noneMatch(IPawn::isPusher)) {
|
|
||||||
return 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
int score = evaluateMaxPawnCountScore(maxPawns, minPawns);
|
|
||||||
|
|
||||||
for (MovingPawn pawn : maxPawns) {
|
|
||||||
score += evaluatePawn(game, pawn);
|
|
||||||
|
|
||||||
if (max) {
|
|
||||||
score += strategy(game, pawn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int evaluateMaxPawnCountScore(Collection<MovingPawn> maxPawns, Collection<MovingPawn> minPawns) {
|
|
||||||
return evaluatePawnCountScore(minPawns) - evaluatePawnCountScore(maxPawns);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Augmente beaucoup le score à partir de 10 pions capturés
|
|
||||||
// f(x) = 0.001x^4, f(1) = 0, f(10) = 10, f(16) = 65.54
|
|
||||||
private static int evaluatePawnCountScore(Collection<MovingPawn> pawns) {
|
|
||||||
int capturedPusherCount = (int) (8 - pawns.stream().filter(IPawn::isPusher).count());
|
|
||||||
return (int) (Math.pow(capturedPusherCount, 2));
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Board
|
|
||||||
private static int evaluatePawn(MovingBoard game, MovingPawn pawn) {
|
|
||||||
if (pawn.getRow() == pawn.getPlayer().getGoal()) {
|
|
||||||
return 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Favorise les pièces qui peuvent gagner au prochain tour
|
|
||||||
if (pawn.getRow() + pawn.getDirection() == pawn.getPlayer().getGoal()) {
|
|
||||||
return 25;
|
|
||||||
}
|
|
||||||
|
|
||||||
int score = 0;
|
|
||||||
|
|
||||||
score += evaluateHomePawnScore(pawn);
|
|
||||||
// score += evaluatePushedScore(pawn);
|
|
||||||
score += evaluatePusherScore(game, pawn);
|
|
||||||
score += evaluateValidMovesScore(game, pawn);
|
|
||||||
score += evaluateAttackScore(game, pawn);
|
|
||||||
score += evaluateDefenseScore(game, pawn);
|
|
||||||
|
|
||||||
if (Math.abs(pawn.getPlayer().getGoal() - pawn.getRow()) < 4) {
|
|
||||||
score += hasEasyWinPath(game, pawn, pawn.getRow(), pawn.getCol(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int evaluateHomePawnScore(MovingPawn pawn) {
|
|
||||||
return pawn.getRow() == pawn.getPlayer().getHome() ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int evaluatePushedScore(MovingPawn pawn) {
|
|
||||||
return pawn.isPusher() ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int evaluatePusherScore(MovingBoard board, MovingPawn pawn) {
|
|
||||||
if (!pawn.isPusher()) return 0;
|
|
||||||
|
|
||||||
int score = 0;
|
|
||||||
for (Pawn.PawnMovement movement : Pawn.PawnMovement.values()) {
|
|
||||||
MovingPawn nearPawn = board.getNextPawn(pawn, movement);
|
|
||||||
if (MovingPawn.areSamePlayers(pawn, nearPawn) && !nearPawn.isPusher()) {
|
|
||||||
score++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int evaluateValidMovesScore(MovingBoard game, MovingPawn pawn) {
|
|
||||||
int score = 0;
|
|
||||||
for (Pawn.PawnMovement movement : Pawn.PawnMovement.values()) {
|
|
||||||
if (pawn.isMoveValid(game.getBoard(), movement)) {
|
|
||||||
score++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return score * pawn.getMoveCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int evaluateAttackScore(MovingBoard game, MovingPawn pawn) {
|
|
||||||
int score = 0;
|
|
||||||
|
|
||||||
Pawn.PawnMovement[] validAttackMoves = new Pawn.PawnMovement[]{Pawn.PawnMovement.LEFT_DIAGONAL, Pawn.PawnMovement.RIGHT_DIAGONAL};
|
|
||||||
for (Pawn.PawnMovement movement : validAttackMoves) {
|
|
||||||
MovingPawn nearPawn = game.getNextPawn(pawn, movement);
|
|
||||||
if (nearPawn != null && pawn.isMoveValid(game.getBoard(), movement) && !MovingPawn.areSamePlayers(pawn, nearPawn)) {
|
|
||||||
score++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Favorise les attaques de notre côté
|
|
||||||
if ((pawn.getPlayer() == Player.RED && pawn.getRow() < 4) ||
|
|
||||||
(pawn.getPlayer() == Player.BLACK && pawn.getRow() >= 4)) {
|
|
||||||
score++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int evaluateDefenseScore(MovingBoard game, MovingPawn pawn) {
|
|
||||||
int score = 0;
|
|
||||||
|
|
||||||
Pawn.PawnMovement[] validAttackMoves = new Pawn.PawnMovement[]{Pawn.PawnMovement.LEFT_DIAGONAL, Pawn.PawnMovement.RIGHT_DIAGONAL};
|
|
||||||
for (Pawn.PawnMovement movement : validAttackMoves) {
|
|
||||||
MovingPawn nearPawn = game.getNextPawn(pawn, movement);
|
|
||||||
Pawn.PawnMovement oppositeMovement = Pawn.PawnMovement.from(movement.getMove() * -1);
|
|
||||||
|
|
||||||
if (nearPawn != null && !MovingPawn.areSamePlayers(pawn, nearPawn) && nearPawn.isMoveValid(game.getBoard(), oppositeMovement)) {
|
|
||||||
score -= 50;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int hasEasyWinPath(MovingBoard game, MovingPawn pawn, int row, int col, int depth) {
|
|
||||||
if (row == pawn.getPlayer().getGoal()) {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
int score = 0;
|
|
||||||
int nextRow = row + pawn.getDirection();
|
|
||||||
|
|
||||||
Pawn.PawnMovement[] preferredMoves = new Pawn.PawnMovement[]{Pawn.PawnMovement.LEFT_DIAGONAL, Pawn.PawnMovement.RIGHT_DIAGONAL};
|
|
||||||
for (Pawn.PawnMovement movement : preferredMoves) {
|
|
||||||
int nextCol = col + movement.getMove();
|
|
||||||
if (nextCol < 0 || nextCol > 7) continue;
|
|
||||||
|
|
||||||
MovingPawn nearPawn = game.getBoard()[nextRow][nextCol];
|
|
||||||
|
|
||||||
if (nearPawn == null) {
|
|
||||||
score += 2;
|
|
||||||
} else if (nearPawn.getPlayer() != pawn.getPlayer()) {
|
|
||||||
score++;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depth < 3) {
|
|
||||||
score += hasEasyWinPath(game, pawn, nextRow, pawn.getCol() + movement.getMove(), depth + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Strategy
|
|
||||||
private static int strategy(MovingBoard game, MovingPawn pawn) {
|
|
||||||
int score = 0;
|
|
||||||
|
|
||||||
score += defensiveStrategy(game, pawn);
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int defensiveStrategy(MovingBoard game, MovingPawn pawn) {
|
|
||||||
int col = pawn.getCol();
|
|
||||||
int row = pawn.getRow();
|
|
||||||
|
|
||||||
if (col < 2 || col > 5) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
MovingPawn leftPawn = game.getBoard()[row][col - 2];
|
|
||||||
MovingPawn rightPawn = game.getBoard()[row][col - 2];
|
|
||||||
int score = 0;
|
|
||||||
|
|
||||||
if (MovingPawn.areSamePlayers(leftPawn, pawn)) {
|
|
||||||
score++;
|
|
||||||
}
|
|
||||||
if (MovingPawn.areSamePlayers(rightPawn, pawn)) {
|
|
||||||
score++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasEnemyNear(game, pawn)) {
|
|
||||||
score += 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hasEnemyNear(MovingBoard game, MovingPawn pawn) {
|
|
||||||
int row = pawn.getRow();
|
|
||||||
int col = pawn.getCol();
|
|
||||||
|
|
||||||
for (int i = 1; i <= 2; i++) {
|
|
||||||
int nextRow = row + i * pawn.getDirection();
|
|
||||||
if (nextRow < 0 || nextRow > 7) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = -1; j <= 1; j++) {
|
|
||||||
int nextCol = col + j;
|
|
||||||
if (nextCol < 0 || nextCol > 7) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
MovingPawn nearPawn = game.getBoard()[nextRow][nextCol];
|
|
||||||
if (nearPawn != null && !MovingPawn.areSamePlayers(pawn, nearPawn)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
}
|
|
|
@ -1,5 +1,7 @@
|
||||||
package laboratoire4;
|
package laboratoire4;
|
||||||
|
|
||||||
|
import laboratoire4.game.PusherGame;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -9,7 +11,7 @@ public class Client {
|
||||||
private final Socket socket;
|
private final Socket socket;
|
||||||
private final BufferedInputStream input;
|
private final BufferedInputStream input;
|
||||||
private final BufferedOutputStream output;
|
private final BufferedOutputStream output;
|
||||||
private PusherBoard board;
|
private PusherGame board;
|
||||||
|
|
||||||
public Client(String host, int port) throws IOException {
|
public Client(String host, int port) throws IOException {
|
||||||
socket = new Socket(host, port);
|
socket = new Socket(host, port);
|
||||||
|
@ -60,7 +62,7 @@ public class Client {
|
||||||
String s = new String(aBuffer).trim();
|
String s = new String(aBuffer).trim();
|
||||||
String[] boardValues = s.split(" ");
|
String[] boardValues = s.split(" ");
|
||||||
|
|
||||||
board = new PusherBoard(player, boardValues);
|
board = new PusherGame(player, boardValues);
|
||||||
|
|
||||||
return player == Player.RED;
|
return player == Player.RED;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +81,7 @@ public class Client {
|
||||||
// Thread.sleep(500);
|
// Thread.sleep(500);
|
||||||
|
|
||||||
String nextMove = board.runNextMove();
|
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.write(nextMove.getBytes(), 0, nextMove.length());
|
||||||
output.flush();
|
output.flush();
|
||||||
|
@ -89,7 +91,7 @@ public class Client {
|
||||||
System.err.println("Mouvement invalide!");
|
System.err.println("Mouvement invalide!");
|
||||||
Thread.sleep(500);
|
Thread.sleep(500);
|
||||||
|
|
||||||
PusherBoard.printBoard(board.getBoard());
|
PusherGame.printBoard(board.getBoard());
|
||||||
|
|
||||||
// play();
|
// play();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package laboratoire4;
|
package laboratoire4;
|
||||||
|
|
||||||
|
import laboratoire4.game.PusherGame;
|
||||||
|
import laboratoire4.pawns.Pawn;
|
||||||
|
import laboratoire4.pawns.PawnMovement;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
public class GameTree {
|
public class GameTree {
|
||||||
private Node root;
|
private Node root;
|
||||||
|
|
||||||
public Node buildTree(PusherBoard board){
|
public Node buildTree(PusherGame board){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,9 +20,9 @@ public class GameTree {
|
||||||
static class Node {
|
static class Node {
|
||||||
private final Collection<Node> childs;
|
private final Collection<Node> childs;
|
||||||
private final Pawn pawn;
|
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.childs = childs;
|
||||||
this.pawn = pawn;
|
this.pawn = pawn;
|
||||||
this.movement = movement;
|
this.movement = movement;
|
||||||
|
@ -32,7 +36,7 @@ public class GameTree {
|
||||||
return pawn;
|
return pawn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pawn.PawnMovement getMovement() {
|
public PawnMovement getMovement() {
|
||||||
return movement;
|
return movement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package laboratoire4;
|
package laboratoire4;
|
||||||
|
|
||||||
|
import laboratoire4.pawns.PawnMovement;
|
||||||
|
|
||||||
public interface IPawn {
|
public interface IPawn {
|
||||||
boolean isMoveValid(IPawn[][] board, Pawn.PawnMovement movement);
|
boolean isMoveValid(IPawn[][] board, PawnMovement movement);
|
||||||
void move(Pawn.PawnMovement movement);
|
boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol);
|
||||||
|
void move(PawnMovement movement);
|
||||||
boolean isPusher();
|
boolean isPusher();
|
||||||
int getDirection();
|
int getDirection();
|
||||||
Player getPlayer();
|
Player getPlayer();
|
||||||
|
|
|
@ -1,134 +0,0 @@
|
||||||
package laboratoire4;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class MiniMax {
|
|
||||||
private static final int MAX_MAX_DEPTH = 6;
|
|
||||||
private static int MAX_DEPTH = 2;
|
|
||||||
|
|
||||||
public static MiniMaxResult miniMax(PusherBoard board) {
|
|
||||||
long startMillis = System.currentTimeMillis();
|
|
||||||
MovingBoard game = new MovingBoard(board.getBoard(), board.getPlayer());
|
|
||||||
Collection<Action> actions = getActions(game, true);
|
|
||||||
|
|
||||||
MiniMaxResult maxResult = null;
|
|
||||||
|
|
||||||
for (int i = 0; i <= MAX_MAX_DEPTH; i += 2) {
|
|
||||||
MAX_DEPTH = i;
|
|
||||||
for (Action action : actions) {
|
|
||||||
int score = min(game, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
|
||||||
|
|
||||||
if (maxResult == null || score > maxResult.getScore()) {
|
|
||||||
MovingPawn pawn = action.getPawn();
|
|
||||||
maxResult = new MiniMaxResult(score, pawn.getRow(), pawn.getCol(), action.getMovement());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long endMillis = System.currentTimeMillis();
|
|
||||||
System.out.printf("%d ms\n", (endMillis - startMillis));
|
|
||||||
|
|
||||||
return maxResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int max(MovingBoard game, int depth, int alpha, int beta) {
|
|
||||||
if (depth >= MAX_DEPTH) {
|
|
||||||
return evaluate(game);
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxScore = Integer.MIN_VALUE;
|
|
||||||
|
|
||||||
for (Action action : getActions(game, true)) {
|
|
||||||
MovingPawn pawn = action.getPawn();
|
|
||||||
Pawn.PawnMovement movement = action.getMovement();
|
|
||||||
|
|
||||||
game.move(pawn, movement);
|
|
||||||
int score = min(game, depth + 1, Math.max(alpha, maxScore), beta);
|
|
||||||
game.revertMove();
|
|
||||||
|
|
||||||
if (score > maxScore) {
|
|
||||||
maxScore = score;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxScore >= beta) {
|
|
||||||
return maxScore;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return maxScore;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int min(MovingBoard game, int depth, int alpha, int beta) {
|
|
||||||
if (depth >= MAX_DEPTH) {
|
|
||||||
return evaluate(game);
|
|
||||||
}
|
|
||||||
|
|
||||||
int minScore = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
for (Action action : getActions(game, false)) {
|
|
||||||
MovingPawn pawn = action.getPawn();
|
|
||||||
Pawn.PawnMovement movement = action.getMovement();
|
|
||||||
|
|
||||||
game.move(pawn, movement);
|
|
||||||
int score = max(game, depth + 1, alpha, Math.min(beta, minScore)) * -1;
|
|
||||||
game.revertMove();
|
|
||||||
|
|
||||||
if (score < minScore) {
|
|
||||||
minScore = score;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minScore <= alpha) {
|
|
||||||
return minScore;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return minScore;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Collection<Action> getActions(MovingBoard game, boolean max) {
|
|
||||||
List<Action> actions = new ArrayList<>();
|
|
||||||
Collection<MovingPawn> pawns = max ? game.getMaxPawns() : game.getMinPawns();
|
|
||||||
Pawn.PawnMovement[] movements = Pawn.PawnMovement.values();
|
|
||||||
|
|
||||||
// Collection<MovingPawn> winningPawns = pawns.stream().filter(p -> p.getRow() + p.getDirection() == p.getPlayer().getGoal()).collect(Collectors.toList());
|
|
||||||
// if (winningPawns.size() > 0) {
|
|
||||||
// pawns = winningPawns;
|
|
||||||
// }
|
|
||||||
|
|
||||||
for (MovingPawn pawn : pawns) {
|
|
||||||
for (Pawn.PawnMovement movement : movements) {
|
|
||||||
if (pawn.isMoveValid(game.getBoard(), movement)) {
|
|
||||||
actions.add(new Action(pawn, movement));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.shuffle(actions);
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int evaluate(MovingBoard board) {
|
|
||||||
return BoardEvaluator.evaluate(board);
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Action {
|
|
||||||
private final MovingPawn pawn;
|
|
||||||
private final Pawn.PawnMovement movement;
|
|
||||||
|
|
||||||
public Action(MovingPawn pawn, Pawn.PawnMovement movement) {
|
|
||||||
this.pawn = pawn;
|
|
||||||
this.movement = movement;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MovingPawn getPawn() {
|
|
||||||
return pawn;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Pawn.PawnMovement getMovement() {
|
|
||||||
return movement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package laboratoire4;
|
|
||||||
|
|
||||||
public class MiniMaxResult {
|
|
||||||
private final int score;
|
|
||||||
private final int row;
|
|
||||||
private final int col;
|
|
||||||
private final Pawn.PawnMovement movement;
|
|
||||||
|
|
||||||
public MiniMaxResult(int score, int row, int col, Pawn.PawnMovement movement) {
|
|
||||||
this.score = score;
|
|
||||||
this.row = row;
|
|
||||||
this.col = col;
|
|
||||||
this.movement = movement;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getScore() {
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRow() {
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCol() {
|
|
||||||
return col;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Pawn.PawnMovement getMovement() {
|
|
||||||
return movement;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "MiniMaxResult{" +
|
|
||||||
"score=" + score +
|
|
||||||
", col=" + col +
|
|
||||||
", row=" + row +
|
|
||||||
", movement=" + movement +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package laboratoire4;
|
|
||||||
|
|
||||||
public interface MovingPawn extends IPawn {
|
|
||||||
void revertMove();
|
|
||||||
int getMoveCount();
|
|
||||||
|
|
||||||
static MovingPawn from(Pawn pawn) {
|
|
||||||
if (pawn instanceof Pusher) {
|
|
||||||
return new MovingPusher(pawn.getPlayer(), pawn.getRow(), pawn.getCol());
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MovingPushed(pawn.getPlayer(), pawn.getRow(), pawn.getCol());
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean areSamePlayers(IPawn a, IPawn b) {
|
|
||||||
return a != null && b != null && a.getPlayer() == b.getPlayer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,20 @@
|
||||||
package laboratoire4;
|
package laboratoire4.game;
|
||||||
|
|
||||||
public class PusherBoard {
|
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.MasterStrategy;
|
||||||
|
import laboratoire4.strategies.Strategy;
|
||||||
|
import laboratoire4.strategies.EvaluationResult;
|
||||||
|
|
||||||
|
public class PusherGame implements Game {
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private Pawn[][] board;
|
private Pawn[][] board;
|
||||||
|
|
||||||
public PusherBoard(Player player, String[] boardValues) {
|
public PusherGame(Player player, String[] boardValues) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
|
|
||||||
newGame(boardValues);
|
newGame(boardValues);
|
||||||
|
@ -37,9 +47,11 @@ public class PusherBoard {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String runNextMove() {
|
public String runNextMove() {
|
||||||
MiniMaxResult result = MiniMax.miniMax(this);
|
// MiniMaxResult result = MiniMax.miniMax(this);
|
||||||
|
EvaluationResult result = MasterStrategy.getInstance().getNextMove(this);
|
||||||
|
|
||||||
Pawn pawn = board[result.getRow()][result.getCol()];
|
Pawn pawn = board[result.getRow()][result.getCol()];
|
||||||
System.out.println(result.getScore());
|
// System.out.println(result.getScore());
|
||||||
|
|
||||||
String initialPosition = pawn.getPosition();
|
String initialPosition = pawn.getPosition();
|
||||||
move(pawn, result.getMovement());
|
move(pawn, result.getMovement());
|
||||||
|
@ -57,11 +69,11 @@ public class PusherBoard {
|
||||||
int fromRow = Integer.parseInt(String.valueOf(from.charAt(1))) - 1;
|
int fromRow = Integer.parseInt(String.valueOf(from.charAt(1))) - 1;
|
||||||
int toCol = (int) to.charAt(0) - 65;
|
int toCol = (int) to.charAt(0) - 65;
|
||||||
|
|
||||||
Pawn.PawnMovement movement = Pawn.PawnMovement.from(toCol - fromCol);
|
PawnMovement movement = PawnMovement.from(toCol - fromCol);
|
||||||
move(fromRow, fromCol, movement);
|
move(fromRow, fromCol, movement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(int row, int col, Pawn.PawnMovement movement) {
|
public void move(int row, int col, PawnMovement movement) {
|
||||||
Pawn pawn = board[row][col];
|
Pawn pawn = board[row][col];
|
||||||
if (pawn == null) {
|
if (pawn == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -70,16 +82,12 @@ public class PusherBoard {
|
||||||
move(pawn, movement);
|
move(pawn, movement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void move(Pawn pawn, Pawn.PawnMovement movement) {
|
public void move(IPawn pawn, PawnMovement movement) {
|
||||||
// if (!pawn.isMoveValid(board, movement)) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
int toRow = pawn.getRow() + pawn.getDirection();
|
int toRow = pawn.getRow() + pawn.getDirection();
|
||||||
int toCol = pawn.getCol() + movement.getMove();
|
int toCol = pawn.getCol() + movement.getMove();
|
||||||
|
|
||||||
board[pawn.getRow()][pawn.getCol()] = null;
|
board[pawn.getRow()][pawn.getCol()] = null;
|
||||||
board[toRow][toCol] = pawn;
|
board[toRow][toCol] = (Pawn) pawn;
|
||||||
pawn.move(movement);
|
pawn.move(movement);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
package laboratoire4;
|
package laboratoire4.game;
|
||||||
|
|
||||||
|
import laboratoire4.IPawn;
|
||||||
|
import laboratoire4.Player;
|
||||||
|
import laboratoire4.pawns.PawnMovement;
|
||||||
|
import laboratoire4.pawns.SimulatedPawn;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -6,34 +11,34 @@ import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class MovingBoard {
|
public class SimulatedGame implements Game {
|
||||||
private final MovingPawn[][] board;
|
private final SimulatedPawn[][] board;
|
||||||
private final Player player;
|
private final Player player;
|
||||||
|
|
||||||
private final Stack<MovingPawn> removedPawns = new Stack<>();
|
private final Stack<SimulatedPawn> removedPawns = new Stack<>();
|
||||||
private final Stack<MovingPawn> previousMovedPawns = new Stack<>();
|
private final Stack<SimulatedPawn> previousMovedPawns = new Stack<>();
|
||||||
|
|
||||||
public MovingBoard(Pawn[][] board, Player player) {
|
public SimulatedGame(IPawn[][] board, Player player) {
|
||||||
this.board = asMovingPawns(board);
|
this.board = asMovingPawns(board);
|
||||||
this.player = player;
|
this.player = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(MovingPawn pawn, Pawn.PawnMovement movement) {
|
public void move(IPawn pawn, PawnMovement movement) {
|
||||||
int toRow = pawn.getRow() + pawn.getDirection();
|
int toRow = pawn.getRow() + pawn.getDirection();
|
||||||
int toCol = pawn.getCol() + movement.getMove();
|
int toCol = pawn.getCol() + movement.getMove();
|
||||||
MovingPawn capturedPawn = board[toRow][toCol];
|
SimulatedPawn capturedPawn = board[toRow][toCol];
|
||||||
|
|
||||||
previousMovedPawns.push(pawn);
|
previousMovedPawns.push((SimulatedPawn) pawn);
|
||||||
removedPawns.push(capturedPawn);
|
removedPawns.push(capturedPawn);
|
||||||
|
|
||||||
board[pawn.getRow()][pawn.getCol()] = null;
|
board[pawn.getRow()][pawn.getCol()] = null;
|
||||||
board[toRow][toCol] = pawn;
|
board[toRow][toCol] = (SimulatedPawn) pawn;
|
||||||
pawn.move(movement);
|
pawn.move(movement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void revertMove() {
|
public void revertMove() {
|
||||||
MovingPawn pawn = previousMovedPawns.pop();
|
SimulatedPawn pawn = previousMovedPawns.pop();
|
||||||
MovingPawn capturedPawn = removedPawns.pop();
|
SimulatedPawn capturedPawn = removedPawns.pop();
|
||||||
|
|
||||||
board[pawn.getRow()][pawn.getCol()] = capturedPawn;
|
board[pawn.getRow()][pawn.getCol()] = capturedPawn;
|
||||||
|
|
||||||
|
@ -41,7 +46,7 @@ public class MovingBoard {
|
||||||
board[pawn.getRow()][pawn.getCol()] = pawn;
|
board[pawn.getRow()][pawn.getCol()] = pawn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MovingPawn getNextPawn(IPawn pawn, Pawn.PawnMovement move) {
|
public SimulatedPawn getNextPawn(IPawn pawn, PawnMovement move) {
|
||||||
int nextRow = pawn.getRow() + pawn.getDirection();
|
int nextRow = pawn.getRow() + pawn.getDirection();
|
||||||
int nextCol = pawn.getCol() + move.getMove();
|
int nextCol = pawn.getCol() + move.getMove();
|
||||||
|
|
||||||
|
@ -52,7 +57,7 @@ public class MovingBoard {
|
||||||
return board[nextRow][nextCol];
|
return board[nextRow][nextCol];
|
||||||
}
|
}
|
||||||
|
|
||||||
public MovingPawn[][] getBoard() {
|
public SimulatedPawn[][] getBoard() {
|
||||||
return board;
|
return board;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,18 +69,11 @@ public class MovingBoard {
|
||||||
return removedPawns.peek() != null;
|
return removedPawns.peek() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<MovingPawn> getMaxPawns() {
|
|
||||||
return getPawns(p -> p.getPlayer() == player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<MovingPawn> getMinPawns() {
|
private Collection<SimulatedPawn> getPawns(Predicate<SimulatedPawn> predicate) {
|
||||||
return getPawns(p -> p.getPlayer() != player);
|
List<SimulatedPawn> pawns = new ArrayList<>();
|
||||||
}
|
for (SimulatedPawn[] row : board) {
|
||||||
|
for (SimulatedPawn pawn : row) {
|
||||||
private Collection<MovingPawn> getPawns(Predicate<MovingPawn> predicate) {
|
|
||||||
List<MovingPawn> pawns = new ArrayList<>();
|
|
||||||
for (MovingPawn[] row : board) {
|
|
||||||
for (MovingPawn pawn : row) {
|
|
||||||
if (pawn != null && predicate.test(pawn)) {
|
if (pawn != null && predicate.test(pawn)) {
|
||||||
pawns.add(pawn);
|
pawns.add(pawn);
|
||||||
}
|
}
|
||||||
|
@ -84,15 +82,15 @@ public class MovingBoard {
|
||||||
return pawns;
|
return pawns;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MovingPawn[][] asMovingPawns(Pawn[][] board) {
|
public static SimulatedPawn[][] asMovingPawns(IPawn[][] board) {
|
||||||
MovingPawn[][] to = new MovingPawn[board.length][board.length];
|
SimulatedPawn[][] to = new SimulatedPawn[board.length][board.length];
|
||||||
for (int row = 0; row < board.length; row++) {
|
for (int row = 0; row < board.length; row++) {
|
||||||
for (int col = 0; col < board.length; col++) {
|
for (int col = 0; col < board.length; col++) {
|
||||||
if (board[row][col] == null) {
|
if (board[row][col] == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MovingPawn pawn = MovingPawn.from(board[row][col]);
|
SimulatedPawn pawn = SimulatedPawn.from(board[row][col]);
|
||||||
to[row][col] = pawn;
|
to[row][col] = pawn;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
package laboratoire4;
|
package laboratoire4.pawns;
|
||||||
|
|
||||||
|
import laboratoire4.IPawn;
|
||||||
|
import laboratoire4.Player;
|
||||||
|
|
||||||
public abstract class Pawn implements IPawn {
|
public abstract class Pawn implements IPawn {
|
||||||
protected final Player player;
|
protected final Player player;
|
||||||
|
@ -13,7 +16,7 @@ public abstract class Pawn implements IPawn {
|
||||||
|
|
||||||
public void move(PawnMovement movement) {
|
public void move(PawnMovement movement) {
|
||||||
setRow(row + player.getDirection());
|
setRow(row + player.getDirection());
|
||||||
setCol(col + movement.move);
|
setCol(col + movement.getMove());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPosition() {
|
public String getPosition() {
|
||||||
|
@ -46,44 +49,23 @@ public abstract class Pawn implements IPawn {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMoveValid(IPawn[][] board, PawnMovement movement) {
|
public boolean isMoveValid(IPawn[][] board, PawnMovement movement) {
|
||||||
int nextRow = row + getDirection();
|
return isMoveValid(board, movement, row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
|
||||||
|
int nextRow = fromRow + getDirection();
|
||||||
if (nextRow < 0 || nextRow >= board.length) {
|
if (nextRow < 0 || nextRow >= board.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nextCol = col + movement.move;
|
int nextCol = fromCol + movement.getMove();
|
||||||
if (nextCol < 0 || nextCol >= board.length) {
|
if (nextCol < 0 || nextCol >= board.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isMoveReallyValid(board, movement);
|
return isMoveReallyValid(board, movement, fromRow, fromCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement);
|
protected abstract boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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,53 @@
|
||||||
|
package laboratoire4.pawns;
|
||||||
|
|
||||||
|
import laboratoire4.IPawn;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
package laboratoire4;
|
package laboratoire4.pawns;
|
||||||
|
|
||||||
|
import laboratoire4.IPawn;
|
||||||
|
import laboratoire4.Player;
|
||||||
|
|
||||||
public class Pushed extends Pawn {
|
public class Pushed extends Pawn {
|
||||||
public Pushed(Player player, int row, int col) {
|
public Pushed(Player player, int row, int col) {
|
||||||
|
@ -11,17 +14,17 @@ public class Pushed extends Pawn {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement) {
|
public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
|
||||||
int direction = getDirection();
|
int direction = getDirection();
|
||||||
IPawn pusher = null;
|
IPawn pusher = null;
|
||||||
IPawn to = board[row + direction][col + movement.getMove()];
|
IPawn to = board[fromRow + direction][fromCol + movement.getMove()];
|
||||||
|
|
||||||
if (col > 0 && movement == PawnMovement.RIGHT_DIAGONAL) {
|
if (fromCol > 0 && movement == PawnMovement.RIGHT_DIAGONAL) {
|
||||||
pusher = board[row - direction][col - movement.getMove()];
|
pusher = board[fromRow - direction][fromCol - movement.getMove()];
|
||||||
} else if (col < board.length - 1 && movement == PawnMovement.LEFT_DIAGONAL) {
|
} else if (fromCol < board.length - 1 && movement == PawnMovement.LEFT_DIAGONAL) {
|
||||||
pusher = board[row - direction][col - movement.getMove()];
|
pusher = board[fromRow - direction][fromCol - movement.getMove()];
|
||||||
} else if (movement == PawnMovement.STRAIGHT) {
|
} else if (movement == PawnMovement.STRAIGHT) {
|
||||||
pusher = board[row - direction][col];
|
pusher = board[fromRow - direction][fromCol];
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean pusherValid = pusher != null && pusher.isPusher() && pusher.getPlayer() == player;
|
boolean pusherValid = pusher != null && pusher.isPusher() && pusher.getPlayer() == player;
|
|
@ -1,4 +1,7 @@
|
||||||
package laboratoire4;
|
package laboratoire4.pawns;
|
||||||
|
|
||||||
|
import laboratoire4.IPawn;
|
||||||
|
import laboratoire4.Player;
|
||||||
|
|
||||||
public class Pusher extends Pawn {
|
public class Pusher extends Pawn {
|
||||||
public Pusher(Player player, int row, int col) {
|
public Pusher(Player player, int row, int col) {
|
||||||
|
@ -11,8 +14,8 @@ public class Pusher extends Pawn {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement) {
|
public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
|
||||||
IPawn to = board[getRow() + getDirection()][getCol() + movement.getMove()];
|
IPawn to = board[fromRow + getDirection()][fromCol + movement.getMove()];
|
||||||
|
|
||||||
if (to == null) {
|
if (to == null) {
|
||||||
return true;
|
return true;
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
package laboratoire4;
|
package laboratoire4.pawns;
|
||||||
|
|
||||||
|
import laboratoire4.Player;
|
||||||
|
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
public class MovingPushed extends Pushed implements MovingPawn {
|
public class SimulatedPushed extends Pushed implements SimulatedPawn {
|
||||||
|
private final int originalRow;
|
||||||
|
private final int originalCol;
|
||||||
private final Stack<PawnMovement> previousMoves = new Stack<>();
|
private final Stack<PawnMovement> previousMoves = new Stack<>();
|
||||||
private int moveCount = 0;
|
private int moveCount = 0;
|
||||||
|
|
||||||
public MovingPushed(Player player, int row, int col) {
|
public SimulatedPushed(Player player, int row, int col) {
|
||||||
super(player, row, col);
|
super(player, row, col);
|
||||||
|
|
||||||
|
originalRow = row;
|
||||||
|
originalCol = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,6 +32,16 @@ public class MovingPushed extends Pushed implements MovingPawn {
|
||||||
moveCount--;
|
moveCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOriginalRow() {
|
||||||
|
return originalRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOriginalCol() {
|
||||||
|
return originalCol;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMoveCount() {
|
public int getMoveCount() {
|
||||||
return moveCount;
|
return moveCount;
|
|
@ -1,13 +1,20 @@
|
||||||
package laboratoire4;
|
package laboratoire4.pawns;
|
||||||
|
|
||||||
|
import laboratoire4.Player;
|
||||||
|
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
public class MovingPusher extends Pusher implements MovingPawn {
|
public class SimulatedPusher extends Pusher implements SimulatedPawn {
|
||||||
|
private final int originalRow;
|
||||||
|
private final int originalCol;
|
||||||
private final Stack<PawnMovement> previousMoves = new Stack<>();
|
private final Stack<PawnMovement> previousMoves = new Stack<>();
|
||||||
private int moveCount = 0;
|
private int moveCount = 0;
|
||||||
|
|
||||||
public MovingPusher(Player player, int row, int col) {
|
public SimulatedPusher(Player player, int row, int col) {
|
||||||
super(player, row, col);
|
super(player, row, col);
|
||||||
|
|
||||||
|
originalRow = row;
|
||||||
|
originalCol = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,6 +32,16 @@ public class MovingPusher extends Pusher implements MovingPawn {
|
||||||
moveCount--;
|
moveCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOriginalRow() {
|
||||||
|
return originalRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOriginalCol() {
|
||||||
|
return originalCol;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMoveCount() {
|
public int getMoveCount() {
|
||||||
return moveCount;
|
return moveCount;
|
|
@ -0,0 +1,68 @@
|
||||||
|
package laboratoire4.strategies;
|
||||||
|
|
||||||
|
import laboratoire4.IPawn;
|
||||||
|
import laboratoire4.game.Game;
|
||||||
|
import laboratoire4.game.GameUtils;
|
||||||
|
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 originalMinPawnCount;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EvaluationResult getNextMove(Game game) {
|
||||||
|
originalMinPawnCount = GameUtils.getMinPawnsAsStream(game.getBoard(), game.getPlayer()).count();
|
||||||
|
|
||||||
|
return super.getNextMove(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int evaluateSimulation() {
|
||||||
|
for (SimulatedPawn pawn : getMaxPawns()) {
|
||||||
|
if (pawn.getRow() == pawn.getPlayer().getGoal()) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
int minPawnCount = getMinPawns().size();
|
||||||
|
long capturedPawnCount = originalMinPawnCount - minPawnCount;
|
||||||
|
score += Math.pow(capturedPawnCount, 2);
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWeight(Game game) {
|
||||||
|
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) {
|
||||||
|
weight = PawnUtils.hasEasyWinPath(game.getBoard(), pawn) ? 8 : 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distanceFromGoal > 2 && distanceFromGoal <= ATTACK_DISTANCE_FROM_GOAL) {
|
||||||
|
weight = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weight > maxWeight) {
|
||||||
|
maxWeight = weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxWeight;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package laboratoire4.strategies;
|
||||||
|
|
||||||
|
import laboratoire4.IPawn;
|
||||||
|
import laboratoire4.game.Game;
|
||||||
|
import laboratoire4.game.GameUtils;
|
||||||
|
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 = 3;
|
||||||
|
|
||||||
|
private final Map<IPawn, Integer> dangerousPawns = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EvaluationResult getNextMove(Game game) {
|
||||||
|
for (IPawn pawn : GameUtils.getMinPawns(game.getBoard(), game.getPlayer())) {
|
||||||
|
int distance = PawnUtils.distanceFromGoal(pawn);
|
||||||
|
|
||||||
|
if (distance <= DEFENSE_DISTANCE_FROM_HOME) {
|
||||||
|
dangerousPawns.put(pawn, DEFENSE_DISTANCE_FROM_HOME - distance + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dangerousPawns.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getNextMove(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWeight(Game game) {
|
||||||
|
Collection<IPawn> minPawns = GameUtils.getMinPawns(game.getBoard(), game.getPlayer());
|
||||||
|
|
||||||
|
int maxWeight = 0;
|
||||||
|
for (IPawn pawn : minPawns) {
|
||||||
|
int weight = 0;
|
||||||
|
|
||||||
|
int distanceFromGoal = PawnUtils.distanceFromGoal(pawn);
|
||||||
|
if (distanceFromGoal <= 1) {
|
||||||
|
return WEIGHT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distanceFromGoal == 2) {
|
||||||
|
weight = PawnUtils.hasEasyWinPath(game.getBoard(), pawn) ? 8 : 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distanceFromGoal > 2 && distanceFromGoal <= DEFENSE_DISTANCE_FROM_HOME) {
|
||||||
|
weight = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weight > maxWeight) {
|
||||||
|
maxWeight = weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int evaluateSimulation() {
|
||||||
|
for (SimulatedPawn pawn : getMinPawns()) {
|
||||||
|
if (pawn.getRow() == pawn.getPlayer().getGoal()) {
|
||||||
|
return Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
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.isEmpty()) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,61 @@
|
||||||
|
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 = 1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EvaluationResult getNextMove(Game game) {
|
||||||
|
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(Game game) {
|
||||||
|
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,77 @@
|
||||||
|
package laboratoire4.strategies;
|
||||||
|
|
||||||
|
import laboratoire4.game.Game;
|
||||||
|
|
||||||
|
public class MasterStrategy implements Strategy {
|
||||||
|
private static Strategy instance;
|
||||||
|
|
||||||
|
public static Strategy getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new MasterStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean starting = true;
|
||||||
|
|
||||||
|
private MasterStrategy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EvaluationResult getNextMove(Game game) {
|
||||||
|
long startMs = System.currentTimeMillis();
|
||||||
|
|
||||||
|
Strategy strategy = getBestStrategy(game);
|
||||||
|
System.out.println(strategy.getClass().getSimpleName());
|
||||||
|
|
||||||
|
EvaluationResult result = strategy.getNextMove(game);
|
||||||
|
|
||||||
|
long endMs = System.currentTimeMillis();
|
||||||
|
System.out.printf("Temps: %d ms\n", endMs - startMs);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Strategy getBestStrategy(Game game) {
|
||||||
|
Strategy[] strategies = new Strategy[]{
|
||||||
|
new WinningStrategy(),
|
||||||
|
new ImmediateDefenseStrategy(),
|
||||||
|
new DefenseStrategy(),
|
||||||
|
new AttackStrategy()
|
||||||
|
};
|
||||||
|
|
||||||
|
int maxWeight = 0;
|
||||||
|
Strategy bestStrategy = null;
|
||||||
|
|
||||||
|
for (Strategy strategy : strategies) {
|
||||||
|
int weight = strategy.getWeight(game);
|
||||||
|
if (weight == WEIGHT_MAX) {
|
||||||
|
return strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weight > maxWeight) {
|
||||||
|
maxWeight = weight;
|
||||||
|
bestStrategy = strategy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxWeight > 0) {
|
||||||
|
starting = false;
|
||||||
|
return bestStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection IfStatementWithIdenticalBranches
|
||||||
|
if (starting) {
|
||||||
|
return new StartingStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO WN: Default strategy
|
||||||
|
return new StartingStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWeight(Game game) {
|
||||||
|
return WEIGHT_MAX;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package laboratoire4.strategies;
|
||||||
|
|
||||||
|
import laboratoire4.Action;
|
||||||
|
import laboratoire4.game.Game;
|
||||||
|
import laboratoire4.game.GameUtils;
|
||||||
|
import laboratoire4.game.SimulatedGame;
|
||||||
|
import laboratoire4.pawns.PawnMovement;
|
||||||
|
import laboratoire4.pawns.SimulatedPawn;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class MiniMaxStrategy implements Strategy {
|
||||||
|
private static final int MAX_DEPTH = 4;
|
||||||
|
|
||||||
|
protected SimulatedGame game;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EvaluationResult getNextMove(Game game) {
|
||||||
|
return miniMax(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EvaluationResult miniMax(Game game) {
|
||||||
|
this.game = new SimulatedGame(game.getBoard(), game.getPlayer());
|
||||||
|
|
||||||
|
EvaluationResult maxResult = null;
|
||||||
|
int maxScore = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
for (Action<SimulatedPawn> action : getActions(true)) {
|
||||||
|
int score = min(0, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||||
|
|
||||||
|
if (maxResult == null || score > maxScore) {
|
||||||
|
SimulatedPawn 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 evaluateSimulation();
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxScore = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
for (Action<SimulatedPawn> action : getActions(true)) {
|
||||||
|
SimulatedPawn 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 evaluateSimulation();
|
||||||
|
}
|
||||||
|
|
||||||
|
int minScore = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
for (Action<SimulatedPawn> action : getActions(false)) {
|
||||||
|
SimulatedPawn pawn = action.getPawn();
|
||||||
|
PawnMovement movement = action.getMovement();
|
||||||
|
|
||||||
|
game.move(pawn, movement);
|
||||||
|
int score = max(depth + 1, alpha, Math.min(beta, minScore)) * -1;
|
||||||
|
game.revertMove();
|
||||||
|
|
||||||
|
if (score < minScore) {
|
||||||
|
minScore = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minScore <= alpha) {
|
||||||
|
return minScore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return minScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<Action<SimulatedPawn>> getActions(boolean max) {
|
||||||
|
List<Action<SimulatedPawn>> actions = new ArrayList<>();
|
||||||
|
Collection<SimulatedPawn> pawns = max ? getMaxPawns() : getMinPawns();
|
||||||
|
PawnMovement[] movements = PawnMovement.values();
|
||||||
|
|
||||||
|
for (SimulatedPawn pawn : pawns) {
|
||||||
|
for (PawnMovement movement : movements) {
|
||||||
|
if (pawn.isMoveValid(game.getBoard(), movement)) {
|
||||||
|
actions.add(new Action<>(pawn, movement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.shuffle(actions);
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Collection<SimulatedPawn> getMaxPawns() {
|
||||||
|
return GameUtils.getMaxPawns(game.getBoard(), game.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Collection<SimulatedPawn> getMinPawns() {
|
||||||
|
return GameUtils.getMinPawns(game.getBoard(), game.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract int evaluateSimulation();
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
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 StartingStrategy implements Strategy {
|
||||||
|
private static final int PAWN_TO_MOVE = 3;
|
||||||
|
private final Random random = new Random();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EvaluationResult getNextMove(Game game) {
|
||||||
|
Collection<IPawn> outsideHomePawns = GameUtils.getMaxPawnsAsStream(game.getBoard(), game.getPlayer())
|
||||||
|
.filter(p -> p.getRow() != p.getPlayer().getHome() && p.getRow() != p.getPlayer().getHome() + p.getDirection())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (outsideHomePawns.size() > PAWN_TO_MOVE) {
|
||||||
|
List<Action<IPawn>> validActions = Strategy.getValidActions(game.getBoard(), outsideHomePawns);
|
||||||
|
if (!validActions.isEmpty()) {
|
||||||
|
return getRandomMove(validActions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Action<IPawn>> validActions = Strategy.getValidActions(game);
|
||||||
|
return getRandomMove(validActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWeight(Game game) {
|
||||||
|
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,40 @@
|
||||||
|
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 {
|
||||||
|
public static final int WEIGHT_MAX = 10;
|
||||||
|
|
||||||
|
EvaluationResult getNextMove(Game game);
|
||||||
|
int getWeight(Game game);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
List<Action<IPawn>> validActions = new ArrayList<>();
|
||||||
|
|
||||||
|
for (IPawn pawn : pawns) {
|
||||||
|
for (PawnMovement movement : PawnMovement.values()) {
|
||||||
|
if (pawn.isMoveValid(board, movement)) {
|
||||||
|
validActions.add(new Action<>(pawn, movement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validActions;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package laboratoire4.strategies;
|
||||||
|
|
||||||
|
import laboratoire4.Action;
|
||||||
|
import laboratoire4.IPawn;
|
||||||
|
import laboratoire4.game.Game;
|
||||||
|
import laboratoire4.game.GameUtils;
|
||||||
|
import laboratoire4.pawns.PawnMovement;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class WinningStrategy implements Strategy {
|
||||||
|
@Override
|
||||||
|
public EvaluationResult getNextMove(Game game) {
|
||||||
|
Action<IPawn> winningAction = getWinningAction(game);
|
||||||
|
|
||||||
|
if (winningAction == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPawn winningPawn = winningAction.getPawn();
|
||||||
|
return new EvaluationResult(winningPawn.getRow(), winningPawn.getCol(), winningAction.getMovement());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWeight(Game game) {
|
||||||
|
Action<IPawn> winningAction = getWinningAction(game);
|
||||||
|
|
||||||
|
if (winningAction == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WEIGHT_MAX + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Action<IPawn> getWinningAction(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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue