From d59698354d5c1cdb874cc64676aebbf926f19149 Mon Sep 17 00:00:00 2001 From: Sailesh Date: Mon, 17 Apr 2017 21:13:18 -0400 Subject: [PATCH 1/2] Implement variable depth alpha beta pruning --- src/CheckersAI.java | 40 +++++++++++++++++++++------ src/CheckersGameState3.java | 55 +++++++++++++++++++++++++++++++++++-- src/Move.java | 3 ++ src/Move3.java | 4 +++ src/RmCheckersClient.java | 3 +- 5 files changed, 92 insertions(+), 13 deletions(-) diff --git a/src/CheckersAI.java b/src/CheckersAI.java index e728930..f761434 100644 --- a/src/CheckersAI.java +++ b/src/CheckersAI.java @@ -9,9 +9,31 @@ public class CheckersAI{ this.player = player; } - public Move minimax(CheckersGameState s, int ply){ + private boolean stop(CheckersGameState state, boolean jumped, int depth){ + CheckersGameState3 s = (CheckersGameState3) state; + if(!s.canJump() && !jumped && !s.canExchange() && depth == 3){ + return true; + } + else if(!s.canJump() && !s.canExchange() && depth == 4){ + return true; + } + else if(depth < 11 && !s.canJump()){ + return true; + } + else if(depth < 20 && s.twoKings()){ + return true; + } + else if(depth >= 20){ + return true; + } + else{ + return false; + } + } + + public Move minimax(CheckersGameState s){ int depth = 0; - if(s.isTerminal() || depth == ply){ + if(s.isTerminal()){ return null; } double alpha = Double.NEGATIVE_INFINITY; @@ -21,7 +43,7 @@ public class CheckersAI{ Move max = null; // System.out.println(s.actions().size()); for(Move a: s.actions()){ - check = minValue(s.result(a), alpha, beta, ply, depth + 1); + check = minValue(s.result(a), alpha, beta, depth + 1, a.isJump()); if(check > v){ v = check; max = a; @@ -34,15 +56,15 @@ public class CheckersAI{ return max; } - private double maxValue(CheckersGameState s, double alpha, double beta, int ply, int depth){ + private double maxValue(CheckersGameState s, double alpha, double beta, int depth, boolean jumped){ - if(s.isTerminal() || depth == ply){ + if(s.isTerminal() || stop(s, jumped, depth)){ return eval.evaluate(s, this.player); // if terminal, piece ratio should be infinite } double v = Double.NEGATIVE_INFINITY; double check; for(Move a: s.actions()){ - check = minValue(s.result(a), alpha, beta, ply, depth + 1); + check = minValue(s.result(a), alpha, beta, depth + 1, a.isJump()); if(check > v){ v = check; } @@ -54,15 +76,15 @@ public class CheckersAI{ return v; } - private double minValue(CheckersGameState s, double alpha, double beta, int ply, int depth){ + private double minValue(CheckersGameState s, double alpha, double beta, int depth, boolean jumped){ - if(s.isTerminal() || depth == ply){ + if(s.isTerminal() || stop(s, jumped, depth)){ return eval.evaluate(s, this.player); } double v = Double.POSITIVE_INFINITY; double check; for(Move a: s.actions()){ - check = maxValue(s.result(a), alpha, beta, ply, depth + 1); + check = maxValue(s.result(a), alpha, beta, depth + 1, a.isJump()); if(check < v){ v = check; } diff --git a/src/CheckersGameState3.java b/src/CheckersGameState3.java index f078075..6593464 100644 --- a/src/CheckersGameState3.java +++ b/src/CheckersGameState3.java @@ -5,10 +5,13 @@ public class CheckersGameState3 implements CheckersGameState{ int player; int[] board; + List actions; public CheckersGameState3(int player, int[] board){ this.player = player; this.board = board; + //System.out.println(this.moves()); + this.actions = this.moves(); } public CheckersGameState3() { @@ -23,14 +26,53 @@ public class CheckersGameState3 implements CheckersGameState{ 2,2,2,2, 2,2,2,2 }; + this.actions = this.moves(); } public CheckersGameState3(int player, String[] board){ this.player = player; this.board = to_array(board); + this.actions = this.moves(); } - public int convert(String s){ + + boolean canJump(){ + //System.out.println(this.moves); + return this.actions.get(0).isJump(); + } + + boolean canExchange(){ + if(canJump()){ + for(Move m: this.actions){ + if(m.captures().length == 1){ + for(Move n: this.result(m).actions()){ + if(n.captures().length == 1){ + return true; + } + } + } + } + return false; + } + return false; + } + + boolean twoKings(){ + int bkings = 0; + int wkings = 0; + for(int i = 0; i < board.length; i++){ + if(board[i] == 3){ + bkings++; + } + else if(board[i] == 4){ + wkings++; + } + } + return Math.abs(bkings - wkings) >= 2; + } + + + public int convert(String s){ if(s.equals("-")){ return 0; } @@ -144,6 +186,11 @@ public class CheckersGameState3 implements CheckersGameState{ } public List actions(){ + return this.actions; + } + + + public List moves(){ LinkedList moves = new LinkedList(); LinkedList jumps = new LinkedList(); for(int i = 0; i < this.board.length; i++){ @@ -166,6 +213,8 @@ public class CheckersGameState3 implements CheckersGameState{ } } } + //System.out.println(jumps); + //System.out.println(moves); if(jumps.isEmpty()){ return moves; } @@ -285,8 +334,8 @@ public class CheckersGameState3 implements CheckersGameState{ double mypieces = 0.0; for(int i = 0; i0){ - Move myMove = ai.minimax(currentState, 8); + currentState.printState(); + Move myMove = ai.minimax(currentState); writeMessageAndEcho(myMove.toString()); if(!applyMove(myMove.toString())) { System.out.println("couldn't apply my move"); From 9f270071fb17de7dc822a38c5fd020b6edb8e1cc Mon Sep 17 00:00:00 2001 From: Sailesh Date: Wed, 19 Apr 2017 16:24:49 -0400 Subject: [PATCH 2/2] Implement variable depth minimax --- src/CheckersAI.java | 35 ++++++++++++++++++----------------- src/CheckersGameState3.java | 2 +- src/RmCheckersClient.java | 2 +- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/CheckersAI.java b/src/CheckersAI.java index f761434..36a5b07 100644 --- a/src/CheckersAI.java +++ b/src/CheckersAI.java @@ -9,21 +9,24 @@ public class CheckersAI{ this.player = player; } - private boolean stop(CheckersGameState state, boolean jumped, int depth){ + private boolean stop(CheckersGameState state, boolean jumped, int depth, int min_ply){ CheckersGameState3 s = (CheckersGameState3) state; - if(!s.canJump() && !jumped && !s.canExchange() && depth == 3){ + if(depth < min_ply){ + return false; + } + else if(depth == min_ply && !s.canJump() && !jumped && !s.canExchange()){ return true; } - else if(!s.canJump() && !s.canExchange() && depth == 4){ + else if(depth == min_ply +1 && !s.canJump() && !s.canExchange() && depth == 4){ return true; } - else if(depth < 11 && !s.canJump()){ + else if(depth > min_ply +1 && depth < min_ply + 10 && !s.canJump()){ return true; } - else if(depth < 20 && s.twoKings()){ + else if(depth > min_ply +10 && depth < min_ply + 20 && s.twoKings()){ return true; } - else if(depth >= 20){ + else if(depth >= min_ply + 20){ return true; } else{ @@ -31,7 +34,7 @@ public class CheckersAI{ } } - public Move minimax(CheckersGameState s){ + public Move minimax(CheckersGameState s, int min_ply){ int depth = 0; if(s.isTerminal()){ return null; @@ -43,7 +46,7 @@ public class CheckersAI{ Move max = null; // System.out.println(s.actions().size()); for(Move a: s.actions()){ - check = minValue(s.result(a), alpha, beta, depth + 1, a.isJump()); + check = minValue(s.result(a), alpha, beta, depth + 1, a.isJump(), min_ply); if(check > v){ v = check; max = a; @@ -56,15 +59,14 @@ public class CheckersAI{ return max; } - private double maxValue(CheckersGameState s, double alpha, double beta, int depth, boolean jumped){ - - if(s.isTerminal() || stop(s, jumped, depth)){ - return eval.evaluate(s, this.player); // if terminal, piece ratio should be infinite + private double maxValue(CheckersGameState s, double alpha, double beta, int depth, boolean jumped, int min_ply){ + if(s.isTerminal() || stop(s, jumped, depth, min_ply)){ + return eval.evaluate(s, this.player); // if terminal, piece ratio should be infinite } double v = Double.NEGATIVE_INFINITY; double check; for(Move a: s.actions()){ - check = minValue(s.result(a), alpha, beta, depth + 1, a.isJump()); + check = minValue(s.result(a), alpha, beta, depth + 1, a.isJump(), min_ply); if(check > v){ v = check; } @@ -76,15 +78,14 @@ public class CheckersAI{ return v; } - private double minValue(CheckersGameState s, double alpha, double beta, int depth, boolean jumped){ - - if(s.isTerminal() || stop(s, jumped, depth)){ + private double minValue(CheckersGameState s, double alpha, double beta, int depth, boolean jumped, int min_ply){ + if(s.isTerminal() || stop(s, jumped, depth, min_ply)){ return eval.evaluate(s, this.player); } double v = Double.POSITIVE_INFINITY; double check; for(Move a: s.actions()){ - check = maxValue(s.result(a), alpha, beta, depth + 1, a.isJump()); + check = maxValue(s.result(a), alpha, beta, depth + 1, a.isJump(), min_ply); if(check < v){ v = check; } diff --git a/src/CheckersGameState3.java b/src/CheckersGameState3.java index 6593464..375f4d2 100644 --- a/src/CheckersGameState3.java +++ b/src/CheckersGameState3.java @@ -38,7 +38,7 @@ public class CheckersGameState3 implements CheckersGameState{ boolean canJump(){ //System.out.println(this.moves); - return this.actions.get(0).isJump(); + return (this.actions.size() > 0 && this.actions.get(0).isJump()); } boolean canExchange(){ diff --git a/src/RmCheckersClient.java b/src/RmCheckersClient.java index 5842136..2b271c8 100644 --- a/src/RmCheckersClient.java +++ b/src/RmCheckersClient.java @@ -145,7 +145,7 @@ public class RmCheckersClient { } while(currentState.actions().size()>0){ currentState.printState(); - Move myMove = ai.minimax(currentState); + Move myMove = ai.minimax(currentState, 8); writeMessageAndEcho(myMove.toString()); if(!applyMove(myMove.toString())) { System.out.println("couldn't apply my move");