Alpha-Beta move generator can look 2 plays (4 plies) ahead on a 4x4 board and blocks every possible attempt by the player to connect 3.
It should be possible to play on larger boards when the computers 'move' is changed from playing a tile to picking the player's next available color.
This commit is contained in:
@@ -5,5 +5,6 @@
|
|||||||
<classpathentry kind="src" path="test"/>
|
<classpathentry kind="src" path="test"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
<classpathentry kind="lib" path="lib/junit-4.10.jar"/>
|
<classpathentry kind="lib" path="lib/junit-4.10.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/log4j-1.2.16.jar"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
BIN
lib/log4j-1.2.16.jar
Normal file
BIN
lib/log4j-1.2.16.jar
Normal file
Binary file not shown.
@@ -5,7 +5,8 @@ import java.awt.event.MouseListener;
|
|||||||
import java.awt.event.MouseWheelEvent;
|
import java.awt.event.MouseWheelEvent;
|
||||||
import java.awt.event.MouseWheelListener;
|
import java.awt.event.MouseWheelListener;
|
||||||
|
|
||||||
import model.HumanPlayer;
|
import player.HumanPlayer;
|
||||||
|
|
||||||
|
|
||||||
import view.Tile;
|
import view.Tile;
|
||||||
import view.TileSelectionPanel;
|
import view.TileSelectionPanel;
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ package controller;
|
|||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
|
|
||||||
|
import player.HumanPlayer;
|
||||||
|
|
||||||
import view.TileSelectionPanel;
|
import view.TileSelectionPanel;
|
||||||
|
|
||||||
import model.HumanPlayer;
|
|
||||||
import model.Board.TileColor;
|
import model.Board.TileColor;
|
||||||
|
|
||||||
public class TSPMouseListener implements MouseListener{
|
public class TSPMouseListener implements MouseListener{
|
||||||
|
|||||||
@@ -8,12 +8,15 @@ public class Board {
|
|||||||
BLUE, GREEN, NONE, RED, YELLOW
|
BLUE, GREEN, NONE, RED, YELLOW
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final int NUM_COLS = 5;
|
public static final int NUM_COLS = 4;
|
||||||
public static final int NUM_ROWS = 5;
|
public static final int NUM_ROWS = 4;
|
||||||
public static final int ROW_REMOVAL_SIZE = 3;
|
public static final int ROW_REMOVAL_SIZE = 3;
|
||||||
|
|
||||||
private final TileColor[][] board;
|
private final TileColor[][] board;
|
||||||
|
|
||||||
|
// a Ply is a play by 1 side. Increments each time setTile() is called.
|
||||||
|
private int numPlies = 0;
|
||||||
|
|
||||||
public Board(Board that) {
|
public Board(Board that) {
|
||||||
this();
|
this();
|
||||||
for (int i = 0; i < NUM_COLS; i++) {
|
for (int i = 0; i < NUM_COLS; i++) {
|
||||||
@@ -22,17 +25,16 @@ public class Board {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Board() {
|
public Board() {
|
||||||
board = new TileColor[NUM_ROWS][NUM_COLS];
|
board = new TileColor[NUM_ROWS][NUM_COLS];
|
||||||
for (int r = 0; r < NUM_ROWS; r++) {
|
for (int r = 0; r < NUM_ROWS; r++) {
|
||||||
for (int c = 0; c < NUM_COLS; c++) {
|
for (int c = 0; c < NUM_COLS; c++) {
|
||||||
setTile(new CellPointer(r, c), TileColor.NONE);
|
board[r][c] = TileColor.NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
@@ -50,7 +52,7 @@ public class Board {
|
|||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass())
|
||||||
return false;
|
return false;
|
||||||
Board other = (Board) obj;
|
Board other = (Board) obj;
|
||||||
|
|
||||||
for (int r = 0; r < NUM_ROWS; r++) {
|
for (int r = 0; r < NUM_ROWS; r++) {
|
||||||
for (int c = 0; c < NUM_COLS; c++) {
|
for (int c = 0; c < NUM_COLS; c++) {
|
||||||
if (this.board[r][c] != other.board[r][c]) {
|
if (this.board[r][c] != other.board[r][c]) {
|
||||||
@@ -58,7 +60,7 @@ public class Board {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +68,31 @@ public class Board {
|
|||||||
return board[r][c];
|
return board[r][c];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTile(CellPointer cp, TileColor tile) {
|
public int getTurn() {
|
||||||
|
return numPlies / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlayerTurn() {
|
||||||
|
return numPlies % 2 == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTerminalState() {
|
||||||
|
for (int r = 0; r < Board.NUM_ROWS; r++) {
|
||||||
|
for (int c = 0; c < Board.NUM_COLS; c++) {
|
||||||
|
if (board[r][c] == TileColor.NONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean playTile(CellPointer cp, TileColor tile) {
|
||||||
|
if (board[cp.r][cp.c] != TileColor.NONE) {
|
||||||
|
return false; //illegal move
|
||||||
|
}
|
||||||
|
|
||||||
board[cp.r][cp.c] = tile;
|
board[cp.r][cp.c] = tile;
|
||||||
|
|
||||||
if (tile != TileColor.NONE) {
|
if (tile != TileColor.NONE) {
|
||||||
@@ -199,5 +225,8 @@ public class Board {
|
|||||||
board[cell.r][cell.c] = TileColor.NONE;
|
board[cell.r][cell.c] = TileColor.NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
numPlies++;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
24
src/model/BoardScorer.java
Normal file
24
src/model/BoardScorer.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scorer for use by various ComPlayer implementations.
|
||||||
|
*
|
||||||
|
* @author Woody
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BoardScorer {
|
||||||
|
|
||||||
|
public int getMaxScore(Board board) {
|
||||||
|
return Board.NUM_ROWS * Board.NUM_COLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getScore(Board board) {
|
||||||
|
int score = 0;
|
||||||
|
for (int r = 0; r < Board.NUM_ROWS; r++) {
|
||||||
|
for (int c = 0; c < Board.NUM_COLS; c++) {
|
||||||
|
score += board.getTile(r, c) == Board.TileColor.NONE ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,19 @@ package model;
|
|||||||
import model.Board.TileColor;
|
import model.Board.TileColor;
|
||||||
|
|
||||||
public class Move {
|
public class Move {
|
||||||
|
/**
|
||||||
|
* Used when genMove() is called but no valid move exists for that player.
|
||||||
|
*/
|
||||||
|
public static final Move NONE = new Move(TileColor.NONE, -1, -1);
|
||||||
|
|
||||||
private final TileColor color;
|
private final TileColor color;
|
||||||
private final CellPointer cp;
|
private final CellPointer cp;
|
||||||
|
|
||||||
|
public Move(TileColor color, int row, int column) {
|
||||||
|
cp = new CellPointer(row,column);
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
public Move(CellPointer cllPntr, TileColor tlClr) {
|
public Move(CellPointer cllPntr, TileColor tlClr) {
|
||||||
cp = cllPntr;
|
cp = cllPntr;
|
||||||
color = tlClr;
|
color = tlClr;
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
package model;
|
package model;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import player.AlphaBetaComPlayer;
|
||||||
|
import player.HumanPlayer;
|
||||||
|
import player.Player;
|
||||||
import view.BoardPanel;
|
import view.BoardPanel;
|
||||||
import view.MessagePanel;
|
import view.MessagePanel;
|
||||||
|
import view.ScorePanel;
|
||||||
import model.Board.TileColor;
|
import model.Board.TileColor;
|
||||||
|
|
||||||
public class Referee implements Runnable {
|
public class Referee implements Runnable {
|
||||||
@@ -10,26 +16,28 @@ public class Referee implements Runnable {
|
|||||||
public static final String GAME_OVER = "Game over!";
|
public static final String GAME_OVER = "Game over!";
|
||||||
public static final String PLAYER_TURN = "Waiting for the player's move.";
|
public static final String PLAYER_TURN = "Waiting for the player's move.";
|
||||||
|
|
||||||
|
public static final Logger LOGGER = Logger.getLogger(Referee.class
|
||||||
|
.getName());
|
||||||
|
|
||||||
private final Board board;
|
private final Board board;
|
||||||
private final HumanPlayer humanPlayer = new HumanPlayer();
|
private final HumanPlayer humanPlayer = new HumanPlayer();
|
||||||
private final Player cc;
|
private final Player computerPlayer;
|
||||||
|
|
||||||
private boolean playerTurn;
|
|
||||||
private BoardPanel boardPanel;
|
private BoardPanel boardPanel;
|
||||||
private int score = 0;
|
|
||||||
private MessagePanel messagePanel;
|
private MessagePanel messagePanel;
|
||||||
|
private ScorePanel scorePanel;
|
||||||
|
|
||||||
public Referee() {
|
public Referee() {
|
||||||
board = new Board();
|
board = new Board();
|
||||||
cc = new RandomComPlayer();
|
computerPlayer = new AlphaBetaComPlayer();
|
||||||
playerTurn = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
int plies = 0;
|
int plies = 0;
|
||||||
while (!isOver()) {
|
while (!isOver()) {
|
||||||
if (playerTurn) {
|
scorePanel.updateScore(getPlayerScore());
|
||||||
|
if (board.isPlayerTurn()) {
|
||||||
boolean wasInterrupted = false;
|
boolean wasInterrupted = false;
|
||||||
while (!humanPlayer.isReady()) {
|
while (!humanPlayer.isReady()) {
|
||||||
try {
|
try {
|
||||||
@@ -39,7 +47,8 @@ public class Referee implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (wasInterrupted) {
|
if (wasInterrupted) {
|
||||||
System.out.println("Interrupted while waiting for human to move!");
|
System.out
|
||||||
|
.println("Interrupted while waiting for human to move!");
|
||||||
} else {
|
} else {
|
||||||
Move mv = humanPlayer.getMove(board);
|
Move mv = humanPlayer.getMove(board);
|
||||||
if (board.getTile(mv.getCell().r, mv.getCell().c) == TileColor.NONE) {
|
if (board.getTile(mv.getCell().r, mv.getCell().c) == TileColor.NONE) {
|
||||||
@@ -49,79 +58,65 @@ public class Referee implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Move mv = cc.getMove(board);
|
Move mv = computerPlayer.getMove(board);
|
||||||
playToken(mv);
|
playToken(mv);
|
||||||
}
|
}
|
||||||
|
|
||||||
messagePanel.updateMessage(getMessage());
|
messagePanel.updateMessage(getMessage());
|
||||||
boardPanel.updateIcons();
|
boardPanel.updateIcons();
|
||||||
System.out.println("plies: " + plies++);
|
LOGGER.info("plies: " + plies++);
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1000L);
|
Thread.sleep(1000L);
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
System.out.println("Interrupted while waiting for human view ply!");
|
System.out
|
||||||
|
.println("Interrupted while waiting for human view ply!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getComputerPlayer() {
|
public Player getComputerPlayer() {
|
||||||
return cc;
|
return computerPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HumanPlayer getHumanPlayer() {
|
public HumanPlayer getHumanPlayer() {
|
||||||
return humanPlayer;
|
return humanPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
if (isOver()) {
|
if (isOver()) {
|
||||||
return GAME_OVER;
|
return GAME_OVER;
|
||||||
} else if (isPlayersTurn()) {
|
} else if (board.isPlayerTurn()) {
|
||||||
return PLAYER_TURN;
|
return PLAYER_TURN;
|
||||||
} else {
|
} else {
|
||||||
return COM_TURN;
|
return COM_TURN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOver() {
|
|
||||||
for (int r = 0; r < Board.NUM_ROWS; r++) {
|
|
||||||
for (int c = 0; c < Board.NUM_COLS; c++) {
|
|
||||||
if (board.getTile(r, c) == TileColor.NONE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
public int getPlayerScore() {
|
||||||
|
return board.getTurn();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPlayersTurn() {
|
|
||||||
return playerTurn;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getScore() {
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TileColor getTile(int r, int c) {
|
public TileColor getTile(int r, int c) {
|
||||||
return board.getTile(r, c);
|
return board.getTile(r, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOver() {
|
||||||
|
return board.isTerminalState();
|
||||||
|
}
|
||||||
|
|
||||||
public void playToken(Move move) {
|
public void playToken(Move move) {
|
||||||
|
board.playTile(move.getCell(), move.getColor());
|
||||||
board.setTile(move.getCell(), move.getColor());
|
|
||||||
|
|
||||||
if (playerTurn) {
|
|
||||||
score++;
|
|
||||||
}
|
|
||||||
|
|
||||||
playerTurn = !playerTurn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBoardPanel(BoardPanel boardPanel) {
|
public void setBoardPanel(BoardPanel boardPanel) {
|
||||||
this.boardPanel = boardPanel;
|
this.boardPanel = boardPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMessagePanel(MessagePanel messagePanel) {
|
public void setMessagePanel(MessagePanel messagePanel) {
|
||||||
this.messagePanel = messagePanel;
|
this.messagePanel = messagePanel;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void setScorePanel(ScorePanel scorePanel) {
|
||||||
|
this.scorePanel = scorePanel;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/player/AlphaBetaComPlayer.java
Normal file
25
src/player/AlphaBetaComPlayer.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package player;
|
||||||
|
|
||||||
|
import model.Board;
|
||||||
|
import model.Move;
|
||||||
|
import player.generator.AlphaBetaMoveGenerator;
|
||||||
|
import player.generator.MoveGenerator;
|
||||||
|
|
||||||
|
public class AlphaBetaComPlayer implements Player {
|
||||||
|
private MoveGenerator moveGenerator = new AlphaBetaMoveGenerator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Move getMove(Board board) {
|
||||||
|
return moveGenerator.genMove(board, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void denyMove() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return true; // always ready to play a random valid move
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package model;
|
package player;
|
||||||
|
|
||||||
|
import model.Board;
|
||||||
|
import model.CellPointer;
|
||||||
|
import model.Move;
|
||||||
import model.Board.TileColor;
|
import model.Board.TileColor;
|
||||||
|
|
||||||
|
|
||||||
5
src/player/MinimaxComPlayer.java
Normal file
5
src/player/MinimaxComPlayer.java
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package player;
|
||||||
|
|
||||||
|
public class MinimaxComPlayer {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package model;
|
package player;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import model.Board;
|
||||||
|
import model.CellPointer;
|
||||||
|
import model.Move;
|
||||||
import model.Board.TileColor;
|
import model.Board.TileColor;
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
package model;
|
package player;
|
||||||
|
|
||||||
|
import model.Board;
|
||||||
|
import model.Move;
|
||||||
|
|
||||||
|
|
||||||
public interface Player {
|
public interface Player {
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package model;
|
package player;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import model.Board;
|
||||||
|
import model.CellPointer;
|
||||||
|
import model.Move;
|
||||||
import model.Board.TileColor;
|
import model.Board.TileColor;
|
||||||
|
|
||||||
|
|
||||||
132
src/player/generator/AlphaBetaMoveGenerator.java
Normal file
132
src/player/generator/AlphaBetaMoveGenerator.java
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package player.generator;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import model.Board;
|
||||||
|
import model.BoardScorer;
|
||||||
|
import model.Move;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
public class AlphaBetaMoveGenerator implements MoveGenerator {
|
||||||
|
private static final Logger LOGGER = Logger
|
||||||
|
.getLogger(AlphaBetaMoveGenerator.class.getName());
|
||||||
|
private static final int DEFAULT_RECURSIVE_PLAYS = 2;
|
||||||
|
|
||||||
|
private final BoardScorer scorer = new BoardScorer();
|
||||||
|
private final ValidMoveGenerator validMoveGenerator = new ValidMoveGenerator();
|
||||||
|
|
||||||
|
private Move bestPick = Move.NONE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Move genMove(Board board, boolean asHuman) {
|
||||||
|
|
||||||
|
int alpha = Integer.MIN_VALUE;
|
||||||
|
int beta = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
if (!asHuman) {
|
||||||
|
getMaxValue(board, asHuman, DEFAULT_RECURSIVE_PLAYS * 2, alpha,
|
||||||
|
beta);
|
||||||
|
} else {
|
||||||
|
getMinValue(board, asHuman, DEFAULT_RECURSIVE_PLAYS * 2, alpha,
|
||||||
|
beta);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestPick;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMaxValue(Board board, boolean asHuman, int recursionLevel,
|
||||||
|
int alpha, int beta) {
|
||||||
|
if (terminalTest(recursionLevel)) {
|
||||||
|
return getUtility(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Move> validMoves = validMoveGenerator.genMoves(board, asHuman,
|
||||||
|
MoveGenerator.ALL_MOVES);
|
||||||
|
|
||||||
|
int value = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
for (Move nextMove : validMoves) {
|
||||||
|
Board nextBoard = new Board(board);
|
||||||
|
|
||||||
|
if (!nextBoard.playTile(nextMove.getCell(), nextMove.getColor())) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Illegal move attempted during search!");
|
||||||
|
}
|
||||||
|
|
||||||
|
int minValue = getMinValue(nextBoard, !asHuman, recursionLevel - 1,
|
||||||
|
alpha, beta);
|
||||||
|
|
||||||
|
if (minValue > value) {
|
||||||
|
value = minValue;
|
||||||
|
if (recursionLevel == DEFAULT_RECURSIVE_PLAYS * 2) {
|
||||||
|
bestPick = nextMove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= beta) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
alpha = Math.max(alpha, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMinValue(Board board, boolean asHuman, int recursionLevel,
|
||||||
|
int alpha, int beta) {
|
||||||
|
if (terminalTest(recursionLevel)) {
|
||||||
|
return getUtility(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Move> validMoves = validMoveGenerator.genMoves(board, asHuman,
|
||||||
|
MoveGenerator.ALL_MOVES);
|
||||||
|
|
||||||
|
int value = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
for (Move nextMove : validMoves) {
|
||||||
|
Board nextBoard = new Board(board);
|
||||||
|
|
||||||
|
if (!nextBoard.playTile(nextMove.getCell(), nextMove.getColor())) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Illegal move attempted during search!");
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxValue = getMaxValue(nextBoard, !asHuman, recursionLevel - 1,
|
||||||
|
alpha, beta);
|
||||||
|
|
||||||
|
if (maxValue < value) {
|
||||||
|
value = maxValue;
|
||||||
|
if (recursionLevel == 2 * DEFAULT_RECURSIVE_PLAYS) {
|
||||||
|
bestPick = nextMove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= alpha) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
beta = Math.min(beta, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean terminalTest(int recursionLevel) {
|
||||||
|
return recursionLevel < 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getUtility(Board board) {
|
||||||
|
return scorer.getScore(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AlphaBetaMoveGenerator2 does not support this method.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Move> genMoves(Board board, boolean asHuman, int nMoves) {
|
||||||
|
Move[] doNothing = new Move[] { Move.NONE };
|
||||||
|
LOGGER.info("Minimax genMoves() stub returning []");
|
||||||
|
return Arrays.asList(doNothing);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/player/generator/GameScore.java
Normal file
26
src/player/generator/GameScore.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package player.generator;
|
||||||
|
|
||||||
|
public class GameScore {
|
||||||
|
boolean terminal;
|
||||||
|
int maxScore;
|
||||||
|
int score;
|
||||||
|
|
||||||
|
public GameScore(boolean terminal, int maxScore, int score) {
|
||||||
|
super();
|
||||||
|
this.terminal = terminal;
|
||||||
|
this.maxScore = maxScore;
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTerminal() {
|
||||||
|
return terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxScore() {
|
||||||
|
return maxScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getScore() {
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/player/generator/MoveCandidate.java
Normal file
13
src/player/generator/MoveCandidate.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package player.generator;
|
||||||
|
|
||||||
|
import model.Move;
|
||||||
|
|
||||||
|
public class MoveCandidate {
|
||||||
|
public final Move move;
|
||||||
|
public final GameScore score;
|
||||||
|
|
||||||
|
public MoveCandidate(Move move, GameScore score) {
|
||||||
|
this.move = move;
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/player/generator/MoveGenerator.java
Normal file
13
src/player/generator/MoveGenerator.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package player.generator;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import model.Board;
|
||||||
|
import model.Move;
|
||||||
|
|
||||||
|
public interface MoveGenerator {
|
||||||
|
public static final int ALL_MOVES = 0;
|
||||||
|
|
||||||
|
public Move genMove(Board board, boolean asHuman);
|
||||||
|
public List<Move> genMoves(Board board, boolean asHuman, int nMoves);
|
||||||
|
}
|
||||||
38
src/player/generator/ValidMoveGenerator.java
Normal file
38
src/player/generator/ValidMoveGenerator.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package player.generator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import model.Board;
|
||||||
|
import model.Board.TileColor;
|
||||||
|
import model.Move;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
public class ValidMoveGenerator implements MoveGenerator {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(ValidMoveGenerator.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Move genMove(Board board, boolean asHuman) {
|
||||||
|
LOGGER.info("ValidMoveGenerator genMove() stub returning NONE");
|
||||||
|
return Move.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Move> genMoves(Board board, boolean asHuman, int nMoves) {
|
||||||
|
|
||||||
|
List<Move> validMoves = new ArrayList<Move>();
|
||||||
|
|
||||||
|
for (int i = 0; i < Board.NUM_ROWS; i++) {
|
||||||
|
for (int j = 0; j < Board.NUM_COLS; j++) {
|
||||||
|
if (board.getTile(i, j) == TileColor.NONE) {
|
||||||
|
for (TileColor color : TileColor.values()) {
|
||||||
|
validMoves.add(new Move(color, i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validMoves;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,8 +35,9 @@ public class MainFrame extends JFrame {
|
|||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
ScorePanel sp = new ScorePanel(referee);
|
ScorePanel sp = new ScorePanel(referee);
|
||||||
TileSelectionPanel tp = new TileSelectionPanel(referee.getHumanPlayer());
|
referee.setScorePanel(sp);
|
||||||
|
|
||||||
|
TileSelectionPanel tp = new TileSelectionPanel(referee.getHumanPlayer());
|
||||||
BoardPanel bp = new BoardPanel(referee,tp);
|
BoardPanel bp = new BoardPanel(referee,tp);
|
||||||
referee.setBoardPanel(bp);
|
referee.setBoardPanel(bp);
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ public class MainFrame extends JFrame {
|
|||||||
add(vWrapper,BorderLayout.CENTER);
|
add(vWrapper,BorderLayout.CENTER);
|
||||||
|
|
||||||
//To ensure correct size, pre-populate the score and message panels with text.
|
//To ensure correct size, pre-populate the score and message panels with text.
|
||||||
sp.updateScore();
|
sp.updateScore(0);
|
||||||
mp.updateMessage("Loading new game...");
|
mp.updateMessage("Loading new game...");
|
||||||
|
|
||||||
pack();
|
pack();
|
||||||
|
|||||||
@@ -11,17 +11,15 @@ public class ScorePanel extends JPanel {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final JLabel message = new JLabel();
|
private final JLabel message = new JLabel();
|
||||||
private final Referee referee;
|
|
||||||
|
|
||||||
public ScorePanel(Referee ref) {
|
public ScorePanel(Referee ref) {
|
||||||
referee = ref;
|
|
||||||
add(message);
|
add(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateScore() {
|
public void updateScore(final int score) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
message.setText("Score: " + referee.getScore());
|
message.setText("Score: " + score);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import javax.swing.JLabel;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
import player.HumanPlayer;
|
||||||
|
|
||||||
import controller.TSPMouseListener;
|
import controller.TSPMouseListener;
|
||||||
|
|
||||||
import model.HumanPlayer;
|
|
||||||
import model.Board.TileColor;
|
import model.Board.TileColor;
|
||||||
|
|
||||||
public class TileSelectionPanel extends JPanel {
|
public class TileSelectionPanel extends JPanel {
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ public class BoardTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructor() {
|
public void testConstructor() {
|
||||||
new Board();
|
Board board = new Board();
|
||||||
|
assertTrue(board.isPlayerTurn());
|
||||||
|
assertFalse(board.isTerminalState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -19,10 +21,10 @@ public class BoardTest {
|
|||||||
Board board = new Board();
|
Board board = new Board();
|
||||||
Board copy = new Board(board);
|
Board copy = new Board(board);
|
||||||
|
|
||||||
board.setTile(new CellPointer(1,2), TileColor.BLUE);
|
board.playTile(new CellPointer(1,2), TileColor.BLUE);
|
||||||
|
|
||||||
assertFalse(board.equals(copy));
|
assertFalse(board.equals(copy));
|
||||||
copy.setTile(new CellPointer(1,2),TileColor.BLUE);
|
copy.playTile(new CellPointer(1,2),TileColor.BLUE);
|
||||||
assertTrue(board.equals(copy));
|
assertTrue(board.equals(copy));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user