diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 58f9c6e..854915f 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,11 +4,23 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -49,7 +96,8 @@ "RunOnceActivity.OpenProjectViewOnStart": "true", "RunOnceActivity.ShowReadmeOnStart": "true", "SHARE_PROJECT_CONFIGURATION_FILES": "true", - "last_opened_file_path": "/home/william/Dev/Projects/Uni/Log320/LOG320_Lab4", + "codeWithMe.voiceChat.enabledByDefault": "false", + "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)", @@ -57,7 +105,7 @@ "vue.rearranger.settings.migration": "true" } }]]> - + + + + + + + @@ -86,11 +162,35 @@ + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index bf36b1a..848b6ab 100644 --- a/pom.xml +++ b/pom.xml @@ -9,8 +9,8 @@ 1.0-SNAPSHOT - 18 - 18 + 11 + 11 UTF-8 diff --git a/src/main/java/laboratoire4/Client.java b/src/main/java/laboratoire4/Client.java index 1fdba45..8ee65da 100644 --- a/src/main/java/laboratoire4/Client.java +++ b/src/main/java/laboratoire4/Client.java @@ -1,124 +1,97 @@ package laboratoire4; -import java.io.*; -import java.net.*; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.net.Socket; -class Client { - public static void main(String[] args) { +public class Client { + private final Socket socket; + private final BufferedInputStream input; + private final BufferedOutputStream output; + private PusherBoard board; - Socket MyClient; - BufferedInputStream input; - BufferedOutputStream output; - int[][] board = new int[8][8]; + public Client(String host, int port) throws IOException { + socket = new Socket(host, port); + input = new BufferedInputStream(socket.getInputStream()); + output = new BufferedOutputStream(socket.getOutputStream()); + } + public void listen() { try { - MyClient = new Socket("localhost", 8888); + while (true) { + char cmd; + cmd = (char) input.read(); - input = new BufferedInputStream(MyClient.getInputStream()); - output = new BufferedOutputStream(MyClient.getOutputStream()); - BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); - while(1 == 1){ - char cmd = 0; - - cmd = (char)input.read(); - System.out.println(cmd); - // Debut de la partie en joueur blanc - if(cmd == '1'){ - byte[] aBuffer = new byte[1024]; - - int size = input.available(); - //System.out.println("size " + size); - input.read(aBuffer,0,size); - String s = new String(aBuffer).trim(); - System.out.println(s); - String[] boardValues; - boardValues = s.split(" "); - int x=0,y=0; - for(int i=0; i 0) { + input.read(aBuffer, 0, size); + String previousMove = new String(aBuffer); + System.out.println("Mouvement reçu: " + previousMove); + + board.move(previousMove); } + String nextMove = board.runNextMove(); + System.out.println("Prochain mouvement: " + nextMove); + + output.write(nextMove.getBytes(), 0, nextMove.length()); + output.flush(); + } + + private void handleInvalidMove() throws InterruptedException { + System.err.println("Mouvement invalide!"); + Thread.sleep(500); + board.printBoard(); + } + + private void stopGame() throws IOException { + System.out.println("GG well played!"); + socket.close(); } } diff --git a/src/main/java/laboratoire4/GameTree.java b/src/main/java/laboratoire4/GameTree.java index 0502068..4c8d46d 100644 --- a/src/main/java/laboratoire4/GameTree.java +++ b/src/main/java/laboratoire4/GameTree.java @@ -5,19 +5,36 @@ import java.util.Collection; public class GameTree { private Node root; + public Node buildTree(PusherBoard board){ + return null; + } + public Node getRoot() { return root; } static class Node { private final Collection childs; + private final Pawn pawn; + private final Pawn.PawnMovement movement; - Node(Collection childs) { + Node(Collection childs, Pawn pawn, Pawn.PawnMovement movement) { this.childs = childs; + this.pawn = pawn; + this.movement = movement; } public Collection getChilds() { return childs; } + + public Pawn getPawn() { + return pawn; + } + + public Pawn.PawnMovement getMovement() { + return movement; + } } + } diff --git a/src/main/java/laboratoire4/Main.java b/src/main/java/laboratoire4/Main.java new file mode 100644 index 0000000..b27a322 --- /dev/null +++ b/src/main/java/laboratoire4/Main.java @@ -0,0 +1,9 @@ +package laboratoire4; + +import java.io.IOException; + +public class Main { + public static void main(String[] args) throws IOException { + new Client("localhost", 8888).listen(); + } +} diff --git a/src/main/java/laboratoire4/MiniMax.java b/src/main/java/laboratoire4/MiniMax.java index 91c7028..2dc1bad 100644 --- a/src/main/java/laboratoire4/MiniMax.java +++ b/src/main/java/laboratoire4/MiniMax.java @@ -1,49 +1,126 @@ package laboratoire4; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + public class MiniMax { private static final int MAX_DEPTH = 4; + private static Random random = new Random(); - public static int miniMax(GameTree tree) { - return miniMax(tree.getRoot(), Player.MAX, 0); + public static MiniMaxResult miniMax(PusherBoard board) { + return miniMax(board, true, 0, Integer.MIN_VALUE); } - private static int miniMax(GameTree.Node node, Player player, int depth) { - if (depth == MAX_DEPTH) { - return evaluate(node); - } + private static MiniMaxResult miniMax(PusherBoard board, boolean max, int depth, int alphaBeta) { + int limScore = max ? Integer.MIN_VALUE : Integer.MAX_VALUE; + List pawns = max ? + board.getMaxPawns() : + board.getMinPawns(); - return player == Player.MAX ? - max(node, depth) : - min(node, depth); - } + List results = new ArrayList<>(); - private static int max(GameTree.Node node, int depth) { - int maxScore = Integer.MIN_VALUE; + for (Pawn pawn : pawns) { + int originalRow = pawn.getRow(); + int originalCol = pawn.getCol(); - for (GameTree.Node child : node.getChilds()) { - int score = miniMax(child, Player.MIN, depth + 1); - if (score > maxScore) { - maxScore = score; + for (Pawn.PawnMovement movement : Pawn.PawnMovement.values()) { + if (pawn.isMoveValid(board, movement)) { + pawn.move(movement); + + 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); + pawn.setCol(originalCol); } } - return maxScore; + // Choisir aléatoirement + int index = random.nextInt(results.size()); + return results.get(index); } - private static int min(GameTree.Node node, int depth) { - int minScore = Integer.MAX_VALUE; + private static int evaluate(Pawn pawn, PusherBoard board) { + int score = didWin(pawn); + score = Math.max(score, didCapture(pawn, board)); - for (GameTree.Node child : node.getChilds()) { - int score = miniMax(child, Player.MIN, depth + 1); - if (score < minScore) { - minScore = score; + return score; + } + + private static int didWin(Pawn pawn) { + int goal = pawn.getPlayer() == Player.RED ? 7 : 0; + + if (pawn.getRow() == goal) { + return Integer.MAX_VALUE; + } + + return 0; + } + + private static int didCapture(Pawn pawn, PusherBoard board) { + Pawn capturedPawn = board.getBoard()[pawn.getRow()][pawn.getCol()]; + + if (capturedPawn != null) { + if (capturedPawn.getPlayer() != pawn.getPlayer()) { + return 50; } } - return minScore; + return 0; } - private static int evaluate(GameTree.Node node) { + static class MiniMaxResult { + private final int score; + private final Pawn pawn; + private final Pawn.PawnMovement movement; + public MiniMaxResult(int score, Pawn pawn, Pawn.PawnMovement movement) { + this.score = score; + this.pawn = pawn; + this.movement = movement; + } + + public int getScore() { + return score; + } + + public Pawn getPawn() { + return pawn; + } + + public Pawn.PawnMovement getMovement() { + return movement; + } + + @Override + public String toString() { + return "MiniMaxResult{" + + "score=" + score + + ", pawn=" + pawn + + ", movement=" + movement + + '}'; + } } } diff --git a/src/main/java/laboratoire4/Pawn.java b/src/main/java/laboratoire4/Pawn.java new file mode 100644 index 0000000..bd6b8d9 --- /dev/null +++ b/src/main/java/laboratoire4/Pawn.java @@ -0,0 +1,93 @@ +package laboratoire4; + +public abstract class Pawn { + protected final Player player; + protected int row; + protected int col; + protected int direction; + + public Pawn(Player player, int row, int col) { + this.player = player; + this.row = row; + this.col = col; + this.direction = player == Player.RED ? 1 : -1; + } + + public void move(PawnMovement movement) { + setRow(row + direction); + setCol(col + movement.move); + } + + public String getPosition() { + char colStr = (char)(col + 65); + return String.format("%s%d", colStr, row + 1); + } + + public Player getPlayer() { + return player; + } + + public int getCol() { + return col; + } + + public int getRow() { + return row; + } + + public void setCol(int col) { + this.col = col; + } + + public void setRow(int row) { + this.row = row; + } + + public int getDirection() { + return direction; + } + + public boolean isMoveValid(PusherBoard game, PawnMovement movement) { + Pawn[][] board = game.getBoard(); + + int nextRow = row + getDirection(); + if (nextRow < 0 || nextRow >= board.length) { + return false; + } + + int nextCol = col + movement.move; + if (nextCol < 0 || nextCol >= board.length) { + return false; + } + + return isMoveValid(board, movement); + } + + protected abstract boolean isMoveValid(Pawn[][] 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; + } + } +} diff --git a/src/main/java/laboratoire4/Player.java b/src/main/java/laboratoire4/Player.java index 99a2b53..a94952b 100644 --- a/src/main/java/laboratoire4/Player.java +++ b/src/main/java/laboratoire4/Player.java @@ -1,6 +1,6 @@ package laboratoire4; public enum Player { - MIN, - MAX + BLACK, + RED } diff --git a/src/main/java/laboratoire4/Pushed.java b/src/main/java/laboratoire4/Pushed.java new file mode 100644 index 0000000..927c490 --- /dev/null +++ b/src/main/java/laboratoire4/Pushed.java @@ -0,0 +1,34 @@ +package laboratoire4; + +public class Pushed extends Pawn { + public Pushed(Player player, int row, int col) { + super(player, row, col); + } + + @Override + public boolean isMoveValid(Pawn[][] board, PawnMovement movement) { + Pawn pusher = null; + Pawn to = board[row + direction][col + movement.getMove()]; + + if (col > 0 && movement == PawnMovement.RIGHT_DIAGONAL) { + pusher = board[row - direction][col - 1]; + } else if (col < board.length - 1 && movement == PawnMovement.LEFT_DIAGONAL) { + pusher = board[row - direction][col + 1]; + } else if (movement == PawnMovement.STRAIGHT) { + pusher = board[row - direction][col]; + } + + boolean pusherValid = pusher instanceof Pusher; + boolean destinationValid = to == null || to.player != this.player; + return pusherValid && destinationValid; + } + + @Override + public String toString() { + return "Pushed{" + + player + + ", " + col + + ", " + row + + "} "; + } +} diff --git a/src/main/java/laboratoire4/Pusher.java b/src/main/java/laboratoire4/Pusher.java new file mode 100644 index 0000000..01e4fbc --- /dev/null +++ b/src/main/java/laboratoire4/Pusher.java @@ -0,0 +1,27 @@ +package laboratoire4; + +public class Pusher extends Pawn { + public Pusher(Player player, int row, int col) { + super(player, row, col); + } + + @Override + public boolean isMoveValid(Pawn[][] board, PawnMovement movement) { + Pawn to = board[row + direction][col + movement.getMove()]; + + if (to == null) { + return true; + } + + return to.player != this.player && movement != PawnMovement.STRAIGHT; + } + + @Override + public String toString() { + return "Pusher{" + + player + + ", " + col + + ", " + row + + "} "; + } +} diff --git a/src/main/java/laboratoire4/PusherBoard.java b/src/main/java/laboratoire4/PusherBoard.java new file mode 100644 index 0000000..b2ebf07 --- /dev/null +++ b/src/main/java/laboratoire4/PusherBoard.java @@ -0,0 +1,141 @@ +package laboratoire4; + +import java.util.ArrayList; +import java.util.List; + +public class PusherBoard { + private final Player player; + private Pawn[][] board; + + private final List maxPawns = new ArrayList<>(); + private final List minPawns = new ArrayList<>(); + + public PusherBoard(Player player, String[] boardValues) { + this.player = player; + + newGame(boardValues); + } + + public void newGame(String[] boardValues) { + this.board = new Pawn[8][8]; + + int col = 0, row = 0; + for (String boardValue : boardValues) { + int v = Integer.parseInt(boardValue); + if (v != 0) { + Player pawnPlayer = (v == 1 || v == 2) ? Player.RED : Player.BLACK; + Pawn pawn; + if (v % 2 == 0) { // 2 et 4 sont les pushers + pawn = new Pusher(pawnPlayer, row, col); + } else { + pawn = new Pushed(pawnPlayer, row, col); + } + + if (pawnPlayer == player) { + maxPawns.add(pawn); + } else { + minPawns.add(pawn); + } + + board[row][col] = pawn; + } + + col++; + if (col == board.length) { + col = 0; + row++; + } + } + } + + public String runNextMove() { + MiniMax.MiniMaxResult result = MiniMax.miniMax(this); + Pawn pawn = result.getPawn(); + String initialPosition = pawn.getPosition(); + + move(pawn, result.getMovement()); + + return initialPosition + "-" + pawn.getPosition(); + } + + public void move(String move) { + //FORMAT ex : D2-D3 + String[] split = move.trim().split(" - "); + move(split[0], split[1]); + } + + private void move(Pawn pawn, Pawn.PawnMovement movement) { + move(pawn.getCol(), pawn.getRow(), pawn.getCol() + movement.getMove(), pawn.getRow() + pawn.getDirection()); + } + + 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; + } + + Pawn pawn = board[from_row][from_col]; + 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 boolean isValid(int from_col, int from_row, int to_col) { + Pawn pawn = getBoard()[from_row][from_col]; + Pawn.PawnMovement move = Pawn.PawnMovement.from(to_col - from_col); + + if (pawn == null) { + return false; + } + + return pawn.isMoveValid(this, move); + } + + public Pawn[][] getBoard() { + return board; + } + + public List getMaxPawns() { + return maxPawns; + } + + public List getMinPawns() { + return minPawns; + } + + public void printBoard() { + for (int i = 7; i >= 0; i--) { + for (int j = 0; j < this.board.length; j++) { + if (this.board[i][j] != null) { + System.out.print(this.board[i][j] + " | "); + } else { + System.out.print(" | "); + } + } + System.out.println(); + System.out.println("----------------------------------------------------------------------------------------"); + } + } +}