Skip to content

Commit

Permalink
Added minimax implementation and game ending conditions.
Browse files Browse the repository at this point in the history
  • Loading branch information
john committed Apr 30, 2016
1 parent 4077858 commit 0d1369f
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 20 deletions.
128 changes: 113 additions & 15 deletions src/controller/Game.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package controller;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;

import model.Board;
import model.Color;
import model.Location;
import model.Move;
import model.Type;
import view.GamePanel;

public class Game {
Expand All @@ -21,16 +25,31 @@ public Game(Board board) {

/* Either the server or the user will request a move */
public void requestMove(Move move) {

/* If this is the first jump of the jump sequence */
if (move.isJump() && !inJumpSequence) {
inJumpSequence = true;
}

if (movePromotesPiece(move)) {
inJumpSequence = false;
}

board.movePiece(move);
gamePanel.movePiece(move);

if (board.getMovesSinceCapture() > GameConstants.MAX_PASSIVE_MOVES) {
gamePanel.displayMessage("It's a tie! Be more aggressive next time.");
// Disable UI here
}
/*
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/* If this is the first jump of the jump sequence */
if (move.isJump() && !inJumpSequence) inJumpSequence = true;
*/

/* If the piece that jumped has no more jumps */
if (move.isJump() && getAvailableMoves(move.destination).isEmpty())
Expand All @@ -39,23 +58,40 @@ public void requestMove(Move move) {
/* If the game is not in the middle of a jump sequence, move the thunk */
if (!inJumpSequence) {

Move thunkMove = getThunkMove();
board.movePiece(thunkMove);
gamePanel.movePiece(thunkMove);

if (thunkMove.isJump()) inJumpSequence = true;

while (inJumpSequence) {
thunkMove = getThunkMove();
if (thunkMove != null) {
board.movePiece(thunkMove);
gamePanel.movePiece(thunkMove);
} else {
Move thunkMove = getMinimaxMove(GameConstants.MAX_SEARCH_DEPTH);
if (thunkMove != null) {
if (thunkMove.isJump()) inJumpSequence = true;

if (movePromotesPiece(thunkMove)) {
inJumpSequence = false;
}

board.movePiece(thunkMove);
gamePanel.movePiece(thunkMove);

while (inJumpSequence) {
thunkMove = getThunkMove();

if (thunkMove != null) {
if (movePromotesPiece(thunkMove)) {
inJumpSequence = false;
}
board.movePiece(thunkMove);
gamePanel.movePiece(thunkMove);
} else {
inJumpSequence = false;
}
}
} else {
gamePanel.displayMessage("Game over. User has won!");
}
}

}

public boolean movePromotesPiece(Move move) {
return board.getPiece(move.source).getType() != Type.KING &&
board.isPromotionLocation(move.destination);

}

public Move getThunkMove() {
Expand All @@ -76,6 +112,64 @@ public Move getThunkMove() {
return null;
}
}

public Move getMinimaxMove(int depth) {
ArrayList<Board> boardFrontier = board.generateFrontier(GameConstants.THUNK_COLOR);
ArrayList<Move> moveFrontier = board.generateAllMoves(GameConstants.THUNK_COLOR);
ArrayList<Integer> moveScores = new ArrayList<Integer>();

Color otherColor = GameConstants.THUNK_COLOR == Color.BLACK ?
Color.WHITE : Color.BLACK;
// Recurse for each one here
for (Board board : boardFrontier) {
moveScores.add(this.getMinimaxScore(otherColor, board, depth));
}

int maxScore = Integer.MIN_VALUE;
Move bestMove = null;

for (int i = 0; i < moveScores.size(); ++i) {
if (moveScores.get(i) > maxScore) {
bestMove = moveFrontier.get(i);
maxScore = moveScores.get(i);
}
System.out.println("Score[" + i + "] = " + moveScores.get(i));
}
System.out.println("---");

return bestMove;
}

public int getMinimaxScore(Color color, Board b, int depth) {
if (depth == 0 || b.getBlackPieces() == 0 || b.getWhitePieces() == 0) {
Color otherColor = color == Color.BLACK ? Color.WHITE : Color.BLACK;
return b.getHeuristic(otherColor);
}
ArrayList<Board> boardFrontier = board.generateFrontier(color);

ArrayList<Integer> moveScores = new ArrayList<Integer>();

for (Board board : boardFrontier) {
Color nextColor = color == Color.BLACK ? Color.WHITE : Color.BLACK;
moveScores.add(getMinimaxScore(nextColor, board, depth - 1));
}
for (int score : moveScores) {
System.out.println(score);
}
System.out.println("---");
if (color == GameConstants.THUNK_COLOR) {
// Maximize
return Collections.max(moveScores);
}
else {
// Minimize
return Collections.min(moveScores);
}
}

public void notifyClientWin() {
gamePanel.displayMessage("Game over. Client has won!");
}


public ArrayList<Move> getAvailableMoves(Location source) {
Expand All @@ -90,6 +184,10 @@ public ArrayList<Move> getAllAvailableJumpMoves(Color player) {
return board.generateAllJumpMoves(player);
}

public ArrayList<Move> getAllAvailableMoves(Color player) {
return board.generateAllMoves(player);
}

public void setGamePanel(GamePanel panel) {
this.gamePanel = panel;
}
Expand Down
2 changes: 2 additions & 0 deletions src/controller/GameConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ public class GameConstants {
public static final int SERVER_MODE = 1;
public static final Color THUNK_COLOR = Color.WHITE;
public static final Color USER_COLOR = Color.BLACK;
public static final int MAX_PASSIVE_MOVES = 50;
public static final int MAX_SEARCH_DEPTH = 2;
}
26 changes: 22 additions & 4 deletions src/model/Board.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ public Board(Board other) {
Piece[][] other_representation = other.getRepresentation();
for (int i = 0; i < other_representation.length; ++i) {
for (int j = 0; j < other_representation[0].length; ++j) {
this.representation[i][j] = new Piece(other_representation[i][j]);
if (other_representation[i][j] != null) {
this.representation[i][j] = new Piece(other_representation[i][j]);
}
}
}
movesSinceCapture = other.getMovesSinceCapture();
Expand Down Expand Up @@ -100,7 +102,12 @@ public void movePiece(Move move) {

/* Remove the piece being jumped ("monkey in the middle") */
representation[monkeyRow][monkeyCol] = null;

this.movesSinceCapture = 0;
}
else {
++this.movesSinceCapture;
}

/* Place the piece in the destination cell */
representation[destRow][destCol] = representation[sourceRow][sourceCol];
Expand Down Expand Up @@ -359,11 +366,14 @@ public boolean isOccupied(Location location) {
}

public boolean canPromote(Piece p) {
int row = p.getLocation().row;

return p.getType() != Type.KING &&
((row == 0 && p.getColor() == Color.WHITE) ||
(row == BOARD_SIZE - 1 && p.getColor() == Color.BLACK));
isPromotionLocation(p.getLocation());
}

public boolean isPromotionLocation(Location location) {
return (location.row == 0 ||
location.row == BOARD_SIZE - 1 );
}

public int getHeuristic(Color color) {
Expand All @@ -387,6 +397,14 @@ public Piece getLastPieceMoved() {
}
public Move getLastMove() {
return this.lastMove;
}

public int getWhitePieces() {
return this.whitePieces;
}

public int getBlackPieces() {
return this.blackPieces;
}
}

2 changes: 1 addition & 1 deletion src/model/Color.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

public enum Color {
WHITE,
BLACK
BLACK,
}
8 changes: 8 additions & 0 deletions src/view/GamePanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ public void run() {
canvas.highlightAndValidateSquare(jump.source);
}
}

if (outOfMoves()) {
game.notifyClientWin();
}
}
}).start();
}
Expand Down Expand Up @@ -203,4 +207,8 @@ public boolean isInJumpSequence() {
public boolean isForceJump() {
return !game.getAllAvailableJumpMoves(GameConstants.USER_COLOR).isEmpty();
}

public boolean outOfMoves() {
return game.getAllAvailableMoves(GameConstants.USER_COLOR).isEmpty();
}
}

0 comments on commit 0d1369f

Please sign in to comment.