From a81709ef84cd61e51801cc182d3161b99f8ff8d4 Mon Sep 17 00:00:00 2001 From: Quentin Legot Date: Sat, 20 Feb 2021 18:54:38 +0100 Subject: [PATCH] fixed many bugs + implemented negamax(not tested) --- .gitignore | 1 + src/othello/Main.java | 16 ++--- src/othello/Point.java | 11 +-- src/othello/State.java | 97 ++++++++++++++------------ src/othello/players/NegamaxPlayer.java | 26 +++++-- src/othello/players/Player.java | 12 ++-- src/othello/players/RandomPlayer.java | 20 ++++-- 7 files changed, 109 insertions(+), 74 deletions(-) diff --git a/.gitignore b/.gitignore index 66c5a93..7c6f4b2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .settings .idea *.iml +out/ \ No newline at end of file diff --git a/src/othello/Main.java b/src/othello/Main.java index b4f255c..be78382 100644 --- a/src/othello/Main.java +++ b/src/othello/Main.java @@ -1,27 +1,27 @@ package othello; -import java.util.ArrayList; - import othello.players.Player; import othello.players.RandomPlayer; public class Main { - + public static void main(String[] args) { - Player p1 = new RandomPlayer(); - Player p2 = new RandomPlayer(); + Player p1 = new RandomPlayer(1); + Player p2 = new RandomPlayer(-1); Player[][] board = initialize(p1, p2); State game = new State(board, p1, p2); System.out.println("joueur 1: " + p1); System.out.println("joueur 2: " + p2); while(!game.isOver()) { Player player = game.getCurrentPlayer(); - ArrayList> moves = game.getMove(player); System.out.println(game.toString()); - game = game.play(player.play(moves, game, player)); + game = game.play(player.play(game)); } - System.out.println("C'est " + game.getWinner() + " qui a gagné"); + System.out.println(game.toString()); + System.out.println(game.getWinner() + " a gagné la partie"); + System.out.println(game.getScore(p1)); + System.out.println(game.getScore(p2)); } public static Player[][] initialize(Player p1, Player p2){ diff --git a/src/othello/Point.java b/src/othello/Point.java index 905fb57..e0d2011 100644 --- a/src/othello/Point.java +++ b/src/othello/Point.java @@ -2,16 +2,17 @@ package othello; public class Point { - public int x; - public int y; + private int x; + private int y; - public Point(int x, int y) { + public Point(int y, int x) { this.x = x; this.y = y; } public boolean isJump(Point other) { - return Math.pow(other.x - this.x, 2) + Math.pow(other.y - this.y, 2) == 4; + double value = Math.pow(other.x - this.x, 2) + Math.pow(other.y - this.y, 2); + return value == 4 || value == 8; } public int getX(){ @@ -24,7 +25,7 @@ public class Point { @Override public String toString () { - return "(" + x + ", " + y + ")"; + return "(" + y + ", " + x + ")"; } } diff --git a/src/othello/State.java b/src/othello/State.java index 6cc05a5..e18315d 100644 --- a/src/othello/State.java +++ b/src/othello/State.java @@ -1,40 +1,45 @@ package othello; -import java.util.ArrayList; - import othello.players.Player; +import java.util.LinkedList; +import java.util.List; + public class State { - private Player[][] board; - private Player player1; - private Player player2; + public static List previousSituations = new LinkedList<>(); + + private final Player[][] board; + private final Player player1; + private final Player player2; private Player currentPlayer; private int n1; private int n2; - public State(Player[][] board, Player p1, Player p2, int n1, int n2) { + public State(Player[][] board, Player p1, Player p2) { this.board = board; this.player1 = p1; this.player2 = p2; currentPlayer = p1; - this.n1 = n1; - this.n2 = n2; - } - - public State(Player[][] board, Player p1, Player p2) { - this(board, p1, p2, 2, 2); + this.n1 = 2; + this.n2 = 2; } public boolean isOver() { - if(n1 == 0 || n2 == 0) - return true; - return getMove(player1).isEmpty() && getMove(player2).isEmpty(); + return n1 == 0 || n2 == 0 || (getMove(player1).isEmpty() && getMove(player2).isEmpty()); + } + + public Player getPlayerById(int id) { + if(id == 1) + return player1; + else if(id == -1) + return player2; + throw new IllegalArgumentException("Invalid player id: " + id); } - public ArrayList> getMove(Player player) { + public LinkedList> getMove(Player player) { // Pair - ArrayList> moves = new ArrayList<>(); + LinkedList> moves = new LinkedList<>(); // Parcours du plateau de jeu for (int y = 0; y < this.board.length; y++) { for (int x = 0; x < this.board[y].length; x++) { @@ -43,19 +48,16 @@ public class State { for (int deltaY = -1; deltaY < 2; deltaY++) { for (int deltaX = -1; deltaX < 2; deltaX++) { // La position du pion trouvée est exclue - if (deltaY != 0 && deltaX != 0) { // Si une place libre est trouvée elle est ajoutée à la liste des coups try { + Point current = new Point(y, x); if (this.board[y+deltaY][x+deltaX] == null) { - moves.add(new Pair(new Point(y, x), new Point(y+deltaY, x+deltaX))); - } else { - Point current = new Point(y, x); - Point other = new Point(y + 2 * deltaY, x + 2 * deltaX); - if(this.board[y+2*deltaY][x+2*deltaX] == null && current.isJump(other)) - moves.add(new Pair(current, other)); + moves.add(new Pair<>(current, new Point(y + deltaY, x + deltaX))); } + Point other = new Point(y + 2 * deltaY, x + 2 * deltaX); + if(this.board[other.getY()][other.getX()] == null && current.isJump(other)) + moves.add(new Pair<>(current, other)); } catch(ArrayIndexOutOfBoundsException ignored) {} - } } } } @@ -65,7 +67,7 @@ public class State { } public int getScore(Player player) { - return player == player1 ? n1/(n1+n2) : n2/(n2+n1); + return player == player1 ? n1 : n2; } public Player getWinner() { @@ -77,25 +79,32 @@ public class State { return null; } - public State play(Pair pair) { + public State play(Pair move) { State copy = this.copy(); - copy.board[pair.getLeft().getX()][pair.getLeft().getY()] = copy.getCurrentPlayer(); - int increment = 0; + copy.board[move.getRight().getY()][move.getRight().getX()] = copy.getCurrentPlayer(); for(int i = -1; i < 2; i++){ for(int z = -1; z < 2; z++){ try { - if(copy.board[pair.getLeft().getX() + i][pair.getLeft().getY() + z] != copy.getCurrentPlayer()){ - increment++; - copy.board[pair.getLeft().getX() + i][pair.getLeft().getY() + z] = copy.getCurrentPlayer(); + if(copy.board[move.getRight().getY() + i][move.getRight().getX() + z] != copy.getCurrentPlayer() + && !move.getLeft().isJump(move.getRight())){ + copy.board[move.getRight().getY() + i][move.getRight().getX() + z] = copy.getCurrentPlayer(); } } catch (IndexOutOfBoundsException ignored) {} } } - if (copy.currentPlayer == player1) - copy.n1 += increment; - else - copy.n2 += increment; - + int ni = 0, nj = 0; + for(int i = 0; i < board.length; i++) { + for(int j = 0; j < board[i].length; j++) { + if(board[i][j] == player1){ + ni++; + } + if(board[i][j] == player2){ + nj++; + } + } + } + copy.n1 = ni; + copy.n2 = nj; copy.switchPlayer(); return copy; } @@ -108,13 +117,13 @@ public class State { } public State copy () { - State copy = new State(this.board, this.player1, this.player2, this.n1, this.n2); + State copy = new State(this.board, this.player1, this.player2); for (int i = 0; i < this.board.length; i++) { - for (int j = 0; j < this.board.length; j++) { - copy.board[i][j] = this.board[i][j]; - } + System.arraycopy(this.board[i], 0, copy.board[i], 0, this.board.length); } copy.setCurrentPlayer(this.currentPlayer); + copy.n1 = n1; + copy.n2 = n2; return copy; } @@ -128,11 +137,11 @@ public class State { @Override public String toString() { StringBuilder str = new StringBuilder(); - for (int y = 0; y < board.length; y++) { + for (Player[] players : board) { for (int x = 0; x < board.length; x++) { - if(board[y][x] == player1) + if (players[x] == player1) str.append("O"); - else if(board[y][x] == player2) + else if (players[x] == player2) str.append("X"); else str.append("."); diff --git a/src/othello/players/NegamaxPlayer.java b/src/othello/players/NegamaxPlayer.java index f49d694..af98f46 100644 --- a/src/othello/players/NegamaxPlayer.java +++ b/src/othello/players/NegamaxPlayer.java @@ -4,17 +4,29 @@ import othello.Pair; import othello.Point; import othello.State; -import java.util.ArrayList; +public class NegamaxPlayer extends Player { -public class NegamaxPlayer implements Player { - - @Override - public Pair play(ArrayList> moves, State game, Player player) { - return null; + public NegamaxPlayer(int id) { + super(id); } - public void negamax(State game, int depth, Player player) { + @Override + public Pair play(State game) { + return game.getMove(this).get(negamax(game, 10, this.id)); + } + private int negamax(State game, int depth, int id) { + if(depth == 0 || game.isOver()) { + return id; + } + int value = Integer.MIN_VALUE; + Player player = game.getPlayerById(id); + for(Pair move : game.getMove(player)) { + game = game.play(move); + game.setCurrentPlayer(player); + value = Math.max(value, -negamax(game, depth - 1, -id)); + } + return value; } } diff --git a/src/othello/players/Player.java b/src/othello/players/Player.java index e9880ee..41f68eb 100644 --- a/src/othello/players/Player.java +++ b/src/othello/players/Player.java @@ -1,13 +1,17 @@ package othello.players; -import java.util.ArrayList; - import othello.Pair; import othello.Point; import othello.State; -public interface Player { +public abstract class Player { - public Pair play(ArrayList> moves, State board, Player player); + protected final int id; + + public Player(int id) { + this.id = id; + } + + public abstract Pair play(State board); } diff --git a/src/othello/players/RandomPlayer.java b/src/othello/players/RandomPlayer.java index 88c8764..4560640 100644 --- a/src/othello/players/RandomPlayer.java +++ b/src/othello/players/RandomPlayer.java @@ -1,17 +1,25 @@ package othello.players; -import java.util.ArrayList; -import java.util.Random; - import othello.Pair; import othello.Point; import othello.State; -public class RandomPlayer implements Player { +import java.util.LinkedList; +import java.util.Random; + +public class RandomPlayer extends Player { + + Random random; + + public RandomPlayer(int id) { + super(id); + random = new Random(); + } @Override - public Pair play(ArrayList> moves, State game, Player player) { - return moves.get(new Random().nextInt(moves.size())); + public Pair play(State game) { + LinkedList> moves = game.getMove(this); + return moves.get(random.nextInt(moves.size())); } }