diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 3e38ebe..c42e22d 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,36 +4,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -55,6 +41,7 @@
+
@@ -86,6 +73,7 @@
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
+ "settings.editor.selected.configurable": "reference.settings.ide.settings.new.ui",
"vue.rearranger.settings.migration": "true"
}
}
@@ -159,7 +147,11 @@
-
+
+
+
+
+
1679263366439
@@ -203,7 +195,14 @@
1680580135038
-
+
+ 1681061194018
+
+
+
+ 1681061194019
+
+
@@ -215,14 +214,18 @@
-
+
+
-
-
-
-
+
+
+ file://$PROJECT_DIR$/src/main/java/laboratoire4/strategies/MasterStrategy.java
+ 20
+
+
+
\ No newline at end of file
diff --git a/src/main/java/laboratoire4/Client.java b/src/main/java/laboratoire4/Client.java
index bcc4dcd..600e24d 100644
--- a/src/main/java/laboratoire4/Client.java
+++ b/src/main/java/laboratoire4/Client.java
@@ -78,7 +78,7 @@ public class Client {
board.move(previousMove);
}
-// Thread.sleep(500);
+ Thread.sleep(200);
String nextMove = board.runNextMove();
System.out.printf("Prochain mouvement: %s\n\n", nextMove);
diff --git a/src/main/java/laboratoire4/IPawn.java b/src/main/java/laboratoire4/IPawn.java
index 529a648..a94ed8c 100644
--- a/src/main/java/laboratoire4/IPawn.java
+++ b/src/main/java/laboratoire4/IPawn.java
@@ -4,6 +4,8 @@ import laboratoire4.pawns.PawnMovement;
public interface IPawn {
boolean isMoveValid(IPawn[][] board, PawnMovement movement);
+ boolean isMoveValid(IPawn[][] board, PawnMovement movement, boolean ignorePlayers);
+ boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayer);
boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol);
void move(PawnMovement movement);
boolean isPusher();
diff --git a/src/main/java/laboratoire4/pawns/Pawn.java b/src/main/java/laboratoire4/pawns/Pawn.java
index 833c887..18e2241 100644
--- a/src/main/java/laboratoire4/pawns/Pawn.java
+++ b/src/main/java/laboratoire4/pawns/Pawn.java
@@ -49,11 +49,19 @@ public abstract class Pawn implements IPawn {
}
public boolean isMoveValid(IPawn[][] board, PawnMovement movement) {
- return isMoveValid(board, movement, row, col);
+ return isMoveValid(board, movement, false);
+ }
+
+ public boolean isMoveValid(IPawn[][] board, PawnMovement movement, boolean ignorePlayers) {
+ return isMoveValid(board, movement, row, col, ignorePlayers);
+ }
+
+ public boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
+ return isMoveValid(board, movement, fromRow, fromCol, false);
}
@Override
- public boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
+ public boolean isMoveValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayers) {
int nextRow = fromRow + getDirection();
if (nextRow < 0 || nextRow >= board.length) {
return false;
@@ -64,8 +72,8 @@ public abstract class Pawn implements IPawn {
return false;
}
- return isMoveReallyValid(board, movement, fromRow, fromCol);
+ return isMoveReallyValid(board, movement, fromRow, fromCol, ignorePlayers);
}
- protected abstract boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol);
+ protected abstract boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayers);
}
diff --git a/src/main/java/laboratoire4/pawns/PawnUtils.java b/src/main/java/laboratoire4/pawns/PawnUtils.java
index b63b32c..c4ee406 100644
--- a/src/main/java/laboratoire4/pawns/PawnUtils.java
+++ b/src/main/java/laboratoire4/pawns/PawnUtils.java
@@ -1,10 +1,15 @@
package laboratoire4.pawns;
+import laboratoire4.Action;
import laboratoire4.IPawn;
+import laboratoire4.Player;
+import laboratoire4.game.Game;
+import laboratoire4.game.GameUtils;
import java.awt.*;
-import java.util.ArrayDeque;
+import java.util.List;
import java.util.Queue;
+import java.util.*;
public class PawnUtils {
public static int distanceFromGoal(IPawn pawn) {
@@ -27,7 +32,7 @@ public class PawnUtils {
Point pos = positionsToVisit.poll();
for (PawnMovement movement : PawnMovement.values()) {
- if (!initialPawn.isMoveValid(board, movement, pos.x, pos.y)) {
+ if (!initialPawn.isMoveValid(board, movement, pos.x, pos.y, false)) {
continue;
}
@@ -50,4 +55,121 @@ public class PawnUtils {
return false;
}
+
+ public static boolean canBeCaptured(Game game, IPawn pawn) {
+ for (PawnMovement movement : PawnMovement.values()) {
+ int nextRow = pawn.getRow() + pawn.getDirection();
+ int nextCol = pawn.getCol() + movement.getMove();
+
+ if (nextRow < 0 || nextRow > 7 || nextCol < 0 || nextCol > 7) {
+ continue;
+ }
+
+ IPawn nearPawn = game.getBoard()[nextRow][nextCol];
+ if (nearPawn == null || PawnUtils.areSamePlayers(pawn, nearPawn)) {
+ continue;
+ }
+
+ if (nearPawn.isMoveValid(game.getBoard(), movement.getOpposite())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean canBeCaptured(Game game, int row, int col, Player player) {
+ for (PawnMovement movement : PawnMovement.values()) {
+ int nextRow = row + player.getDirection();
+ int nextCol = col + movement.getMove();
+
+ if (nextRow < 0 || nextRow > 7 || nextCol < 0 || nextCol > 7) {
+ continue;
+ }
+
+ IPawn nearPawn = game.getBoard()[nextRow][nextCol];
+ if (nearPawn == null || player == nearPawn.getPlayer()) {
+ continue;
+ }
+
+ if (nearPawn.isMoveValid(game.getBoard(), movement.getOpposite(), true)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean canCapture(Game game, IPawn max, IPawn min) {
+ int rowDistance = Math.abs(min.getRow() - max.getRow());
+ int colDistance = Math.abs(min.getCol() - max.getCol());
+
+ if (colDistance > rowDistance) {
+ // Le pion ne pourra jamais être atteint
+ return false;
+ }
+
+ return PawnUtils.canCapture(game, max, min, max.getRow(), max.getCol(), 0, rowDistance);
+ }
+
+ public static boolean canCapture(Game game, IPawn max, IPawn min, int row, int col, int depth, int maxDepth) {
+ if (min.getRow() == row && min.getCol() == col) {
+ return true;
+ }
+
+ if (depth >= maxDepth) {
+ return false;
+ }
+
+ for (PawnMovement movement : PawnMovement.values()) {
+ if (max.isMoveValid(game.getBoard(), movement, row, col, false)) {
+ int nextRow = row + max.getDirection();
+ int nextCol = col + movement.getMove();
+
+ if (canCapture(game, max, min, nextRow, nextCol, depth + 1, maxDepth)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static Collection> getActions(Game game, boolean max) {
+ return getActions(game, max, true);
+ }
+
+ public static Collection> getActions(Game game, boolean max, boolean excludeDefense) {
+ List> actions = new ArrayList<>();
+ PawnMovement[] movements = PawnMovement.values();
+
+ //noinspection unchecked
+ Collection pawns = (Collection) (max ?
+ GameUtils.getMaxPawns(game.getBoard(), game.getPlayer()) :
+ GameUtils.getMinPawns(game.getBoard(), game.getPlayer()));
+
+ for (T pawn : pawns) {
+ if (excludeDefense && pawn.getRow() == pawn.getPlayer().getHome()) {
+ int col = pawn.getCol();
+
+ // Si possible, on ne bouge pas ces pushers, comme ça on a une défense d'urgence totale
+ if (col == 1 || col == 2 || col == 5 || col == 6) {
+ continue;
+ }
+ }
+
+ for (PawnMovement movement : movements) {
+ if (pawn.isMoveValid(game.getBoard(), movement)) {
+ actions.add(new Action<>(pawn, movement));
+ }
+ }
+ }
+
+ if (excludeDefense && actions.isEmpty()) {
+ return getActions(game, max, false);
+ }
+
+ Collections.shuffle(actions);
+ return actions;
+ }
}
diff --git a/src/main/java/laboratoire4/pawns/Pushed.java b/src/main/java/laboratoire4/pawns/Pushed.java
index 7bac9f6..633701e 100644
--- a/src/main/java/laboratoire4/pawns/Pushed.java
+++ b/src/main/java/laboratoire4/pawns/Pushed.java
@@ -14,7 +14,7 @@ public class Pushed extends Pawn {
}
@Override
- public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
+ public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayers) {
int direction = getDirection();
IPawn pusher = null;
IPawn to = board[fromRow + direction][fromCol + movement.getMove()];
@@ -28,7 +28,7 @@ public class Pushed extends Pawn {
}
boolean pusherValid = pusher != null && pusher.isPusher() && pusher.getPlayer() == player;
- boolean destinationValid = to == null || (movement != PawnMovement.STRAIGHT && to.getPlayer() != this.player);
+ boolean destinationValid = to == null || (movement != PawnMovement.STRAIGHT && (ignorePlayers || to.getPlayer() != this.player));
return pusherValid && destinationValid;
}
diff --git a/src/main/java/laboratoire4/pawns/Pusher.java b/src/main/java/laboratoire4/pawns/Pusher.java
index 0535a39..a6d0620 100644
--- a/src/main/java/laboratoire4/pawns/Pusher.java
+++ b/src/main/java/laboratoire4/pawns/Pusher.java
@@ -14,14 +14,14 @@ public class Pusher extends Pawn {
}
@Override
- public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol) {
+ public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement, int fromRow, int fromCol, boolean ignorePlayers) {
IPawn to = board[fromRow + getDirection()][fromCol + movement.getMove()];
if (to == null) {
return true;
}
- if (to.getPlayer() == player) {
+ if (!ignorePlayers && to.getPlayer() == player) {
return false;
}
diff --git a/src/main/java/laboratoire4/strategies/AttackStrategy.java b/src/main/java/laboratoire4/strategies/AttackStrategy.java
index e2fd873..474475d 100644
--- a/src/main/java/laboratoire4/strategies/AttackStrategy.java
+++ b/src/main/java/laboratoire4/strategies/AttackStrategy.java
@@ -1,34 +1,64 @@
package laboratoire4.strategies;
+import laboratoire4.Action;
import laboratoire4.IPawn;
import laboratoire4.game.Game;
import laboratoire4.game.GameUtils;
+import laboratoire4.pawns.PawnMovement;
import laboratoire4.pawns.PawnUtils;
import laboratoire4.pawns.SimulatedPawn;
import java.util.Collection;
+import java.util.stream.Collectors;
public class AttackStrategy extends MiniMaxStrategy {
private static final int ATTACK_DISTANCE_FROM_GOAL = 4;
private long originalMinPawnCount;
+ private long originalMaxPusherCount;
@Override
public EvaluationResult getNextMove(Game game) {
originalMinPawnCount = GameUtils.getMinPawnsAsStream(game.getBoard(), game.getPlayer()).count();
+ originalMaxPusherCount = GameUtils.getMaxPawnsAsStream(game.getBoard(), game.getPlayer())
+ .filter(IPawn::isPusher)
+ .count();
return super.getNextMove(game);
}
@Override
protected int evaluateSimulation() {
+ int score = 0;
+
+ if (getMinPawns().stream().anyMatch(p -> p.getRow() == p.getPlayer().getGoal())) {
+ return Integer.MIN_VALUE;
+ }
+
+ long pusherCount = getMaxPawns().stream().filter(IPawn::isPusher).count();
+ if (pusherCount == 0) {
+ return Integer.MIN_VALUE;
+ }
+ score -= Math.pow(originalMaxPusherCount - pusherCount, 2);
+
for (SimulatedPawn pawn : getMaxPawns()) {
if (pawn.getRow() == pawn.getPlayer().getGoal()) {
return Integer.MAX_VALUE;
}
- }
- int score = 0;
+ if (PawnUtils.hasEasyWinPath(game.getBoard(), pawn)) {
+ return Integer.MAX_VALUE / 2;
+ }
+
+// int distanceFromGoal = PawnUtils.distanceFromGoal(pawn);
+// if (distanceFromGoal <= ATTACK_DISTANCE_FROM_GOAL) {
+// score += ATTACK_DISTANCE_FROM_GOAL - distanceFromGoal + 1;
+// }
+
+ if (pawn.isPusher() && PawnUtils.canBeCaptured(game, pawn)) {
+ score -= 10; // On ne veut pas nécessairement bouger où on peut être capturé facilement
+ }
+ }
int minPawnCount = getMinPawns().size();
long capturedPawnCount = originalMinPawnCount - minPawnCount;
@@ -51,7 +81,11 @@ public class AttackStrategy extends MiniMaxStrategy {
}
if (distanceFromGoal == 2) {
- weight = PawnUtils.hasEasyWinPath(game.getBoard(), pawn) ? 8 : 5;
+ if (PawnUtils.hasEasyWinPath(game.getBoard(), pawn)) {
+ weight = 8;
+ } else {
+ weight = 5;
+ }
}
if (distanceFromGoal > 2 && distanceFromGoal <= ATTACK_DISTANCE_FROM_GOAL) {
@@ -65,4 +99,14 @@ public class AttackStrategy extends MiniMaxStrategy {
return maxWeight;
}
+
+ private boolean cantBeCaptured(Action action) {
+ IPawn pawn = action.getPawn();
+ PawnMovement movement = action.getMovement();
+
+ int row = pawn.getRow() + pawn.getDirection();
+ int col = pawn.getCol() + movement.getMove();
+
+ return !PawnUtils.canBeCaptured(game, row, col, pawn.getPlayer());
+ }
}
diff --git a/src/main/java/laboratoire4/strategies/DefenseStrategy.java b/src/main/java/laboratoire4/strategies/DefenseStrategy.java
index e72d3df..ddd656c 100644
--- a/src/main/java/laboratoire4/strategies/DefenseStrategy.java
+++ b/src/main/java/laboratoire4/strategies/DefenseStrategy.java
@@ -1,5 +1,6 @@
package laboratoire4.strategies;
+import laboratoire4.Action;
import laboratoire4.IPawn;
import laboratoire4.game.Game;
import laboratoire4.game.GameUtils;
@@ -13,6 +14,7 @@ import java.util.Optional;
public class DefenseStrategy extends MiniMaxStrategy {
private static final int DEFENSE_DISTANCE_FROM_HOME = 3;
+ private static final int PAWN_DEFENSE_DISTANCE = 3;
private final Map dangerousPawns = new HashMap<>();
@@ -31,23 +33,35 @@ public class DefenseStrategy extends MiniMaxStrategy {
}
return super.getNextMove(game);
+// EvaluationResult result = super.getNextMove(game);
+// if (Math.abs(result.getRow() - game.getPlayer().getHome()) > DEFENSE_DISTANCE_FROM_HOME) {
+// return null;
+// }
+//
+// return result;
}
@Override
public int getWeight(Game game) {
Collection minPawns = GameUtils.getMinPawns(game.getBoard(), game.getPlayer());
-
+ Collection maxPawns = GameUtils.getMaxPawns(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) {
+ if (PawnUtils.hasEasyWinPath(game.getBoard(), pawn)) {
+ if (maxPawns.stream().noneMatch(p -> PawnUtils.canCapture(game, p, pawn))) {
+ // Le pion ne peut pas être arrêté, on doit attaquer !
+ return 0;
+ } else {
+ return WEIGHT_MAX;
+ }
+ } else {
+ weight = 5;
+ }
}
if (distanceFromGoal > 2 && distanceFromGoal <= DEFENSE_DISTANCE_FROM_HOME) {
@@ -64,15 +78,17 @@ public class DefenseStrategy extends MiniMaxStrategy {
@Override
protected int evaluateSimulation() {
- for (SimulatedPawn pawn : getMinPawns()) {
+ int score = 0;
+
+ Collection maxPawns = getMaxPawns();
+ Collection minPawns = getMinPawns();
+
+ for (SimulatedPawn pawn : minPawns) {
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())
@@ -85,9 +101,65 @@ public class DefenseStrategy extends MiniMaxStrategy {
// On est toujours en danger
int distance = PawnUtils.distanceFromGoal(simulated.get());
score -= Math.pow(DEFENSE_DISTANCE_FROM_HOME - distance + 1, 3);
+
+ for (SimulatedPawn pawn : maxPawns) {
+ score += PAWN_DEFENSE_DISTANCE - distanceFromCapture(pawn, simulated.get()) * 5;
+ }
+ }
+ }
+
+ for (SimulatedPawn pawn : maxPawns) {
+ if (PawnUtils.canBeCaptured(game, pawn)) {
+ score -= 5;
+ }
+
+ // On préfère les groupes de 2 pushers, puisqu'ils ont une meilleure surface de défense
+ if (!pawn.isPusher()) {
+ continue;
+ }
+
+ int col = pawn.getCol();
+ int row = pawn.getRow();
+
+ if (col > 0 && PawnUtils.areSamePlayers(pawn, game.getBoard()[col - 1][row])) {
+ score += 5;
+ }
+ if (col < 7 && PawnUtils.areSamePlayers(pawn, game.getBoard()[col + 1][row])) {
+ score += 5;
}
}
return score;
}
+
+ @Override
+ protected Collection> getActions(boolean max) {
+ Collection pawns = max ? getMaxPawns() : getMinPawns();
+
+ long defensivePawnCount = pawns.stream()
+ .filter(IPawn::isPusher)
+ .filter(p -> p.getRow() != p.getPlayer().getHome())
+ .filter(p -> PawnUtils.distanceFromHome(p) <= DEFENSE_DISTANCE_FROM_HOME)
+ .count();
+
+ return PawnUtils.getActions(game, max, defensivePawnCount > 0);
+ }
+
+ private int distanceFromCapture(IPawn max, IPawn min) {
+ int rowDistance = Math.abs(min.getRow() - max.getRow());
+ int colDistance = Math.abs(min.getCol() - max.getCol());
+
+ if (colDistance > rowDistance) {
+ // Le pion ne pourra jamais être atteint
+ return PAWN_DEFENSE_DISTANCE;
+ }
+
+ if (!PawnUtils.canCapture(game, max, min, max.getRow(), max.getCol(), 0, rowDistance)) {
+ return PAWN_DEFENSE_DISTANCE;
+ }
+
+ return rowDistance;
+ }
+
+
}
diff --git a/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java b/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java
index c3b6d54..88d37bc 100644
--- a/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java
+++ b/src/main/java/laboratoire4/strategies/ImmediateDefenseStrategy.java
@@ -11,6 +11,7 @@ public class ImmediateDefenseStrategy implements Strategy {
@Override
public EvaluationResult getNextMove(Game game) {
+ // On utilise pas la méthode utilitaire, car on veut bouger la ligne de défense si nécessaire
for (IPawn pawn : GameUtils.getMaxPawns(game.getBoard(), game.getPlayer())) {
if (PawnUtils.distanceFromHome(pawn) > IMM_DEFENSE_DISTANCE_FROM_HOME) {
continue;
diff --git a/src/main/java/laboratoire4/strategies/MasterStrategy.java b/src/main/java/laboratoire4/strategies/MasterStrategy.java
index 209c619..e830f7e 100644
--- a/src/main/java/laboratoire4/strategies/MasterStrategy.java
+++ b/src/main/java/laboratoire4/strategies/MasterStrategy.java
@@ -13,8 +13,6 @@ public class MasterStrategy implements Strategy {
return instance;
}
- private boolean starting = true;
-
private MasterStrategy() {
}
@@ -35,8 +33,8 @@ public class MasterStrategy implements Strategy {
private Strategy getBestStrategy(Game game) {
Strategy[] strategies = new Strategy[]{
- new WinningStrategy(),
new ImmediateDefenseStrategy(),
+ new WinningStrategy(),
new DefenseStrategy(),
new AttackStrategy()
};
@@ -57,17 +55,10 @@ public class MasterStrategy implements Strategy {
}
if (maxWeight > 0) {
- starting = false;
return bestStrategy;
}
- //noinspection IfStatementWithIdenticalBranches
- if (starting) {
- return new StartingStrategy();
- }
-
- // TODO WN: Default strategy
- return new StartingStrategy();
+ return new RandomStrategy();
}
@Override
diff --git a/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java b/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java
index 9ea0270..7dfe213 100644
--- a/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java
+++ b/src/main/java/laboratoire4/strategies/MiniMaxStrategy.java
@@ -1,19 +1,18 @@
package laboratoire4.strategies;
import laboratoire4.Action;
+import laboratoire4.IPawn;
import laboratoire4.game.Game;
import laboratoire4.game.GameUtils;
import laboratoire4.game.SimulatedGame;
import laboratoire4.pawns.PawnMovement;
+import laboratoire4.pawns.PawnUtils;
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;
+ private static final int MAX_DEPTH = 6;
protected SimulatedGame game;
@@ -28,11 +27,11 @@ public abstract class MiniMaxStrategy implements Strategy {
EvaluationResult maxResult = null;
int maxScore = Integer.MIN_VALUE;
- for (Action action : getActions(true)) {
+ for (Action action : getActions(true)) {
int score = min(0, Integer.MIN_VALUE, Integer.MAX_VALUE);
if (maxResult == null || score > maxScore) {
- SimulatedPawn pawn = action.getPawn();
+ IPawn pawn = action.getPawn();
maxResult = new EvaluationResult(pawn.getRow(), pawn.getCol(), action.getMovement());
}
}
@@ -42,13 +41,13 @@ public abstract class MiniMaxStrategy implements Strategy {
private int max(int depth, int alpha, int beta) {
if (depth >= MAX_DEPTH) {
- return evaluateSimulation();
+ return evaluate();
}
int maxScore = Integer.MIN_VALUE;
- for (Action action : getActions(true)) {
- SimulatedPawn pawn = action.getPawn();
+ for (Action action : getActions(true)) {
+ IPawn pawn = action.getPawn();
PawnMovement movement = action.getMovement();
game.move(pawn, movement);
@@ -69,13 +68,13 @@ public abstract class MiniMaxStrategy implements Strategy {
private int min(int depth, int alpha, int beta) {
if (depth >= MAX_DEPTH) {
- return evaluateSimulation();
+ return evaluate();
}
int minScore = Integer.MAX_VALUE;
- for (Action action : getActions(false)) {
- SimulatedPawn pawn = action.getPawn();
+ for (Action action : getActions(false)) {
+ IPawn pawn = action.getPawn();
PawnMovement movement = action.getMovement();
game.move(pawn, movement);
@@ -94,23 +93,6 @@ public abstract class MiniMaxStrategy implements Strategy {
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());
}
@@ -119,5 +101,17 @@ public abstract class MiniMaxStrategy implements Strategy {
return GameUtils.getMinPawns(game.getBoard(), game.getPlayer());
}
+ protected Collection> getActions(boolean max) {
+ return PawnUtils.getActions(game, max);
+ }
+
+ private int evaluate() {
+ int score = evaluateSimulation();
+
+ // Logique générale
+
+ return score;
+ }
+
protected abstract int evaluateSimulation();
}
diff --git a/src/main/java/laboratoire4/strategies/StartingStrategy.java b/src/main/java/laboratoire4/strategies/RandomStrategy.java
similarity index 76%
rename from src/main/java/laboratoire4/strategies/StartingStrategy.java
rename to src/main/java/laboratoire4/strategies/RandomStrategy.java
index 3ef3968..3176f7f 100644
--- a/src/main/java/laboratoire4/strategies/StartingStrategy.java
+++ b/src/main/java/laboratoire4/strategies/RandomStrategy.java
@@ -14,18 +14,20 @@ 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;
+public class RandomStrategy implements Strategy {
+ private static final int PAWN_TO_MOVE = 2;
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())
+ Collection outsideHomePushers = GameUtils.getMaxPawnsAsStream(game.getBoard(), game.getPlayer())
+ .filter(IPawn::isPusher)
+ .filter(p -> p.getRow() != p.getPlayer().getHome())
+ .filter(p -> p.getRow() != p.getPlayer().getHome() + p.getDirection())
.collect(Collectors.toList());
- if (outsideHomePawns.size() > PAWN_TO_MOVE) {
- List> validActions = Strategy.getValidActions(game.getBoard(), outsideHomePawns);
+ if (outsideHomePushers.size() >= PAWN_TO_MOVE) {
+ List> validActions = Strategy.getValidActions(game.getBoard(), outsideHomePushers);
if (!validActions.isEmpty()) {
return getRandomMove(validActions);
}
diff --git a/src/main/java/laboratoire4/strategies/Strategy.java b/src/main/java/laboratoire4/strategies/Strategy.java
index b239c72..2163beb 100644
--- a/src/main/java/laboratoire4/strategies/Strategy.java
+++ b/src/main/java/laboratoire4/strategies/Strategy.java
@@ -12,6 +12,7 @@ public interface Strategy {
public static final int WEIGHT_MAX = 10;
EvaluationResult getNextMove(Game game);
+
int getWeight(Game game);
static List> getValidActions(Game game) {
@@ -25,9 +26,22 @@ public interface Strategy {
}
static List> getValidActions(IPawn[][] board, Collection pawns) {
+ return getValidActions(board, pawns, true);
+ }
+
+ static List> getValidActions(IPawn[][] board, Collection pawns, boolean excludeDefense) {
List> validActions = new ArrayList<>();
for (IPawn pawn : pawns) {
+ if (excludeDefense && pawn.getRow() == pawn.getPlayer().getHome()) {
+ int col = pawn.getCol();
+
+ // Si possible, on ne bouge pas ces pushers, comme ça on a une défense d'urgence totale
+ if (col == 1 || col == 2 || col == 5 || col == 6) {
+ continue;
+ }
+ }
+
for (PawnMovement movement : PawnMovement.values()) {
if (pawn.isMoveValid(board, movement)) {
validActions.add(new Action<>(pawn, movement));
@@ -35,6 +49,10 @@ public interface Strategy {
}
}
+ if (excludeDefense && validActions.isEmpty()) {
+ return getValidActions(board, pawns, false);
+ }
+
return validActions;
}
}
diff --git a/src/main/java/laboratoire4/strategies/WinningStrategy.java b/src/main/java/laboratoire4/strategies/WinningStrategy.java
index 0c8fc23..eabbb0a 100644
--- a/src/main/java/laboratoire4/strategies/WinningStrategy.java
+++ b/src/main/java/laboratoire4/strategies/WinningStrategy.java
@@ -5,15 +5,16 @@ import laboratoire4.IPawn;
import laboratoire4.game.Game;
import laboratoire4.game.GameUtils;
import laboratoire4.pawns.PawnMovement;
+import laboratoire4.pawns.PawnUtils;
import java.util.Collection;
import java.util.stream.Collectors;
public class WinningStrategy implements Strategy {
+ private Action winningAction;
+
@Override
public EvaluationResult getNextMove(Game game) {
- Action winningAction = getWinningAction(game);
-
if (winningAction == null) {
return null;
}
@@ -24,16 +25,20 @@ public class WinningStrategy implements Strategy {
@Override
public int getWeight(Game game) {
- Action winningAction = getWinningAction(game);
-
+ winningAction = findImmediateWinningAction(game);
if (winningAction == null) {
- return 0;
+ // On cherche un chemin où on peut gagner quoi qu'il arrive
+ winningAction = findWinningPath(game);
}
- return WEIGHT_MAX + 1;
+ if (winningAction != null) {
+ return WEIGHT_MAX;
+ }
+
+ return 0;
}
- private static Action getWinningAction(Game game) {
+ private static Action findImmediateWinningAction(Game game) {
Collection nearPawns = GameUtils.getMaxPawnsAsStream(game.getBoard(), game.getPlayer())
.filter(p -> p.getRow() + p.getDirection() == p.getPlayer().getGoal())
.collect(Collectors.toList());
@@ -41,11 +46,60 @@ public class WinningStrategy implements Strategy {
for (IPawn pawn : nearPawns) {
for (PawnMovement movement : PawnMovement.values()) {
if (pawn.isMoveValid(game.getBoard(), movement)) {
- return new Action(pawn, movement);
+ return new Action<>(pawn, movement);
}
}
}
return null;
}
+
+ private Action findWinningPath(Game game) {
+ for (IPawn pawn : GameUtils.getMaxPawns(game.getBoard(), game.getPlayer())) {
+ if (PawnUtils.distanceFromGoal(pawn) > 4) {
+ continue;
+ }
+
+ for (PawnMovement movement : PawnMovement.values()) {
+ int nextRow = pawn.getRow() + pawn.getDirection();
+ int nextCol = pawn.getCol() + movement.getMove();
+
+ if (pawn.isMoveValid(game.getBoard(), movement) &&
+ !PawnUtils.canBeCaptured(game, nextRow, nextCol, game.getPlayer()) &&
+ hasWinningPath(game, pawn, nextRow, nextCol)) {
+ return new Action<>(pawn, movement);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private boolean hasWinningPath(Game game, IPawn initialPawn, int row, int col) {
+ if (row == initialPawn.getPlayer().getGoal()) {
+ return true;
+ }
+
+ for (PawnMovement movement : PawnMovement.values()) {
+ int nextRow = row + initialPawn.getDirection();
+ int nextCol = col + movement.getMove();
+
+ if (!initialPawn.isMoveValid(game.getBoard(), movement, row, col)) {
+ continue;
+ }
+
+ IPawn nextPawn = game.getBoard()[nextRow][nextCol];
+ if (nextPawn == null && hasWinningPath(game, initialPawn, nextRow, nextCol)) {
+ return true;
+ }
+
+ if (nextPawn != null && !PawnUtils.areSamePlayers(initialPawn, nextPawn) &&
+ !PawnUtils.canBeCaptured(game, row, col, initialPawn.getPlayer()) &&
+ hasWinningPath(game, initialPawn, nextRow, nextCol)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}