Plus d'heuristiques
This commit is contained in:
parent
abf7f5bfd8
commit
5f7d74e0ae
|
@ -5,12 +5,14 @@
|
|||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="41395b4b-3252-492c-a869-5f4dab107186" name="Changes" comment="Fixes?">
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/BoardEvaluator.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MiniMaxResult.java" 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/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/Player.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Player.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/PusherBoard.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/PusherBoard.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/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/MovingPawn.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPawn.java" afterDir="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/MovingPusher.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPusher.java" afterDir="false" />
|
||||
</list>
|
||||
<list id="98b8a79f-2f53-42bf-96da-7af322697a0d" name="Changes by acastonguay" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
|
@ -50,22 +52,22 @@
|
|||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||
"codeWithMe.voiceChat.enabledByDefault": "false",
|
||||
"git-widget-placeholder": "william",
|
||||
"last_opened_file_path": "/home/william/Dev/Projects",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||
"codeWithMe.voiceChat.enabledByDefault": "false",
|
||||
"git-widget-placeholder": "william",
|
||||
"last_opened_file_path": "/home/william/Dev/Projects",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
}</component>
|
||||
<component name="RunManager" selected="Application.Main">
|
||||
<configuration name="Client" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
|
||||
<option name="MAIN_CLASS_NAME" value="laboratoire4.Client" />
|
||||
|
@ -132,6 +134,7 @@
|
|||
<workItem from="1679672719638" duration="8340000" />
|
||||
<workItem from="1679779362814" duration="5912000" />
|
||||
<workItem from="1679787823160" duration="2189000" />
|
||||
<workItem from="1680198283785" duration="8256000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="MiniMax">
|
||||
<created>1679263366439</created>
|
||||
|
|
|
@ -4,23 +4,26 @@ import java.util.Collection;
|
|||
|
||||
public class BoardEvaluator {
|
||||
public static int evaluate(MovingBoard game) {
|
||||
MovingPawn[][] board = game.getBoard();
|
||||
Collection<MovingPawn> maxPawns = game.getMaxPawns();
|
||||
Collection<MovingPawn> minPawns = game.getMinPawns();
|
||||
|
||||
return evaluatePlayer(board, maxPawns, minPawns) - evaluatePlayer(board, minPawns, maxPawns);
|
||||
return evaluatePlayer(game, maxPawns, minPawns, true) - evaluatePlayer(game, minPawns, maxPawns, false);
|
||||
}
|
||||
|
||||
//region Full Board
|
||||
private static int evaluatePlayer(MovingPawn[][] board, Collection<MovingPawn> maxPawns, Collection<MovingPawn> minPawns) {
|
||||
if (minPawns.size() == 0) {
|
||||
return Integer.MAX_VALUE;
|
||||
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(board, pawn);
|
||||
score += evaluatePawn(game, pawn);
|
||||
|
||||
if (max) {
|
||||
score += strategy(game, pawn);
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
|
@ -33,83 +36,195 @@ public class BoardEvaluator {
|
|||
// 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) {
|
||||
return (int) (0.001f * Math.pow(16 - pawns.size(), 4)) * 100;
|
||||
int capturedPusherCount = (int) (8 - pawns.stream().filter(IPawn::isPusher).count());
|
||||
return (int) (Math.pow(capturedPusherCount, 2));
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Board
|
||||
private static int evaluatePawn(MovingPawn[][] board, MovingPawn pawn) {
|
||||
private static int evaluatePawn(MovingBoard game, MovingPawn pawn) {
|
||||
if (pawn.getRow() == pawn.getPlayer().getGoal()) {
|
||||
return Integer.MAX_VALUE;
|
||||
return 50;
|
||||
}
|
||||
|
||||
// Favorise les pièces qui peuvent gagner au prochain tour
|
||||
if (pawn.getRow() + pawn.getDirection() == pawn.getPlayer().getGoal()) {
|
||||
return Integer.MAX_VALUE / 2;
|
||||
return 25;
|
||||
}
|
||||
|
||||
int score = 0;
|
||||
|
||||
score += evaluateHomePawnScore(pawn);
|
||||
score += evaluatePushedScore(pawn);
|
||||
score += evaluateValidMovesScore(board, pawn);
|
||||
score += evaluateAttackScore(board, 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() ? 50 : 0;
|
||||
return pawn.getRow() == pawn.getPlayer().getHome() ? 1 : 0;
|
||||
}
|
||||
|
||||
private static int evaluatePushedScore(MovingPawn pawn) {
|
||||
return pawn.isPusher() ? 0 : 50;
|
||||
return pawn.isPusher() ? 0 : 1;
|
||||
}
|
||||
|
||||
private static int evaluateValidMovesScore(MovingPawn[][] board, MovingPawn pawn) {
|
||||
private static int evaluatePusherScore(MovingBoard board, MovingPawn pawn) {
|
||||
if (!pawn.isPusher()) return 0;
|
||||
|
||||
int score = 0;
|
||||
|
||||
if (pawn.isMoveValid(board, Pawn.PawnMovement.LEFT_DIAGONAL)) {
|
||||
score += 50;
|
||||
for (Pawn.PawnMovement movement : Pawn.PawnMovement.values()) {
|
||||
MovingPawn nearPawn = board.getNextPawn(pawn, movement);
|
||||
if (MovingPawn.areSamePlayers(pawn, nearPawn) && !nearPawn.isPusher()) {
|
||||
score++;
|
||||
}
|
||||
if (pawn.isMoveValid(board, Pawn.PawnMovement.STRAIGHT)) {
|
||||
score += 50;
|
||||
}
|
||||
if (pawn.isMoveValid(board, Pawn.PawnMovement.RIGHT_DIAGONAL)) {
|
||||
score += 50;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private static int evaluateAttackScore(MovingPawn[][] board, MovingPawn pawn) {
|
||||
private static int evaluateValidMovesScore(MovingBoard game, MovingPawn pawn) {
|
||||
int score = 0;
|
||||
int row = pawn.getRow();
|
||||
int col = pawn.getCol();
|
||||
|
||||
if (pawn.isMoveValid(board, Pawn.PawnMovement.LEFT_DIAGONAL)) {
|
||||
MovingPawn destPawn = board[row + pawn.getDirection()][col - 1];
|
||||
|
||||
if (destPawn != null && destPawn.getPlayer() != pawn.getPlayer()) {
|
||||
score += 100;
|
||||
for (Pawn.PawnMovement movement : Pawn.PawnMovement.values()) {
|
||||
if (pawn.isMoveValid(game.getBoard(), movement)) {
|
||||
score++;
|
||||
}
|
||||
}
|
||||
return score * pawn.getMoveCount();
|
||||
}
|
||||
|
||||
if (pawn.isMoveValid(board, Pawn.PawnMovement.RIGHT_DIAGONAL)) {
|
||||
MovingPawn destPawn = board[row + pawn.getDirection()][col + 1];
|
||||
private static int evaluateAttackScore(MovingBoard game, MovingPawn pawn) {
|
||||
int score = 0;
|
||||
|
||||
if (destPawn != null && destPawn.getPlayer() != pawn.getPlayer()) {
|
||||
score += 100;
|
||||
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 *= 2;
|
||||
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
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ public class Client {
|
|||
|
||||
if (cmd == '5') {
|
||||
stopGame();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
|
|
@ -6,7 +6,8 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
|
||||
public class MiniMax {
|
||||
private static final int MAX_DEPTH = 4;
|
||||
private static final int MAX_MAX_DEPTH = 6;
|
||||
private static int MAX_DEPTH = 2;
|
||||
|
||||
public static MiniMaxResult miniMax(PusherBoard board) {
|
||||
long startMillis = System.currentTimeMillis();
|
||||
|
@ -15,6 +16,8 @@ public class MiniMax {
|
|||
|
||||
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);
|
||||
|
||||
|
@ -23,6 +26,7 @@ public class MiniMax {
|
|||
maxResult = new MiniMaxResult(score, pawn.getRow(), pawn.getCol(), action.getMovement());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long endMillis = System.currentTimeMillis();
|
||||
System.out.printf("%d ms\n", (endMillis - startMillis));
|
||||
|
@ -89,6 +93,11 @@ public class MiniMax {
|
|||
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)) {
|
||||
|
|
|
@ -41,6 +41,17 @@ public class MovingBoard {
|
|||
board[pawn.getRow()][pawn.getCol()] = pawn;
|
||||
}
|
||||
|
||||
public MovingPawn getNextPawn(IPawn pawn, Pawn.PawnMovement move) {
|
||||
int nextRow = pawn.getRow() + pawn.getDirection();
|
||||
int nextCol = pawn.getCol() + move.getMove();
|
||||
|
||||
if (nextCol < 0 || nextCol > 7) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return board[nextRow][nextCol];
|
||||
}
|
||||
|
||||
public MovingPawn[][] getBoard() {
|
||||
return board;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package laboratoire4;
|
|||
|
||||
public interface MovingPawn extends IPawn {
|
||||
void revertMove();
|
||||
int getMoveCount();
|
||||
|
||||
static MovingPawn from(Pawn pawn) {
|
||||
if (pawn instanceof Pusher) {
|
||||
|
@ -10,5 +11,9 @@ public interface MovingPawn extends IPawn {
|
|||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.Stack;
|
|||
|
||||
public class MovingPushed extends Pushed implements MovingPawn {
|
||||
private final Stack<PawnMovement> previousMoves = new Stack<>();
|
||||
private int moveCount = 0;
|
||||
|
||||
public MovingPushed(Player player, int row, int col) {
|
||||
super(player, row, col);
|
||||
|
@ -13,6 +14,7 @@ public class MovingPushed extends Pushed implements MovingPawn {
|
|||
public void move(PawnMovement movement) {
|
||||
super.move(movement);
|
||||
previousMoves.push(movement);
|
||||
moveCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,6 +22,11 @@ public class MovingPushed extends Pushed implements MovingPawn {
|
|||
PawnMovement move = previousMoves.pop();
|
||||
setRow(row - getDirection());
|
||||
setCol(col - move.getMove());
|
||||
moveCount--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMoveCount() {
|
||||
return moveCount;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.Stack;
|
|||
|
||||
public class MovingPusher extends Pusher implements MovingPawn {
|
||||
private final Stack<PawnMovement> previousMoves = new Stack<>();
|
||||
private int moveCount = 0;
|
||||
|
||||
public MovingPusher(Player player, int row, int col) {
|
||||
super(player, row, col);
|
||||
|
@ -13,6 +14,7 @@ public class MovingPusher extends Pusher implements MovingPawn {
|
|||
public void move(PawnMovement movement) {
|
||||
previousMoves.push(movement);
|
||||
super.move(movement);
|
||||
moveCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,5 +22,11 @@ public class MovingPusher extends Pusher implements MovingPawn {
|
|||
PawnMovement move = previousMoves.pop();
|
||||
setRow(row - getDirection());
|
||||
setCol(col - move.getMove());
|
||||
moveCount--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMoveCount() {
|
||||
return moveCount;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue