Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Made the code a lot more modular and set it up so it can easily commu…
…nicate with the server
  • Loading branch information
john committed May 1, 2016
1 parent 4133180 commit 9b207cf
Show file tree
Hide file tree
Showing 11 changed files with 419 additions and 239 deletions.
25 changes: 22 additions & 3 deletions src/controller/CheckersMain.java
Expand Up @@ -6,6 +6,9 @@ import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import model.Board;
import player.ComputerPlayer;
import player.HumanPlayer;
import player.ServerPlayer;
import view.CheckersWindow;

public class CheckersMain {
Expand Down Expand Up @@ -41,19 +44,35 @@ public class CheckersMain {
catch (IllegalAccessException e) {
// handle exception
}

Board board = new Board();

final Game game = new Game(board);

ComputerPlayer computer = new ComputerPlayer(GameConstants.THUNK_COLOR, board);
final HumanPlayer user = new HumanPlayer(GameConstants.USER_COLOR, board, game);

game.setComputer(computer);

if (mode == GameConstants.SERVER_MODE) {
/* Create a ServerListener to listen for messages from the server */
final ServerPlayer server = new ServerPlayer(GameConstants.SERVER_COLOR, board, game);
game.setOpponent(server);
new Thread(new Runnable() {

@Override
public void run() {
server.listen();
}

}).start();
} else {
game.setOpponent(user);
}

SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
CheckersWindow window = new CheckersWindow(game, mode);
CheckersWindow window = new CheckersWindow(game, user, mode);
window.open();
}
});
Expand Down
251 changes: 34 additions & 217 deletions src/controller/Game.java
Expand Up @@ -9,258 +9,75 @@ import model.Color;
import model.Location;
import model.Move;
import model.Type;
import player.ComputerPlayer;
import player.HumanPlayer;
import player.Player;
import view.GamePanel;

public class Game {
private ComputerPlayer computer;
private Player opponent;
private Board board;
private GamePanel gamePanel;
private boolean inJumpSequence;


public Game(Board board) {
this.board = board;
this.inJumpSequence = false;
}

/* 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
}

/* If the piece that jumped has no more jumps */
if (move.isJump() && getAvailableMoves(move.destination).isEmpty())
inJumpSequence = false;

checkEndGame();
}

public Move makeComputerMove() {
/* If the game is not in the middle of a jump sequence, move the thunk */
if (!inJumpSequence) {

Move thunkMove = getMinimaxMove(GameConstants.MAX_SEARCH_DEPTH, inJumpSequence);
if (thunkMove != null) {
if (thunkMove.isJump()) inJumpSequence = true;

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

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

while (inJumpSequence) {
thunkMove = getMinimaxMove(GameConstants.MAX_SEARCH_DEPTH, inJumpSequence);
Move computerMove = null;

if (!opponent.isInJumpSequence()) {

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

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

private Move getThunkMove() {
ArrayList<Move> availableMoves;

if (inJumpSequence) {
availableMoves =
board.generateJumpMovesForPiece(board.getLastPieceMoved());
} else {
availableMoves = board.generateAllMoves(GameConstants.THUNK_COLOR);
}

if (!availableMoves.isEmpty()) {
/* Just take the first move we see */
Move moveChoice = availableMoves.get(0);
return moveChoice;
} else {
return null;
}
}

private Move getMinimaxMove(int depth, boolean inJumpSequence) {
ArrayList<Board> boardFrontier = null;
ArrayList<Move> moveFrontier = null;
ArrayList<Integer> moveScores = new ArrayList<Integer>();

if (inJumpSequence) {
/* Generate the frontier only for the piece that just moves */
boardFrontier = board.generateJumpFrontierForPiece(board.getLastPieceMoved());
moveFrontier = board.generateJumpMovesForPiece(board.getLastPieceMoved());

/* If we can't jump anymore, we can't make a move */
if (boardFrontier.isEmpty()) {
return null;
}

} else {
/* Generate the frontier for all pieces */
boardFrontier = board.generateFrontier(GameConstants.THUNK_COLOR);
moveFrontier = board.generateAllMoves(GameConstants.THUNK_COLOR);
}

Color nextColor;
/* Determine the next color to move */
if (inJumpSequence) {
nextColor = GameConstants.THUNK_COLOR;
} else if (GameConstants.THUNK_COLOR == Color.BLACK) {
nextColor = Color.WHITE;
} else {
nextColor = Color.BLACK;
}

/* Calculate the minimax score for each board in the frontier */
for (Board b : boardFrontier) {
moveScores.add(this.getMinimaxScore(nextColor, b, depth, inJumpSequence));
}

/* Determine the maximum minimax score and which move led to that score */
int maxScore = Integer.MIN_VALUE;
Move bestMove = null;

for (int i = 0; i < moveScores.size(); ++i) {
//System.out.println("score[" + i + "] = " + moveScores.get(i));
if (moveScores.get(i) > maxScore) {
//System.out.println("Best move is " + i);
bestMove = moveFrontier.get(i);
maxScore = moveScores.get(i);
}

System.out.println(moveFrontier.get(i) + " --> " + moveScores.get(i));
}

/* All moves have the same outcome */
if (!moveScores.isEmpty() && maxScore == Collections.min(moveScores)) {
return getBestOfSimilarMoves(moveFrontier);
}
System.out.println("Choosing: " + bestMove);

return bestMove;
}

private int getMinimaxScore(Color color, Board b, int depth, boolean inJumpSequence) {
ArrayList<Board> boardFrontier;
ArrayList<Integer> moveScores = new ArrayList<Integer>();
Color otherColor = (color == Color.BLACK ? Color.WHITE : Color.BLACK);

if (depth == 0) {
return b.getHeuristic(otherColor);
}

if (inJumpSequence) {
/* Generate the frontier only for the piece that just moved */
boardFrontier = b.generateJumpFrontierForPiece(b.getLastPieceMoved());

/* If we can't jump anymore, get out of the jump sequence */
if (boardFrontier.isEmpty()) {
return getMinimaxScore(otherColor, b, depth-1, inJumpSequence);
}
} else {
/* Generate the frontier for all pieces */
boardFrontier = b.generateFrontier(color);
}

/* If we have reached the maximum depth or an end state for the game */
if (b.getBlackPieces() == 0 || b.getWhitePieces() == 0
|| boardFrontier.size() == 0) {
return b.getHeuristic(otherColor);
}

Color nextColor;
/* Determine the next color to move */
if (inJumpSequence) {
nextColor = color;
} else {
nextColor = otherColor;
}

for (Board board : boardFrontier) {
int moveScore = getMinimaxScore(nextColor, board, depth - 1, inJumpSequence);
moveScores.add(moveScore);
}

if (color == GameConstants.THUNK_COLOR) {
// Maximize
return Collections.max(moveScores);
}
else {
// Minimize
return Collections.min(moveScores);
}

return computerMove;
}

private Move getBestOfSimilarMoves(ArrayList<Move> moves) {
ArrayList<Move> regularMoves = new ArrayList<Move>();

for (Move move : moves) {
if (!move.isJump()) {
regularMoves.add(move);
}
}

/* We prefer to advance regular checkers over kings
* if all moves have the same outcome
*/
if (!regularMoves.isEmpty()) {
return regularMoves.get(ThreadLocalRandom.current().nextInt(regularMoves.size()));
public void checkEndGame() {
if (board.getMovesSinceCapture() > GameConstants.MAX_PASSIVE_MOVES) {
gamePanel.displayMessage("It's a tie! Be more aggressive next time.");
gamePanel.disableInteraction();
}

return moves.get(ThreadLocalRandom.current().nextInt(moves.size()));
}

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


public ArrayList<Move> getAvailableMoves(Location source) {
if (inJumpSequence) {
return board.generateJumpMovesForPiece(board.getPiece(source));
} else {
return board.generateMovesForPiece(board.getPiece(source));
}
}

public ArrayList<Move> getAllAvailableJumpMoves(Color player) {
return board.generateAllJumpMoves(player);
public void setGamePanel(GamePanel panel) {
this.gamePanel = panel;
}

public ArrayList<Move> getAllAvailableMoves(Color player) {
return board.generateAllMoves(player);
public void setComputer(ComputerPlayer computer) {
this.computer = computer;
}

public void setGamePanel(GamePanel panel) {
this.gamePanel = panel;
public void setOpponent(Player opponent) {
this.opponent = opponent;
}

public boolean isInJumpSequence() {
return inJumpSequence;
public Player getComputer() {
return computer;
}



}
7 changes: 4 additions & 3 deletions src/controller/GameConstants.java
Expand Up @@ -5,10 +5,11 @@ import model.Color;
public class GameConstants {
public static final int USER_MODE = 0;
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 Color THUNK_COLOR = Color.WHITE;
public static Color USER_COLOR = Color.BLACK;
public static Color SERVER_COLOR = Color.BLACK;
public static final int MAX_PASSIVE_MOVES = 50;
public static final int MAX_SEARCH_DEPTH = 7;
public static final int MAX_SEARCH_DEPTH = 6;
public static final int BOARD_SIZE = 8;

/* Parameters to the heuristic */
Expand Down
5 changes: 5 additions & 0 deletions src/model/Board.java
Expand Up @@ -384,6 +384,11 @@ public class Board {
isPromotionLocation(p.getLocation());
}

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

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

0 comments on commit 9b207cf

Please sign in to comment.