diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 8fa8e54..3e38ebe 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,15 +4,36 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -74,7 +95,7 @@
-
+
@@ -87,7 +108,7 @@
-
+
@@ -100,7 +121,7 @@
-
+
@@ -135,6 +156,10 @@
+
+
+
+
1679263366439
@@ -171,7 +196,14 @@
1679786894920
-
+
+ 1680580135038
+
+
+
+ 1680580135038
+
+
@@ -182,7 +214,8 @@
-
+
+
diff --git a/src/main/java/laboratoire4/Action.java b/src/main/java/laboratoire4/Action.java
new file mode 100644
index 0000000..283017b
--- /dev/null
+++ b/src/main/java/laboratoire4/Action.java
@@ -0,0 +1,21 @@
+package laboratoire4;
+
+import laboratoire4.pawns.PawnMovement;
+
+public class Action {
+ 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;
+ }
+}
diff --git a/src/main/java/laboratoire4/BoardEvaluator.java b/src/main/java/laboratoire4/BoardEvaluator.java
deleted file mode 100644
index b9b204b..0000000
--- a/src/main/java/laboratoire4/BoardEvaluator.java
+++ /dev/null
@@ -1,230 +0,0 @@
-package laboratoire4;
-
-import java.util.Collection;
-
-public class BoardEvaluator {
- public static int evaluate(MovingBoard game) {
- Collection maxPawns = game.getMaxPawns();
- Collection minPawns = game.getMinPawns();
-
- return evaluatePlayer(game, maxPawns, minPawns, true) - evaluatePlayer(game, minPawns, maxPawns, false);
- }
-
- //region Full Board
- private static int evaluatePlayer(MovingBoard game, Collection maxPawns, Collection 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 maxPawns, Collection 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 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
-}
diff --git a/src/main/java/laboratoire4/Client.java b/src/main/java/laboratoire4/Client.java
index b76468a..bcc4dcd 100644
--- a/src/main/java/laboratoire4/Client.java
+++ b/src/main/java/laboratoire4/Client.java
@@ -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;
}
@@ -79,7 +81,7 @@ public class Client {
// Thread.sleep(500);
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();
@@ -89,7 +91,7 @@ public class Client {
System.err.println("Mouvement invalide!");
Thread.sleep(500);
- PusherBoard.printBoard(board.getBoard());
+ PusherGame.printBoard(board.getBoard());
// play();
}
diff --git a/src/main/java/laboratoire4/GameTree.java b/src/main/java/laboratoire4/GameTree.java
index 4c8d46d..dacb216 100644
--- a/src/main/java/laboratoire4/GameTree.java
+++ b/src/main/java/laboratoire4/GameTree.java
@@ -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 childs;
private final Pawn pawn;
- private final Pawn.PawnMovement movement;
+ private final PawnMovement movement;
- Node(Collection childs, Pawn pawn, Pawn.PawnMovement movement) {
+ Node(Collection 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;
}
}
diff --git a/src/main/java/laboratoire4/IPawn.java b/src/main/java/laboratoire4/IPawn.java
index f3eefb1..529a648 100644
--- a/src/main/java/laboratoire4/IPawn.java
+++ b/src/main/java/laboratoire4/IPawn.java
@@ -1,8 +1,11 @@
package laboratoire4;
+import laboratoire4.pawns.PawnMovement;
+
public interface IPawn {
- boolean isMoveValid(IPawn[][] board, Pawn.PawnMovement movement);
- void move(Pawn.PawnMovement movement);
+ boolean isMoveValid(IPawn[][] board, PawnMovement movement);
+ boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol);
+ void move(PawnMovement movement);
boolean isPusher();
int getDirection();
Player getPlayer();
diff --git a/src/main/java/laboratoire4/MiniMax.java b/src/main/java/laboratoire4/MiniMax.java
deleted file mode 100644
index 9c772b6..0000000
--- a/src/main/java/laboratoire4/MiniMax.java
+++ /dev/null
@@ -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 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 getActions(MovingBoard game, boolean max) {
- List actions = new ArrayList<>();
- Collection pawns = max ? game.getMaxPawns() : game.getMinPawns();
- Pawn.PawnMovement[] movements = Pawn.PawnMovement.values();
-
-// Collection 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;
- }
- }
-}
diff --git a/src/main/java/laboratoire4/MiniMaxResult.java b/src/main/java/laboratoire4/MiniMaxResult.java
deleted file mode 100644
index ca7dfac..0000000
--- a/src/main/java/laboratoire4/MiniMaxResult.java
+++ /dev/null
@@ -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 +
- '}';
- }
-}
diff --git a/src/main/java/laboratoire4/MovingPawn.java b/src/main/java/laboratoire4/MovingPawn.java
deleted file mode 100644
index bf6d0da..0000000
--- a/src/main/java/laboratoire4/MovingPawn.java
+++ /dev/null
@@ -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();
- }
-}
-
diff --git a/src/main/java/laboratoire4/game/Game.java b/src/main/java/laboratoire4/game/Game.java
new file mode 100644
index 0000000..6d8f239
--- /dev/null
+++ b/src/main/java/laboratoire4/game/Game.java
@@ -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();
+}
diff --git a/src/main/java/laboratoire4/game/GameUtils.java b/src/main/java/laboratoire4/game/GameUtils.java
new file mode 100644
index 0000000..f90dbc2
--- /dev/null
+++ b/src/main/java/laboratoire4/game/GameUtils.java
@@ -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 Stream getPawnsAsStream(T[][] board) {
+ return Arrays.stream(board)
+ .flatMap(Arrays::stream)
+ .filter(Objects::nonNull);
+ }
+
+ public static Collection getPawns(T[][] board) {
+ return getPawnsAsStream(board).collect(Collectors.toList());
+ }
+
+ public static Stream getMaxPawnsAsStream(T[][] board, Player player) {
+ return getPawnsAsStream(board).filter(p -> p.getPlayer() == player);
+ }
+
+ public static Collection getMaxPawns(T[][] board, Player player) {
+ return getMaxPawnsAsStream(board, player).collect(Collectors.toList());
+ }
+
+ public static Stream getMinPawnsAsStream(T[][] board, Player player) {
+ return getPawnsAsStream(board).filter(p -> p.getPlayer() != player);
+ }
+
+ public static Collection getMinPawns(T[][] board, Player player) {
+ return getMinPawnsAsStream(board, player).collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/laboratoire4/PusherBoard.java b/src/main/java/laboratoire4/game/PusherGame.java
similarity index 74%
rename from src/main/java/laboratoire4/PusherBoard.java
rename to src/main/java/laboratoire4/game/PusherGame.java
index 6675b8a..5f9a53e 100644
--- a/src/main/java/laboratoire4/PusherBoard.java
+++ b/src/main/java/laboratoire4/game/PusherGame.java
@@ -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 Pawn[][] board;
- public PusherBoard(Player player, String[] boardValues) {
+ public PusherGame(Player player, String[] boardValues) {
this.player = player;
newGame(boardValues);
@@ -37,9 +47,11 @@ public class PusherBoard {
}
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()];
- System.out.println(result.getScore());
+// System.out.println(result.getScore());
String initialPosition = pawn.getPosition();
move(pawn, result.getMovement());
@@ -57,11 +69,11 @@ public class PusherBoard {
int fromRow = Integer.parseInt(String.valueOf(from.charAt(1))) - 1;
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);
}
- public void move(int row, int col, Pawn.PawnMovement movement) {
+ public void move(int row, int col, PawnMovement movement) {
Pawn pawn = board[row][col];
if (pawn == null) {
return;
@@ -70,16 +82,12 @@ public class PusherBoard {
move(pawn, movement);
}
- private void move(Pawn pawn, Pawn.PawnMovement movement) {
-// if (!pawn.isMoveValid(board, movement)) {
-// return;
-// }
-
+ 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;
+ board[toRow][toCol] = (Pawn) pawn;
pawn.move(movement);
}
diff --git a/src/main/java/laboratoire4/MovingBoard.java b/src/main/java/laboratoire4/game/SimulatedGame.java
similarity index 53%
rename from src/main/java/laboratoire4/MovingBoard.java
rename to src/main/java/laboratoire4/game/SimulatedGame.java
index 6a9be74..732d59a 100644
--- a/src/main/java/laboratoire4/MovingBoard.java
+++ b/src/main/java/laboratoire4/game/SimulatedGame.java
@@ -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.Collection;
@@ -6,34 +11,34 @@ import java.util.List;
import java.util.Stack;
import java.util.function.Predicate;
-public class MovingBoard {
- private final MovingPawn[][] board;
+public class SimulatedGame implements Game {
+ private final SimulatedPawn[][] board;
private final Player player;
- private final Stack removedPawns = new Stack<>();
- private final Stack previousMovedPawns = new Stack<>();
+ private final Stack removedPawns = new Stack<>();
+ private final Stack previousMovedPawns = new Stack<>();
- public MovingBoard(Pawn[][] board, Player player) {
+ public SimulatedGame(IPawn[][] board, Player player) {
this.board = asMovingPawns(board);
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 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);
board[pawn.getRow()][pawn.getCol()] = null;
- board[toRow][toCol] = pawn;
+ board[toRow][toCol] = (SimulatedPawn) pawn;
pawn.move(movement);
}
public void revertMove() {
- MovingPawn pawn = previousMovedPawns.pop();
- MovingPawn capturedPawn = removedPawns.pop();
+ SimulatedPawn pawn = previousMovedPawns.pop();
+ SimulatedPawn capturedPawn = removedPawns.pop();
board[pawn.getRow()][pawn.getCol()] = capturedPawn;
@@ -41,7 +46,7 @@ public class MovingBoard {
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 nextCol = pawn.getCol() + move.getMove();
@@ -52,7 +57,7 @@ public class MovingBoard {
return board[nextRow][nextCol];
}
- public MovingPawn[][] getBoard() {
+ public SimulatedPawn[][] getBoard() {
return board;
}
@@ -64,18 +69,11 @@ public class MovingBoard {
return removedPawns.peek() != null;
}
- public Collection getMaxPawns() {
- return getPawns(p -> p.getPlayer() == player);
- }
- public Collection getMinPawns() {
- return getPawns(p -> p.getPlayer() != player);
- }
-
- private Collection getPawns(Predicate predicate) {
- List pawns = new ArrayList<>();
- for (MovingPawn[] row : board) {
- for (MovingPawn pawn : row) {
+ private Collection getPawns(Predicate predicate) {
+ List pawns = new ArrayList<>();
+ for (SimulatedPawn[] row : board) {
+ for (SimulatedPawn pawn : row) {
if (pawn != null && predicate.test(pawn)) {
pawns.add(pawn);
}
@@ -84,15 +82,15 @@ public class MovingBoard {
return pawns;
}
- public static MovingPawn[][] asMovingPawns(Pawn[][] board) {
- MovingPawn[][] to = new MovingPawn[board.length][board.length];
+ 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;
}
- MovingPawn pawn = MovingPawn.from(board[row][col]);
+ SimulatedPawn pawn = SimulatedPawn.from(board[row][col]);
to[row][col] = pawn;
}
}
diff --git a/src/main/java/laboratoire4/Pawn.java b/src/main/java/laboratoire4/pawns/Pawn.java
similarity index 59%
rename from src/main/java/laboratoire4/Pawn.java
rename to src/main/java/laboratoire4/pawns/Pawn.java
index 801b93a..833c887 100644
--- a/src/main/java/laboratoire4/Pawn.java
+++ b/src/main/java/laboratoire4/pawns/Pawn.java
@@ -1,4 +1,7 @@
-package laboratoire4;
+package laboratoire4.pawns;
+
+import laboratoire4.IPawn;
+import laboratoire4.Player;
public abstract class Pawn implements IPawn {
protected final Player player;
@@ -13,11 +16,11 @@ public abstract class Pawn implements IPawn {
public void move(PawnMovement movement) {
setRow(row + player.getDirection());
- setCol(col + movement.move);
+ setCol(col + movement.getMove());
}
public String getPosition() {
- char colStr = (char)(col + 65);
+ char colStr = (char) (col + 65);
return String.format("%s%d", colStr, row + 1);
}
@@ -46,44 +49,23 @@ public abstract class Pawn implements IPawn {
}
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) {
return false;
}
- int nextCol = col + movement.move;
+ int nextCol = fromCol + movement.getMove();
if (nextCol < 0 || nextCol >= board.length) {
return false;
}
- return isMoveReallyValid(board, movement);
+ return isMoveReallyValid(board, movement, fromRow, fromCol);
}
- protected abstract boolean isMoveReallyValid(IPawn[][] 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;
- }
- }
+ protected abstract boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol);
}
diff --git a/src/main/java/laboratoire4/pawns/PawnMovement.java b/src/main/java/laboratoire4/pawns/PawnMovement.java
new file mode 100644
index 0000000..cf9461c
--- /dev/null
+++ b/src/main/java/laboratoire4/pawns/PawnMovement.java
@@ -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;
+ }
+}
diff --git a/src/main/java/laboratoire4/pawns/PawnUtils.java b/src/main/java/laboratoire4/pawns/PawnUtils.java
new file mode 100644
index 0000000..b63b32c
--- /dev/null
+++ b/src/main/java/laboratoire4/pawns/PawnUtils.java
@@ -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 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;
+ }
+}
diff --git a/src/main/java/laboratoire4/Pushed.java b/src/main/java/laboratoire4/pawns/Pushed.java
similarity index 58%
rename from src/main/java/laboratoire4/Pushed.java
rename to src/main/java/laboratoire4/pawns/Pushed.java
index 4398a2d..7bac9f6 100644
--- a/src/main/java/laboratoire4/Pushed.java
+++ b/src/main/java/laboratoire4/pawns/Pushed.java
@@ -1,4 +1,7 @@
-package laboratoire4;
+package laboratoire4.pawns;
+
+import laboratoire4.IPawn;
+import laboratoire4.Player;
public class Pushed extends Pawn {
public Pushed(Player player, int row, int col) {
@@ -11,17 +14,17 @@ public class Pushed extends Pawn {
}
@Override
- public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement) {
+ public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
int direction = getDirection();
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) {
- pusher = board[row - direction][col - movement.getMove()];
- } else if (col < board.length - 1 && movement == PawnMovement.LEFT_DIAGONAL) {
- pusher = board[row - direction][col - 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[row - direction][col];
+ pusher = board[fromRow - direction][fromCol];
}
boolean pusherValid = pusher != null && pusher.isPusher() && pusher.getPlayer() == player;
diff --git a/src/main/java/laboratoire4/Pusher.java b/src/main/java/laboratoire4/pawns/Pusher.java
similarity index 74%
rename from src/main/java/laboratoire4/Pusher.java
rename to src/main/java/laboratoire4/pawns/Pusher.java
index 28d3b0d..0535a39 100644
--- a/src/main/java/laboratoire4/Pusher.java
+++ b/src/main/java/laboratoire4/pawns/Pusher.java
@@ -1,4 +1,7 @@
-package laboratoire4;
+package laboratoire4.pawns;
+
+import laboratoire4.IPawn;
+import laboratoire4.Player;
public class Pusher extends Pawn {
public Pusher(Player player, int row, int col) {
@@ -11,8 +14,8 @@ public class Pusher extends Pawn {
}
@Override
- public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement) {
- IPawn to = board[getRow() + getDirection()][getCol() + movement.getMove()];
+ public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
+ IPawn to = board[fromRow + getDirection()][fromCol + movement.getMove()];
if (to == null) {
return true;
diff --git a/src/main/java/laboratoire4/pawns/SimulatedPawn.java b/src/main/java/laboratoire4/pawns/SimulatedPawn.java
new file mode 100644
index 0000000..a86b522
--- /dev/null
+++ b/src/main/java/laboratoire4/pawns/SimulatedPawn.java
@@ -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());
+ }
+}
+
diff --git a/src/main/java/laboratoire4/MovingPushed.java b/src/main/java/laboratoire4/pawns/SimulatedPushed.java
similarity index 55%
rename from src/main/java/laboratoire4/MovingPushed.java
rename to src/main/java/laboratoire4/pawns/SimulatedPushed.java
index f093a91..059f2d7 100644
--- a/src/main/java/laboratoire4/MovingPushed.java
+++ b/src/main/java/laboratoire4/pawns/SimulatedPushed.java
@@ -1,13 +1,20 @@
-package laboratoire4;
+package laboratoire4.pawns;
+
+import laboratoire4.Player;
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 previousMoves = new Stack<>();
private int moveCount = 0;
- public MovingPushed(Player player, int row, int col) {
+ public SimulatedPushed(Player player, int row, int col) {
super(player, row, col);
+
+ originalRow = row;
+ originalCol = col;
}
@Override
@@ -25,6 +32,16 @@ public class MovingPushed extends Pushed implements MovingPawn {
moveCount--;
}
+ @Override
+ public int getOriginalRow() {
+ return originalRow;
+ }
+
+ @Override
+ public int getOriginalCol() {
+ return originalCol;
+ }
+
@Override
public int getMoveCount() {
return moveCount;
diff --git a/src/main/java/laboratoire4/MovingPusher.java b/src/main/java/laboratoire4/pawns/SimulatedPusher.java
similarity index 55%
rename from src/main/java/laboratoire4/MovingPusher.java
rename to src/main/java/laboratoire4/pawns/SimulatedPusher.java
index 30ae492..0827eed 100644
--- a/src/main/java/laboratoire4/MovingPusher.java
+++ b/src/main/java/laboratoire4/pawns/SimulatedPusher.java
@@ -1,13 +1,20 @@
-package laboratoire4;
+package laboratoire4.pawns;
+
+import laboratoire4.Player;
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 previousMoves = new Stack<>();
private int moveCount = 0;
- public MovingPusher(Player player, int row, int col) {
+ public SimulatedPusher(Player player, int row, int col) {
super(player, row, col);
+
+ originalRow = row;
+ originalCol = col;
}
@Override
@@ -25,6 +32,16 @@ public class MovingPusher extends Pusher implements MovingPawn {
moveCount--;
}
+ @Override
+ public int getOriginalRow() {
+ return originalRow;
+ }
+
+ @Override
+ public int getOriginalCol() {
+ return originalCol;
+ }
+
@Override
public int getMoveCount() {
return moveCount;
diff --git a/src/main/java/laboratoire4/strategies/AttackStrategy.java b/src/main/java/laboratoire4/strategies/AttackStrategy.java
new file mode 100644
index 0000000..e2fd873
--- /dev/null
+++ b/src/main/java/laboratoire4/strategies/AttackStrategy.java
@@ -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 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;
+ }
+}
diff --git a/src/main/java/laboratoire4/strategies/DefenseStrategy.java b/src/main/java/laboratoire4/strategies/DefenseStrategy.java
new file mode 100644
index 0000000..e72d3df
--- /dev/null
+++ b/src/main/java/laboratoire4/strategies/DefenseStrategy.java
@@ -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 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 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 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;
+ }
+}
diff --git a/src/main/java/laboratoire4/strategies/EvaluationResult.java b/src/main/java/laboratoire4/strategies/EvaluationResult.java
new file mode 100644
index 0000000..4e99074
--- /dev/null
+++ b/src/main/java/laboratoire4/strategies/EvaluationResult.java
@@ -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;
+ }
+}
diff --git a/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java b/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java
new file mode 100644
index 0000000..c3b6d54
--- /dev/null
+++ b/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java
@@ -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;
+ }
+}
diff --git a/src/main/java/laboratoire4/strategies/MasterStrategy.java b/src/main/java/laboratoire4/strategies/MasterStrategy.java
new file mode 100644
index 0000000..209c619
--- /dev/null
+++ b/src/main/java/laboratoire4/strategies/MasterStrategy.java
@@ -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;
+ }
+}
diff --git a/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java b/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java
new file mode 100644
index 0000000..9ea0270
--- /dev/null
+++ b/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java
@@ -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 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 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 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> getActions(boolean max) {
+ List> actions = new ArrayList<>();
+ Collection 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 getMaxPawns() {
+ return GameUtils.getMaxPawns(game.getBoard(), game.getPlayer());
+ }
+
+ protected Collection getMinPawns() {
+ return GameUtils.getMinPawns(game.getBoard(), game.getPlayer());
+ }
+
+ protected abstract int evaluateSimulation();
+}
diff --git a/src/main/java/laboratoire4/strategies/StartingStrategy.java b/src/main/java/laboratoire4/strategies/StartingStrategy.java
new file mode 100644
index 0000000..3ef3968
--- /dev/null
+++ b/src/main/java/laboratoire4/strategies/StartingStrategy.java
@@ -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 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> validActions = Strategy.getValidActions(game.getBoard(), outsideHomePawns);
+ if (!validActions.isEmpty()) {
+ return getRandomMove(validActions);
+ }
+ }
+
+ List> validActions = Strategy.getValidActions(game);
+ return getRandomMove(validActions);
+ }
+
+ @Override
+ public int getWeight(Game game) {
+ return 0;
+ }
+
+ private EvaluationResult getRandomMove(List> actions) {
+ int randomIndex = random.nextInt(actions.size());
+ Action nextAction = actions.get(randomIndex);
+ IPawn nextPawn = nextAction.getPawn();
+
+ return new EvaluationResult(nextPawn.getRow(), nextPawn.getCol(), nextAction.getMovement());
+ }
+}
diff --git a/src/main/java/laboratoire4/strategies/Strategy.java b/src/main/java/laboratoire4/strategies/Strategy.java
new file mode 100644
index 0000000..b239c72
--- /dev/null
+++ b/src/main/java/laboratoire4/strategies/Strategy.java
@@ -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> getValidActions(Game game) {
+ List 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> getValidActions(IPawn[][] board, Collection pawns) {
+ List> 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;
+ }
+}
diff --git a/src/main/java/laboratoire4/strategies/WinningStrategy.java b/src/main/java/laboratoire4/strategies/WinningStrategy.java
new file mode 100644
index 0000000..0c8fc23
--- /dev/null
+++ b/src/main/java/laboratoire4/strategies/WinningStrategy.java
@@ -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 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 winningAction = getWinningAction(game);
+
+ if (winningAction == null) {
+ return 0;
+ }
+
+ return WEIGHT_MAX + 1;
+ }
+
+ private static Action getWinningAction(Game game) {
+ Collection 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;
+ }
+}