All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aima.core.environment.connectfour.ConnectFourState Maven / Gradle / Ivy

Go to download

AIMA-Java Core Algorithms from the book Artificial Intelligence a Modern Approach 3rd Ed.

The newest version!
package aima.core.environment.connectfour;

/**
 * A state of the Connect Four game is characterized by a board containing a
 * grid of spaces for disks, the next player to move, and some utility
 * informations. A win position for a player x is an empty space which turns a
 * situation into a win situation for x if he is able to place a disk there.
 * 
 * @author Ruediger Lunde
 * 
 */
public class ConnectFourState implements Cloneable {
	private int cols;
	/**
	 * Uses special bit coding. First bit: disk of player 1, second bit: disk of
	 * player 2, third bit: win position for player 1, fourth bit: win position
	 * for player 2.
	 */
	private byte[] board;

	private int moveCount;
	/**
	 * Indicates the utility of the state. 1: win for player 1, 0: win for
	 * player 2, 0.5: draw, -1 for all non-terminal states.
	 */
	private double utility;
	public int winPositions1;
	public int winPositions2;

	public ConnectFourState(int rows, int cols) {
		utility = -1;
		this.cols = cols;
		board = new byte[rows * cols];
	}

	public int getRows() {
		return board.length / cols;
	}

	public int getCols() {
		return cols;
	}

	public double getUtility() {
		return utility;
	}

	public int getPlayerNum(int row, int col) {
		return board[row * cols + col] & 3;
	}

	public int getPlayerToMove() {
		return moveCount % 2 + 1;
	}
	
	public int getMoves() {
		return moveCount;
	}

	public void dropDisk(int col) {
		int playerNum = getPlayerToMove();
		int row = getFreeRow(col);
		if (row != -1) {
			moveCount++;
			if (moveCount == board.length)
				utility = 0.5;
			if (isWinPositionFor(row, col, 1)) {
				winPositions1--;
				if (playerNum == 1)
					utility = 1.0;
			}
			if (isWinPositionFor(row, col, 2)) {
				winPositions2--;
				if (playerNum == 2)
					utility = 0.0;
			}
			board[row * cols + col] = (byte) playerNum;
			if (utility == -1)
				analyzeWinPositions(row, col);
		}
	}

	/**
	 * Returns the row of the first empty space in the specified column and -1
	 * if the column is full.
	 */
	private int getFreeRow(int col) {
		for (int row = getRows() - 1; row >= 0; row--)
			if (getPlayerNum(row, col) == 0)
				return row;
		return -1;
	}

	public boolean isWinMoveFor(int col, int playerNum) {
		return isWinPositionFor(getFreeRow(col), col, playerNum);
	}
	
	public boolean isWinPositionFor(int row, int col, int playerNum) {
		return (board[row * cols + col] & playerNum * 4) > 0;
	}

	private void setWinPositionFor(int row, int col, int playerNum) {
		if (playerNum == 1) {
			if (!isWinPositionFor(row, col, 1))
				winPositions1++;
		} else if (playerNum == 2) {
			if (!isWinPositionFor(row, col, 2))
				winPositions2++;
		} else {
			throw new IllegalArgumentException("Wrong player number.");
		}
		board[row * cols + col] |= playerNum * 4;
	}

	/**
	 * Assumes a disk at position moveRow and moveCol
	 * and analyzes the vicinity with respect to win positions.
	 */
	private void analyzeWinPositions(int moveRow, int moveCol) {
		final int[] rowIncr = new int[] { 1, 0, 1, 1 };
		final int[] colIncr = new int[] { 0, 1, -1, 1 };
		int playerNum = getPlayerNum(moveRow, moveCol);
		WinPositionInfo[] wInfo = new WinPositionInfo[] {
				new WinPositionInfo(), new WinPositionInfo() };
		for (int i = 0; i < 4; i++) {
			int rIncr = rowIncr[i];
			int cIncr = colIncr[i];
			int diskCount = 1;

			for (int j = 0; j < 2; j++) {
				WinPositionInfo wInf = wInfo[j];
				wInf.clear();
				int rBound = rIncr > 0 ? getRows() : -1;
				int cBound = cIncr > 0 ? getCols() : -1;

				int row = moveRow + rIncr;
				int col = moveCol + cIncr;
				while (row != rBound && col != cBound) {
					int plNum = getPlayerNum(row, col);
					if (plNum == playerNum) {
						if (wInf.hasData())
							wInf.diskCount++;
						else
							diskCount++;
					} else if (plNum == 0) {
						if (!wInf.hasData()) {
							wInf.row = row;
							wInf.col = col;
						} else {
							break;
						}
					} else {
						break;
					}
					row += rIncr;
					col += cIncr;
				}
				rIncr = -rIncr;
				cIncr = -cIncr;
			}
			for (int j = 0; j < 2; j++) {
				WinPositionInfo wInf = wInfo[j];
				if (wInf.hasData() && diskCount + wInf.diskCount >= 3) {
					setWinPositionFor(wInf.row, wInf.col, playerNum);
				}
			}
		}
	}

	public int analyzePotentialWinPositions(Integer action) {
		final int[] rowIncr = new int[] { 1, 0, 1, 1 };
		final int[] colIncr = new int[] { 0, 1, -1, 1 };
		int moveCol = action;
		int moveRow = getFreeRow(moveCol);

		int playerNum = getPlayerToMove();
		int result = 0;
		for (int i = 0; i < 4; i++) {
			int rIncr = rowIncr[i];
			int cIncr = colIncr[i];
			int posCountSum = 0;

			for (int j = 0; j < 2; j++) {
				int rBound = rIncr > 0 ? getRows() : -1;
				int cBound = cIncr > 0 ? getCols() : -1;
				int posCount = 0;

				int row = moveRow + rIncr;
				int col = moveCol + cIncr;
				while (row != rBound && col != cBound && posCount < 3) {
					int plNum = getPlayerNum(row, col);
					if (plNum == 3 - playerNum)
						break;
					posCount++;
					row += rIncr;
					col += cIncr;
				}
				posCountSum += posCount;
				rIncr = -rIncr;
				cIncr = -cIncr;
			}
			if (posCountSum >= 3)
				result += posCountSum;
		}
		return result;
	}

	public ConnectFourState clone() {
		ConnectFourState result = null;
		try {
			result = (ConnectFourState) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		result.board = board.clone();
		return result;
	}

	@Override
	public int hashCode() {
		int result = 0;
		for (int i = 0; i < board.length; i++)
			result = result * 7 + board[i] + 1;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof ConnectFourState) {
			ConnectFourState s = (ConnectFourState) obj;
			for (int i = 0; i < board.length; i++)
				if (board[i] != s.board[i])
					return false;
			return true;
		}
		return false;
	}

	// ////////////////////////////////////////////////////////////////////
	// nested classes

	static class WinPositionInfo {
		int row = -1;
		int col = -1;
		int diskCount;

		void clear() {
			row = -1;
			col = -1;
			diskCount = 0;
		}

		boolean hasData() {
			return row != -1;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy