Compare commits

...

4 Commits

Author SHA1 Message Date
william bd0fc7edfd Fixes? 2023-03-24 14:36:13 -04:00
william 0b528baa8b AI 2023-03-22 10:15:27 -04:00
william 39051a8fdd Small fixes 2023-03-22 09:01:39 -04:00
William Nolin 0ff8d6f057 Merge pull request 'william' (#1) from william into master
Reviewed-on: #1
2023-03-21 18:08:13 -04:00
14 changed files with 428 additions and 226 deletions

View File

@ -8,7 +8,7 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_18" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

View File

@ -4,25 +4,23 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="41395b4b-3252-492c-a869-5f4dab107186" name="Changes" comment="Movement logic"> <list default="true" id="41395b4b-3252-492c-a869-5f4dab107186" name="Changes" comment="AI">
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Main.java" afterDir="false" /> <change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/IPawn.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingBoard.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPawn.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPushed.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MovingPusher.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Client.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Client.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Client.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Client.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/GameTree.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/GameTree.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/MiniMax.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MiniMax.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Pawn.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Pawn.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Pawn.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Pawn.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/Player.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Player.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Pushed.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Pushed.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Pushed.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Pushed.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Pusher.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Pusher.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Pusher.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/Pusher.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/PusherBoard.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/PusherBoard.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/Test.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/TestPusherBoard.java" beforeDir="false" />
</list>
<list id="98b8a79f-2f53-42bf-96da-7af322697a0d" name="Changes by acastonguay" comment="">
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/GameTree.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/GameTree.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/laboratoire4/MiniMax.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/laboratoire4/MiniMax.java" afterDir="false" />
</list> </list>
<list id="98b8a79f-2f53-42bf-96da-7af322697a0d" name="Changes by acastonguay" comment="" />
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@ -32,44 +30,17 @@
<option name="RECENT_TEMPLATES"> <option name="RECENT_TEMPLATES">
<list> <list>
<option value="Class" /> <option value="Class" />
<option value="Interface" />
</list> </list>
</option> </option>
</component> </component>
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY"> <option name="RECENT_BRANCH_BY_REPOSITORY">
<map> <map>
<entry key="$PROJECT_DIR$" value="alexis" /> <entry key="$PROJECT_DIR$" value="master" />
</map> </map>
</option> </option>
</component> </component>
<component name="LineStatusTrackerManager">
<file path="$PROJECT_DIR$/src/main/java/laboratoire4/MiniMax.java">
<ranges>
<range start1="2" end1="2" start2="2" end2="6" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="4" end1="4" start2="8" end2="9" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="5" end1="7" start2="10" end2="12" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="9" end1="13" start2="14" end2="19" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="14" end1="18" start2="20" end2="21" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="19" end1="21" start2="22" end2="25" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="22" end1="28" start2="26" end2="39" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="29" end1="31" start2="40" end2="50" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="32" end1="34" start2="51" end2="53" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="35" end1="39" start2="54" end2="56" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="42" end1="43" start2="59" end2="62" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="45" end1="46" start2="64" end2="67" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="47" end1="48" start2="68" end2="69" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
<range start1="49" end1="49" start2="70" end2="125" changelist="98b8a79f-2f53-42bf-96da-7af322697a0d" />
</ranges>
</file>
<file path="$PROJECT_DIR$/src/main/java/laboratoire4/GameTree.java">
<ranges>
<range start1="17" end1="17" start2="17" end2="19" changelist="98b8a79f-2f53-42bf-96da-7af322697a0d" />
<range start1="18" end1="19" start2="20" end2="21" changelist="98b8a79f-2f53-42bf-96da-7af322697a0d" />
<range start1="20" end1="20" start2="22" end2="24" changelist="98b8a79f-2f53-42bf-96da-7af322697a0d" />
<range start1="25" end1="25" start2="29" end2="37" changelist="41395b4b-3252-492c-a869-5f4dab107186" />
</ranges>
</file>
</component>
<component name="MacroExpansionManager"> <component name="MacroExpansionManager">
<option name="directoryName" value="x0367gi2" /> <option name="directoryName" value="x0367gi2" />
</component> </component>
@ -83,6 +54,9 @@
</MavenImportingSettings> </MavenImportingSettings>
</option> </option>
</component> </component>
<component name="ProblemsViewState">
<option name="selectedTabId" value="ProjectErrors" />
</component>
<component name="ProjectId" id="2NFN0RGJNNJqiDmt34ixVwkIwEm" /> <component name="ProjectId" id="2NFN0RGJNNJqiDmt34ixVwkIwEm" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true"> <component name="ProjectLevelVcsManager" settingsEditedManually="true">
<ConfirmationsSetting value="2" id="Add" /> <ConfirmationsSetting value="2" id="Add" />
@ -91,20 +65,21 @@
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent"><![CDATA[{ <component name="PropertiesComponent">{
"keyToString": { &quot;keyToString&quot;: {
"RunOnceActivity.OpenProjectViewOnStart": "true", &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
"RunOnceActivity.ShowReadmeOnStart": "true", &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
"SHARE_PROJECT_CONFIGURATION_FILES": "true", &quot;SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
"codeWithMe.voiceChat.enabledByDefault": "false", &quot;codeWithMe.voiceChat.enabledByDefault&quot;: &quot;false&quot;,
"last_opened_file_path": "/home/william/Dev/Projects", &quot;last_opened_file_path&quot;: &quot;/home/william/Dev/Projects&quot;,
"node.js.detected.package.eslint": "true", &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
"node.js.detected.package.tslint": "true", &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
"node.js.selected.package.eslint": "(autodetect)", &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
"node.js.selected.package.tslint": "(autodetect)", &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
"vue.rearranger.settings.migration": "true" &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
} }
}]]></component> }</component>
<component name="RunManager" selected="Application.Main"> <component name="RunManager" selected="Application.Main">
<configuration name="Client" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true"> <configuration name="Client" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="laboratoire4.Client" /> <option name="MAIN_CLASS_NAME" value="laboratoire4.Client" />
@ -167,6 +142,8 @@
<workItem from="1679344371350" duration="4138000" /> <workItem from="1679344371350" duration="4138000" />
<workItem from="1679365557789" duration="21000" /> <workItem from="1679365557789" duration="21000" />
<workItem from="1679424430928" duration="11764000" /> <workItem from="1679424430928" duration="11764000" />
<workItem from="1679593788411" duration="10753000" />
<workItem from="1679672719638" duration="7700000" />
</task> </task>
<task id="LOCAL-00001" summary="MiniMax"> <task id="LOCAL-00001" summary="MiniMax">
<created>1679263366439</created> <created>1679263366439</created>
@ -182,7 +159,14 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1679348475071</updated> <updated>1679348475071</updated>
</task> </task>
<option name="localTasksCounter" value="3" /> <task id="LOCAL-00003" summary="Small fixes">
<created>1679490100944</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1679490100944</updated>
</task>
<option name="localTasksCounter" value="4" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@ -191,6 +175,15 @@
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value="MiniMax" /> <MESSAGE value="MiniMax" />
<MESSAGE value="Movement logic" /> <MESSAGE value="Movement logic" />
<option name="LAST_COMMIT_MESSAGE" value="Movement logic" /> <MESSAGE value="Small fixes" />
<option name="LAST_COMMIT_MESSAGE" value="Small fixes" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<default-breakpoints>
<breakpoint enabled="true" type="CidrExceptionBreakpoint" />
<breakpoint enabled="true" type="java-exception" />
</default-breakpoints>
</breakpoint-manager>
</component> </component>
</project> </project>

View File

@ -65,7 +65,7 @@ public class Client {
return player == Player.RED; return player == Player.RED;
} }
private void play() throws IOException { private void play() throws IOException, InterruptedException {
byte[] aBuffer = new byte[16]; byte[] aBuffer = new byte[16];
int size = input.available(); int size = input.available();
if (size > 0) { if (size > 0) {
@ -76,6 +76,8 @@ public class Client {
board.move(previousMove); board.move(previousMove);
} }
Thread.sleep(500);
String nextMove = board.runNextMove(); String nextMove = board.runNextMove();
System.out.println("Prochain mouvement: " + nextMove); System.out.println("Prochain mouvement: " + nextMove);
@ -83,15 +85,18 @@ public class Client {
output.flush(); output.flush();
} }
private void handleInvalidMove() throws InterruptedException { private void handleInvalidMove() throws InterruptedException, IOException {
System.err.println("Mouvement invalide!"); System.err.println("Mouvement invalide!");
Thread.sleep(500); Thread.sleep(500);
board.printBoard();
PusherBoard.printBoard(board.getBoard());
// play();
} }
private void stopGame() throws IOException { private void stopGame() throws IOException {
System.out.println("GG well played!"); System.out.println("GG well played!");
socket.close(); // socket.close();
} }
} }

View File

@ -0,0 +1,13 @@
package laboratoire4;
public interface IPawn {
boolean isMoveValid(IPawn[][] board, Pawn.PawnMovement movement);
void move(Pawn.PawnMovement movement);
boolean isPusher();
int getDirection();
Player getPlayer();
int getRow();
int getCol();
void setRow(int row);
void setCol(int col);
}

View File

@ -1,91 +1,141 @@
package laboratoire4; package laboratoire4;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MiniMax { public class MiniMax {
private static final int MAX_DEPTH = 4; private static final int MAX_DEPTH = 1;
private static Random random = new Random();
public static MiniMaxResult miniMax(PusherBoard board) { public static MiniMaxResult miniMax(PusherBoard board) {
return miniMax(board, true, 0, Integer.MIN_VALUE); MovingBoard game = new MovingBoard(board.getBoard(), board.getPlayer());
return miniMax(game, true, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
} }
private static MiniMaxResult miniMax(PusherBoard board, boolean max, int depth, int alphaBeta) { private static MiniMaxResult miniMax(MovingBoard game, boolean max, int depth, int alpha, int beta) {
int limScore = max ? Integer.MIN_VALUE : Integer.MAX_VALUE; depth += 1;
List<Pawn> pawns = max ?
board.getMaxPawns() :
board.getMinPawns();
List<MiniMaxResult> results = new ArrayList<>(); if (max) {
return max(game, depth, alpha, beta);
}
for (Pawn pawn : pawns) { return min(game, depth, alpha, beta);
int originalRow = pawn.getRow(); }
int originalCol = pawn.getCol();
private static MiniMaxResult max(MovingBoard game, int depth, int alpha, int beta) {
int alphaT = Integer.MIN_VALUE;
MovingPawn lastPawn = null;
Pawn.PawnMovement lastMovement = null;
for (MovingPawn pawn : game.getMaxPawns()) {
for (Pawn.PawnMovement movement : Pawn.PawnMovement.values()) { for (Pawn.PawnMovement movement : Pawn.PawnMovement.values()) {
if (pawn.isMoveValid(board, movement)) { if (!pawn.isMoveValid(game.getBoard(), movement)) {
pawn.move(movement); continue;
int score = depth < MAX_DEPTH ?
miniMax(board, !max, depth + 1, limScore).getScore() :
evaluate(pawn, board);
score *= pawn.getDirection();
if ((max && score > limScore) ||
(!max && score < limScore)) {
limScore = score;
}
MiniMaxResult result = new MiniMaxResult(limScore, pawn, movement);
//elagage alphaBeta
if ((max && limScore >= alphaBeta) ||
(!max && limScore <= alphaBeta)) {
pawn.setRow(originalRow);
pawn.setCol(originalCol);
return result;
}
results.add(result);
} }
pawn.setRow(originalRow); lastPawn = pawn;
pawn.setCol(originalCol); lastMovement = movement;
game.move(pawn, movement);
int score = evaluate(pawn, game, depth);
if (depth < MAX_DEPTH) {
score = miniMax(game, false, depth, Math.max(alpha, alphaT), beta).getScore();
}
game.revertMove();
if (score > alphaT) {
alphaT = score;
}
if (alphaT >= beta) {
return new MiniMaxResult(alphaT, pawn.getRow(), pawn.getCol(), movement);
}
} }
} }
// Choisir aléatoirement if (lastPawn == null) {
int index = random.nextInt(results.size()); return new MiniMaxResult(alphaT, 0, 0, null);
return results.get(index); }
return new MiniMaxResult(alphaT, lastPawn.getRow(), lastPawn.getCol(), lastMovement);
} }
private static int evaluate(Pawn pawn, PusherBoard board) { private static MiniMaxResult min(MovingBoard game, int depth, int alpha, int beta) {
int score = didWin(pawn); int betaT = Integer.MAX_VALUE;
score = Math.max(score, didCapture(pawn, board)); MovingPawn lastPawn = null;
Pawn.PawnMovement lastMovement = null;
return score; for (MovingPawn pawn : game.getMinPawns()) {
for (Pawn.PawnMovement movement : Pawn.PawnMovement.values()) {
if (!pawn.isMoveValid(game.getBoard(), movement)) {
continue;
}
lastPawn = pawn;
lastMovement = movement;
game.move(pawn, movement);
int score = evaluate(pawn, game, depth);
if (depth < MAX_DEPTH) {
score = miniMax(game, true, depth, alpha, Math.min(betaT, beta)).getScore();
}
game.revertMove();
if (score < betaT) {
betaT = score;
}
if (betaT <= beta) {
return new MiniMaxResult(betaT, pawn.getRow(), pawn.getCol(), movement);
}
}
}
if (lastPawn == null) {
return new MiniMaxResult(betaT, 0, 0, null);
}
return new MiniMaxResult(betaT, lastPawn.getRow(), lastPawn.getCol(), lastMovement);
} }
private static int didWin(Pawn pawn) { private static int evaluate(IPawn pawn, MovingBoard game, int depth) {
int goal = pawn.getPlayer() == Player.RED ? 7 : 0; int score = didWin(pawn) + didCapture(pawn, game);
if (pawn.getRow() == goal) { score += pawn.isPusher() ? 0 : 5;
return Integer.MAX_VALUE;
int row = pawn.getRow();
int col = pawn.getCol();
int nextNextRow = row + pawn.getDirection() * 2;
// Attire nos pusher vers nos pushed
if (pawn.isPusher() && nextNextRow >= 0 && nextNextRow <= 7) {
for (Pawn.PawnMovement move : Pawn.PawnMovement.values()) {
int nextCol = col + move.getMove();
if (nextCol < 0 || nextCol > 7) {
continue;
}
IPawn nearPawn = game.getBoard()[nextNextRow][nextCol];
if (nearPawn != null && !nearPawn.isPusher() && nearPawn.getPlayer() == pawn.getPlayer()) {
score += 5;
}
}
}
return score * pawn.getDirection();
}
private static int didWin(IPawn pawn) {
if (pawn.getRow() == pawn.getPlayer().getGoal()) {
return 100;
} }
return 0; return 0;
} }
private static int didCapture(Pawn pawn, PusherBoard board) { private static int didCapture(IPawn pawn, MovingBoard game) {
Pawn capturedPawn = board.getBoard()[pawn.getRow()][pawn.getCol()]; IPawn capturedPawn = game.getBoard()[pawn.getRow()][pawn.getCol()];
if (capturedPawn != null) { if (capturedPawn != null && capturedPawn.getPlayer() != game.getPlayer()) {
if (capturedPawn.getPlayer() != pawn.getPlayer()) { return 500;
return 50;
}
} }
return 0; return 0;
@ -93,12 +143,14 @@ public class MiniMax {
static class MiniMaxResult { static class MiniMaxResult {
private final int score; private final int score;
private final Pawn pawn; private final int row;
private final int col;
private final Pawn.PawnMovement movement; private final Pawn.PawnMovement movement;
public MiniMaxResult(int score, Pawn pawn, Pawn.PawnMovement movement) { public MiniMaxResult(int score, int row, int col, Pawn.PawnMovement movement) {
this.score = score; this.score = score;
this.pawn = pawn; this.row = row;
this.col = col;
this.movement = movement; this.movement = movement;
} }
@ -106,8 +158,12 @@ public class MiniMax {
return score; return score;
} }
public Pawn getPawn() { public int getRow() {
return pawn; return row;
}
public int getCol() {
return col;
} }
public Pawn.PawnMovement getMovement() { public Pawn.PawnMovement getMovement() {
@ -118,7 +174,8 @@ public class MiniMax {
public String toString() { public String toString() {
return "MiniMaxResult{" + return "MiniMaxResult{" +
"score=" + score + "score=" + score +
", pawn=" + pawn + ", col=" + col +
", row=" + row +
", movement=" + movement + ", movement=" + movement +
'}'; '}';
} }

View File

@ -0,0 +1,86 @@
package laboratoire4;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Stack;
import java.util.function.Predicate;
public class MovingBoard {
private final MovingPawn[][] board;
private final Player player;
private final Stack<MovingPawn> removedPawns = new Stack<>();
private final Stack<MovingPawn> previousMovedPawns = new Stack<>();
public MovingBoard(Pawn[][] board, Player player) {
this.board = asMovingPawns(board);
this.player = player;
}
public void move(MovingPawn pawn, Pawn.PawnMovement movement) {
int toRow = pawn.getRow() + pawn.getDirection();
int toCol = pawn.getCol() + movement.getMove();
MovingPawn capturedPawn = board[toRow][toCol];
previousMovedPawns.push(pawn);
removedPawns.push(capturedPawn);
board[pawn.getRow()][pawn.getCol()] = null;
board[toRow][toCol] = pawn;
pawn.move(movement);
}
public void revertMove() {
MovingPawn pawn = previousMovedPawns.pop();
MovingPawn capturedPawn = removedPawns.pop();
board[pawn.getRow()][pawn.getCol()] = capturedPawn;
pawn.revertMove();
board[pawn.getRow()][pawn.getCol()] = pawn;
}
public MovingPawn[][] getBoard() {
return board;
}
public Player getPlayer() {
return player;
}
public Collection<MovingPawn> getMaxPawns() {
return getPawns(p -> p.getPlayer() == player);
}
public Collection<MovingPawn> getMinPawns() {
return getPawns(p -> p.getPlayer() != player);
}
private Collection<MovingPawn> getPawns(Predicate<MovingPawn> predicate) {
List<MovingPawn> pawns = new ArrayList<>();
for (MovingPawn[] row : board) {
for (MovingPawn pawn : row) {
if (pawn != null && predicate.test(pawn)) {
pawns.add(pawn);
}
}
}
return pawns;
}
public static MovingPawn[][] asMovingPawns(Pawn[][] board) {
MovingPawn[][] to = new MovingPawn[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]);
to[row][col] = pawn;
}
}
return to;
}
}

View File

@ -0,0 +1,14 @@
package laboratoire4;
public interface MovingPawn extends IPawn {
void revertMove();
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());
}
}

View File

@ -0,0 +1,25 @@
package laboratoire4;
import java.util.Stack;
public class MovingPushed extends Pushed implements MovingPawn {
private final Stack<PawnMovement> previousMoves = new Stack<>();
public MovingPushed(Player player, int row, int col) {
super(player, row, col);
}
@Override
public void move(PawnMovement movement) {
super.move(movement);
previousMoves.push(movement);
}
@Override
public void revertMove() {
PawnMovement move = previousMoves.pop();
setRow(row - getDirection());
setCol(col - move.getMove());
}
}

View File

@ -0,0 +1,24 @@
package laboratoire4;
import java.util.Stack;
public class MovingPusher extends Pusher implements MovingPawn {
private final Stack<PawnMovement> previousMoves = new Stack<>();
public MovingPusher(Player player, int row, int col) {
super(player, row, col);
}
@Override
public void move(PawnMovement movement) {
previousMoves.push(movement);
super.move(movement);
}
@Override
public void revertMove() {
PawnMovement move = previousMoves.pop();
setRow(row - getDirection());
setCol(col - move.getMove());
}
}

View File

@ -1,20 +1,18 @@
package laboratoire4; package laboratoire4;
public abstract class Pawn { public abstract class Pawn implements IPawn {
protected final Player player; protected final Player player;
protected int row; protected int row;
protected int col; protected int col;
protected int direction;
public Pawn(Player player, int row, int col) { public Pawn(Player player, int row, int col) {
this.player = player; this.player = player;
this.row = row; this.row = row;
this.col = col; this.col = col;
this.direction = player == Player.RED ? 1 : -1;
} }
public void move(PawnMovement movement) { public void move(PawnMovement movement) {
setRow(row + direction); setRow(row + player.getDirection());
setCol(col + movement.move); setCol(col + movement.move);
} }
@ -44,12 +42,10 @@ public abstract class Pawn {
} }
public int getDirection() { public int getDirection() {
return direction; return player.getDirection();
} }
public boolean isMoveValid(PusherBoard game, PawnMovement movement) { public boolean isMoveValid(IPawn[][] board, PawnMovement movement) {
Pawn[][] board = game.getBoard();
int nextRow = row + getDirection(); int nextRow = row + getDirection();
if (nextRow < 0 || nextRow >= board.length) { if (nextRow < 0 || nextRow >= board.length) {
return false; return false;
@ -60,10 +56,10 @@ public abstract class Pawn {
return false; return false;
} }
return isMoveValid(board, movement); return isMoveReallyValid(board, movement);
} }
protected abstract boolean isMoveValid(Pawn[][] board, PawnMovement movement); protected abstract boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement);
enum PawnMovement { enum PawnMovement {
STRAIGHT(0), STRAIGHT(0),

View File

@ -1,6 +1,22 @@
package laboratoire4; package laboratoire4;
public enum Player { public enum Player {
BLACK, BLACK(-1, 0),
RED RED(1, 7);
private int direction;
private int goal;
Player(int direction, int goal) {
this.direction = direction;
this.goal = goal;
}
public int getDirection() {
return direction;
}
public int getGoal() {
return goal;
}
} }

View File

@ -6,29 +6,31 @@ public class Pushed extends Pawn {
} }
@Override @Override
public boolean isMoveValid(Pawn[][] board, PawnMovement movement) { public boolean isPusher() {
Pawn pusher = null; return false;
Pawn to = board[row + direction][col + movement.getMove()]; }
@Override
public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement) {
int direction = getDirection();
IPawn pusher = null;
IPawn to = board[row + direction][col + movement.getMove()];
if (col > 0 && movement == PawnMovement.RIGHT_DIAGONAL) { if (col > 0 && movement == PawnMovement.RIGHT_DIAGONAL) {
pusher = board[row - direction][col - 1]; pusher = board[row - direction][col - movement.getMove()];
} else if (col < board.length - 1 && movement == PawnMovement.LEFT_DIAGONAL) { } else if (col < board.length - 1 && movement == PawnMovement.LEFT_DIAGONAL) {
pusher = board[row - direction][col + 1]; pusher = board[row - direction][col - movement.getMove()];
} else if (movement == PawnMovement.STRAIGHT) { } else if (movement == PawnMovement.STRAIGHT) {
pusher = board[row - direction][col]; pusher = board[row - direction][col];
} }
boolean pusherValid = pusher instanceof Pusher; boolean pusherValid = pusher != null && pusher.isPusher() && pusher.getPlayer() == player;
boolean destinationValid = to == null || to.player != this.player; boolean destinationValid = to == null || (movement != PawnMovement.STRAIGHT && to.getPlayer() != this.player);
return pusherValid && destinationValid; return pusherValid && destinationValid;
} }
@Override @Override
public String toString() { public String toString() {
return "Pushed{" + return String.format("Pushed{%s, %s}", player, getPosition());
player +
", " + col +
", " + row +
"} ";
} }
} }

View File

@ -6,22 +6,27 @@ public class Pusher extends Pawn {
} }
@Override @Override
public boolean isMoveValid(Pawn[][] board, PawnMovement movement) { public boolean isPusher() {
Pawn to = board[row + direction][col + movement.getMove()]; return true;
}
@Override
public boolean isMoveReallyValid(IPawn[][] board, PawnMovement movement) {
IPawn to = board[getRow() + getDirection()][getCol() + movement.getMove()];
if (to == null) { if (to == null) {
return true; return true;
} }
return to.player != this.player && movement != PawnMovement.STRAIGHT; if (to.getPlayer() == player) {
return false;
}
return movement != PawnMovement.STRAIGHT;
} }
@Override @Override
public String toString() { public String toString() {
return "Pusher{" + return String.format("Pusher{%s, %s}", player, getPosition());
player +
", " + col +
", " + row +
"} ";
} }
} }

View File

@ -1,15 +1,9 @@
package laboratoire4; package laboratoire4;
import java.util.ArrayList;
import java.util.List;
public class PusherBoard { public class PusherBoard {
private final Player player; private final Player player;
private Pawn[][] board; private Pawn[][] board;
private final List<Pawn> maxPawns = new ArrayList<>();
private final List<Pawn> minPawns = new ArrayList<>();
public PusherBoard(Player player, String[] boardValues) { public PusherBoard(Player player, String[] boardValues) {
this.player = player; this.player = player;
@ -31,12 +25,6 @@ public class PusherBoard {
pawn = new Pushed(pawnPlayer, row, col); pawn = new Pushed(pawnPlayer, row, col);
} }
if (pawnPlayer == player) {
maxPawns.add(pawn);
} else {
minPawns.add(pawn);
}
board[row][col] = pawn; board[row][col] = pawn;
} }
@ -50,88 +38,66 @@ public class PusherBoard {
public String runNextMove() { public String runNextMove() {
MiniMax.MiniMaxResult result = MiniMax.miniMax(this); MiniMax.MiniMaxResult result = MiniMax.miniMax(this);
Pawn pawn = result.getPawn(); Pawn pawn = board[result.getRow()][result.getCol()];
System.out.println(result.getScore());
String initialPosition = pawn.getPosition(); String initialPosition = pawn.getPosition();
move(pawn, result.getMovement()); move(pawn, result.getMovement());
String nextPosition = pawn.getPosition();
return initialPosition + "-" + pawn.getPosition(); return initialPosition + "-" + nextPosition;
} }
public void move(String move) { public void move(String move) {
//FORMAT ex : D2-D3
String[] split = move.trim().split(" - "); String[] split = move.trim().split(" - ");
move(split[0], split[1]); String from = split[0];
String to = split[1];
int fromCol = (int) from.charAt(0) - 65;
int fromRow = Integer.parseInt(String.valueOf(from.charAt(1))) - 1;
int toCol = (int) to.charAt(0) - 65;
Pawn.PawnMovement movement = Pawn.PawnMovement.from(toCol - fromCol);
move(fromRow, fromCol, movement);
} }
private void move(Pawn pawn, Pawn.PawnMovement movement) { public void move(int row, int col, Pawn.PawnMovement movement) {
move(pawn.getCol(), pawn.getRow(), pawn.getCol() + movement.getMove(), pawn.getRow() + pawn.getDirection()); Pawn pawn = board[row][col];
} if (pawn == null) {
public void move(String from, String to) {
//FORMAT ex : from {D2}, to {D3}
int from_col = (int) from.charAt(0) - 65;
int from_row = Integer.parseInt(String.valueOf(from.charAt(1))) - 1;
int to_col = (int) to.charAt(0) - 65;
int to_row = Integer.parseInt(String.valueOf(to.charAt(1))) - 1;
move(from_col, from_row, to_col, to_row);
}
public void move(int from_col, int from_row, int to_col, int to_row) {
if (!isValid(from_col, from_row, to_col)) {
return; return;
} }
Pawn pawn = board[from_row][from_col]; move(pawn, movement);
Pawn destPawn = board[to_row][to_col];
if (destPawn != null) {
if (destPawn.getPlayer() == Player.RED) {
maxPawns.remove(destPawn);
} else {
minPawns.remove(destPawn);
}
}
board[from_row][from_col] = null;
board[to_row][to_col] = pawn;
pawn.setRow(to_row);
pawn.setCol(to_col);
} }
private void move(Pawn pawn, Pawn.PawnMovement movement) {
// if (!pawn.isMoveValid(board, movement)) {
// return;
// }
private boolean isValid(int from_col, int from_row, int to_col) { int toRow = pawn.getRow() + pawn.getDirection();
Pawn pawn = getBoard()[from_row][from_col]; int toCol = pawn.getCol() + movement.getMove();
Pawn.PawnMovement move = Pawn.PawnMovement.from(to_col - from_col);
if (pawn == null) { board[pawn.getRow()][pawn.getCol()] = null;
return false; board[toRow][toCol] = pawn;
} pawn.move(movement);
}
return pawn.isMoveValid(this, move); public Player getPlayer() {
return player;
} }
public Pawn[][] getBoard() { public Pawn[][] getBoard() {
return board; return board;
} }
public List<Pawn> getMaxPawns() { public static void printBoard(Pawn[][] board) {
return maxPawns;
}
public List<Pawn> getMinPawns() {
return minPawns;
}
public void printBoard() {
for (int i = 7; i >= 0; i--) { for (int i = 7; i >= 0; i--) {
for (int j = 0; j < this.board.length; j++) { for (int j = 0; j < board.length; j++) {
if (this.board[i][j] != null) { if (board[i][j] != null) {
System.out.print(this.board[i][j] + " | "); System.out.print(board[i][j] + " | ");
} else { } else {
System.out.print(" | "); System.out.print(" | ");
} }
} }
System.out.println(); System.out.println();