package com.captstudios.games.tafl.core.es.model.ai.evaluators; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.IntSet; import com.captstudios.games.tafl.core.consts.Constants; import com.captstudios.games.tafl.core.es.model.TaflBoard; import com.captstudios.games.tafl.core.es.model.ai.optimization.BitBoard; import com.captstudios.games.tafl.core.es.model.ai.optimization.moves.Move; public class CompleteEvaluator implements BoardEvaluator<TaflBoard> { private static final int WIN = 100000; private static final int LOSS = -WIN; private static final int NEAR_WIN = WIN / 4; private static final int WHITE_PIECE_VALUE = 5; private static final int BLACK_PIECE_VALUE = 10; private static final int WHITE_PIECE_VULNERABILITY_VALUE = 3; private static final int BLACK_PIECE_VULNERABILITY_VALUE = 5; private static final int WHITE_OPPOSITE_PIECE_VULNERABILITY_VALUE = 4; private static final int BLACK_OPPOSITE_PIECE_VULNERABILITY_VALUE = 2; private static final int KING_MOBILITY = 5; private static final int KING_EMPTY_RANK_2 = 90; private static final int KING_EMPTY_RANK_3 = 45; private static final int KING_EMPTY_FILE_2 = 90; private static final int KING_EMPTY_FILE_3 = 70; private static final int CORNER_PROTECTION_BONUS = 10; private static final int WHITE_CORNER_PROTECTION_BONUS = 30; private static final int DANGER_SQUARE_BONUS = -2; private static final int FULL_BARRICADE_BONUS = 25; private static final int REPEAT_MOVE_PENALTY = -5; public BitBoard tempBitBoard; public BitBoard allPiecesBoard; public BitBoard cornerProtection; public BitBoard north; public BitBoard south; public BitBoard east; public BitBoard west; public IntArray barricadeStack; public IntSet barricadeSet; public Array<Move> moves; public CompleteEvaluator(TaflBoard board) { tempBitBoard = new BitBoard(board.boardSize); allPiecesBoard = new BitBoard(board.boardSize); cornerProtection = new BitBoard(board.boardSize); for (int element : board.boardType.barricades) { cornerProtection.set(element); } int half = board.dimensions / 2; north = new BitBoard(board.boardSize); south = new BitBoard(board.boardSize); east = new BitBoard(board.boardSize); west = new BitBoard(board.boardSize); for (int i = 0; i < board.dimensions; i++) { for (int j = 0; j < board.dimensions; j++) { if (i < half) { south.set(i * board.dimensions + j); } else if (i > half) { north.set(i * board.dimensions + j); } if (j < half) { west.set(i * board.dimensions + j); } else if (j > half) { east.set(i * board.dimensions + j); } } } barricadeStack = new IntArray(); barricadeSet = new IntSet(); moves = new Array<Move>(Constants.GameConstants.DRAW_MOVE_THRESHHOLD); } @Override public int evaluate(TaflBoard board, int turn) { board.rules.generateLegalMoves(Constants.BoardConstants.WHITE_TEAM); board.rules.generateLegalMoves(Constants.BoardConstants.BLACK_TEAM); int value = 0; if (board.rules.isGameOver(turn)) { int winner = board.rules.checkWinner(); if (winner == Constants.BoardConstants.NO_TEAM) { return 0; } else if (winner == turn) { return WIN; } else { return LOSS; } } else { value += detectBarricade(board, turn); value += kingMobility(board, turn); value += squareWeight(board, turn); value += materialBallance(board, turn); value += checkRankAndFiles(board, turn); value += checkKingRanksAndFiles(board, turn); value += checkRepeatMoves(board, turn); } return value; } private int detectBarricade(TaflBoard board, int turn) { int value = 0; BitBoard blackBoard = board.blackBitBoard(); BitBoard whiteBoard = board.whiteBitBoard(); if (checkBarricade(board, 0, blackBoard, whiteBoard, 1, board.dimensions) > 0) { if (south.intersects(board.kingBitBoard())) { value += FULL_BARRICADE_BONUS; } if (west.intersects(board.kingBitBoard())) { value += FULL_BARRICADE_BONUS; } } if (checkBarricade(board, 10, blackBoard, whiteBoard, -1, board.dimensions) > 0) { if (south.intersects(board.kingBitBoard())) { value += FULL_BARRICADE_BONUS; } if (east.intersects(board.kingBitBoard())) { value += FULL_BARRICADE_BONUS; } } if (checkBarricade(board, 110, blackBoard, whiteBoard, 1, -board.dimensions) > 0) { if (north.intersects(board.kingBitBoard())) { value += FULL_BARRICADE_BONUS; } if (west.intersects(board.kingBitBoard())) { value += FULL_BARRICADE_BONUS; } } if (checkBarricade(board, 120, blackBoard, whiteBoard, -1, -board.dimensions) > 0) { if (north.intersects(board.kingBitBoard())) { value += FULL_BARRICADE_BONUS; } if (east.intersects(board.kingBitBoard())) { value += FULL_BARRICADE_BONUS; } } return value; } private int checkBarricade(TaflBoard board, int start, BitBoard blackBoard, BitBoard whiteBoard, int xDirection, int yDirection) { int baseCornerCellx = start + xDirection * 2; int baseCornerCelly = start + yDirection * 2; int baseCornerCellxy = start + xDirection + yDirection; int dangerCornerCellx = start + xDirection; int dangerCornerCelly = start + yDirection; if (blackBoard.get(baseCornerCellx) && blackBoard.get(baseCornerCelly) && blackBoard.get(baseCornerCellxy) && !whiteBoard.get(dangerCornerCellx) && !whiteBoard.get(dangerCornerCelly)) { return 3; } else if (whiteBoard.get(dangerCornerCellx) || whiteBoard.get(dangerCornerCelly)) { return -1; } else { barricadeSet.clear(); barricadeStack.clear(); barricadeSet.add(baseCornerCellx); barricadeSet.add(baseCornerCelly); barricadeSet.add(baseCornerCellxy); barricadeStack.add(baseCornerCellx); barricadeStack.add(baseCornerCelly); barricadeStack.add(baseCornerCellxy); int barricadeCount = 2; while (barricadeStack.size > 0) { int current = barricadeStack.pop(); if (whiteBoard.get(current)) { return -1; } if (blackBoard.get(current)) { barricadeCount++; } else { int nextX = current + xDirection; if (board.isValid(nextX) && board.inRow(current, nextX) && !barricadeSet.contains(nextX)) { barricadeStack.add(nextX); barricadeSet.add(nextX); } int nextY = current + yDirection; if (board.isValid(nextY) && !barricadeSet.contains(nextY)) { barricadeStack.add(nextY); barricadeSet.add(nextY); } } } return barricadeCount; } } private int kingMobility(TaflBoard board, int turn) { int value = 0; value += board.rules.getLegalMoves(Constants.BoardConstants.WHITE_TEAM, board.getKing()).cardinality() * KING_MOBILITY; if (turn == Constants.BoardConstants.BLACK_TEAM) { value *= -1; } return value; } private int squareWeight(TaflBoard board, int turn) { int value = 0; BitBoard whiteBoard = board.whiteBitBoard(); BitBoard blackBoard = board.blackBitBoard(); BitBoard yHemisphere = null; if (north.intersects(board.kingBitBoard())) { yHemisphere = north; } else if (south.intersects(board.kingBitBoard())) { yHemisphere = south; } BitBoard xHemisphere = null; if (west.intersects(board.kingBitBoard())) { xHemisphere = west; } else if (east.intersects(board.kingBitBoard())) { xHemisphere = east; } for (int i = blackBoard.nextSetBit(0); i >= 0; i = blackBoard.nextSetBit(i + 1)) { if (cornerProtection.get(i)) { if (board.kingBitBoard().get(board.center)) { value += CORNER_PROTECTION_BONUS; } else { if (yHemisphere != null && yHemisphere.get(i)) { value += CORNER_PROTECTION_BONUS * 2; } if (xHemisphere != null && xHemisphere.get(i)) { value += CORNER_PROTECTION_BONUS * 2; } } } else if (board.nearCorners.get(i)) { value += DANGER_SQUARE_BONUS; } else if (board.nearCenter.get(i)) { value += DANGER_SQUARE_BONUS; } } for (int i = whiteBoard.nextSetBit(0); i >= 0; i = whiteBoard.nextSetBit(i + 1)) { if (cornerProtection.get(i)) { value -= WHITE_CORNER_PROTECTION_BONUS; } else if (board.nearCorners.get(i)) { value -= DANGER_SQUARE_BONUS; } else if (board.nearCenter.get(i)) { value -= DANGER_SQUARE_BONUS; } } if (turn == Constants.BoardConstants.WHITE_TEAM) { value *= -1; } return value; } private int materialBallance(TaflBoard board, int turn) { int value = 0; int oppositTeam = (turn + 1) % 2; BitBoard turnBoard = board.bitBoards[turn]; BitBoard oppositeBoard = board.bitBoards[oppositTeam]; int pieceValue = WHITE_PIECE_VALUE; int pieceVulnerability = WHITE_PIECE_VULNERABILITY_VALUE; int oppositeVulnerability = WHITE_OPPOSITE_PIECE_VULNERABILITY_VALUE; if (turn == Constants.BoardConstants.BLACK_TEAM) { pieceValue = BLACK_PIECE_VALUE; pieceVulnerability = BLACK_PIECE_VULNERABILITY_VALUE; oppositeVulnerability = BLACK_OPPOSITE_PIECE_VULNERABILITY_VALUE; } for (int i = turnBoard.nextSetBit(0); i >= 0; i = turnBoard.nextSetBit(i + 1)) { value += pieceValue - (board.rules.isVulnerable(turn, i) ? pieceVulnerability : 0); } for (int i = oppositeBoard.nextSetBit(0); i >= 0; i = oppositeBoard.nextSetBit(i + 1)) { value -= pieceValue - (board.rules.isVulnerable(oppositTeam, i) ? oppositeVulnerability : 0); } return value; } private int checkRankAndFiles(TaflBoard board, int turn) { float value = 0; BitBoard blackBoard = board.blackBitBoard(); for (int i = 0; i < board.dimensions; i++) { int blackRowCount = BitBoard.intersectionCount(blackBoard, board.rows[i]); int blackColumnCount = BitBoard.intersectionCount(blackBoard, board.columns[i]); if (turn == Constants.BoardConstants.WHITE_TEAM) { if (blackRowCount == 0) { value++; } if (blackColumnCount == 0) { value++; } } else { value += 1 + blackRowCount / 4.0f; value += 1 + blackColumnCount / 4.0f; } } return (int) value; } private int checkKingRanksAndFiles(TaflBoard board, int turn) { int value = 0; int king = board.getKing(); if (board.nearCorners.get(king)) { return NEAR_WIN; } // all the pieces that are in the same column as the king allPiecesBoard.set(board.whiteBitBoard()).or(board.blackBitBoard()); tempBitBoard.set(allPiecesBoard).and(board.getColumn(king)); int kingColumn = king % board.dimensions; int kingRow = king / board.dimensions; int piecesAbove = 0; for (int i = tempBitBoard.nextSetBit(king + 1); i >= 0; i = tempBitBoard.nextSetBit(i+1)) { piecesAbove++; } if (piecesAbove == 0) { if (kingColumn == 1 || kingColumn == board.dimensions - 2) { value += KING_EMPTY_RANK_2; } else if (kingColumn == 2 || kingColumn == board.dimensions - 3) { value += KING_EMPTY_RANK_3; } } int piecesBelow = 0; for (int i = tempBitBoard.prevSetBit(king - 1); i >= 0; i = tempBitBoard.prevSetBit(i-1)) { piecesBelow++; } if (piecesBelow == 0) { if (kingColumn == 1 || kingColumn == board.dimensions - 2) { value += KING_EMPTY_RANK_2; } else if (kingColumn == 2 || kingColumn == board.dimensions - 3) { value += KING_EMPTY_RANK_3; } } // all the pieces that are in the same row as the king tempBitBoard.set(allPiecesBoard).and(board.getRow(king)); int piecesLeft = 0; for (int i = tempBitBoard.nextSetBit(king + 1); i >= 0; i = tempBitBoard.nextSetBit(i+1)) { piecesAbove++; } if (piecesLeft == 0) { if (kingRow == 1 || kingRow == board.dimensions - 2) { value += KING_EMPTY_FILE_2; } else if (kingRow == 2 || kingRow == board.dimensions - 3) { value += KING_EMPTY_FILE_3; } } int piecesRight = 0; for (int i = tempBitBoard.prevSetBit(king - 1); i >= 0; i = tempBitBoard.prevSetBit(i-1)) { piecesBelow++; } if (piecesRight == 0) { if (kingRow == 1 || kingRow == board.dimensions - 2) { value += KING_EMPTY_FILE_2; } else if (kingRow == 2 || kingRow == board.dimensions - 3) { value += KING_EMPTY_FILE_3; } } if (turn == Constants.BoardConstants.BLACK_TEAM) { value *= -1; } return value; } private int checkRepeatMoves(TaflBoard board, int turn) { int value = 0; moves.clear(); if (board.undoStack.size + board.simulatedStack.size > 4) { moves.addAll(board.undoStack); moves.addAll(board.simulatedStack); Move previousMove = moves.get(moves.size - 2); for (int i = moves.size - 4; i >= 0 && i >= moves.size - 13; i-=2) { Move currentMove = moves.get(i); if (previousMove.source == currentMove.destination && previousMove.destination == currentMove.source) { value += REPEAT_MOVE_PENALTY; } previousMove = currentMove; } } return value; } }