//можно ли поставить фишку public bool CanSetFieldColor(int rowIndex, int columnIndex, DiscColor color) { bool hasColor = (this[rowIndex, columnIndex] != DiscColor.None);//если в этой клетке есть фишка if (!hasColor)//если поле не имеет цвет(в клетке нет фишки) { if (color == DiscColor.None)//если цвет равен прозрачному { return true; } else { for (int rowIndexChange = -1; rowIndexChange <= 1; rowIndexChange++) { for (int columnIndexChange = -1; columnIndexChange <= 1; columnIndexChange++) { if ((rowIndexChange != 0) || (columnIndexChange != 0)) { if (this.CheckDirection(rowIndex, columnIndex, rowIndexChange, columnIndexChange, color)) { return true; } } } } } } return false; }
//функция оценки доски private int EvaluateBoard(Board board) { DiscColor color = this.Player.Color; DiscColor oppositeColor = DiscColor.GetOpposite(this.Player.Color); //списки предположительных ходов игроков List <int[]> oppositePlayerPossibleMoves = this.GetPossibleMoves(board, oppositeColor); List <int[]> possibleMoves = this.GetPossibleMoves(board, color); if ((possibleMoves.Count == 0) && (oppositePlayerPossibleMoves.Count == 0)) //если списки пусты { int result = board.GetDiscsCount(color) - board.GetDiscsCount(oppositeColor); //от количества фишек ии отнимает количество фишек противника int addend = (int)Math.Pow(board.Size, 4) + (int)Math.Pow(board.Size, 3); // потому что это конечное состояние, его вес должен быть больше эвристического if (result < 0) //если разность меньше 0 { addend = -addend; } return(result + addend); } else//если ходы есть { //количество фишек противника, которые могут быть преобразованы int mobility = this.GetPossibleConvertions(board, color, possibleMoves) - this.GetPossibleConvertions(board, oppositeColor, oppositePlayerPossibleMoves); //число стабильных фишек на доске игрока int stability = (this.GetStableDiscsCount(board, color) - this.GetStableDiscsCount(board, oppositeColor)) * board.Size * 2 / 3; return(mobility + stability); } }
private int EvaluateBoard(Board board) { DiscColor color = this.Player.Color; DiscColor oppositeColor = DiscColor.GetOpposite(this.Player.Color); List <int[]> oppositePlayerPossibleMoves = this.GetPossibleMoves(board, oppositeColor); List <int[]> possibleMoves = this.GetPossibleMoves(board, color); if ((possibleMoves.Count == 0) && (oppositePlayerPossibleMoves.Count == 0)) { int result = board.GetDiscsCount(color) - board.GetDiscsCount(oppositeColor); int addend = (int)Math.Pow(board.Size, 4) + (int)Math.Pow(board.Size, 3); // because it is a terminal state, its weight must be bigger than the heuristic ones if (result < 0) { addend = -addend; } return(result + addend); } else { int mobility = this.GetPossibleConvertions(board, color, possibleMoves) - this.GetPossibleConvertions(board, oppositeColor, oppositePlayerPossibleMoves); int stability = (this.GetStableDiscsCount(board, color) - this.GetStableDiscsCount(board, oppositeColor)) * board.Size * 2 / 3; return(mobility + stability); } }
public bool CanSetFieldColor(int rowIndex, int columnIndex, DiscColor color) { bool hasColor = (this[rowIndex, columnIndex] != DiscColor.None); if (!hasColor) { if (color == DiscColor.None) { return(true); } else { for (int rowIndexChange = -1; rowIndexChange <= 1; rowIndexChange++) { for (int columnIndexChange = -1; columnIndexChange <= 1; columnIndexChange++) { if ((rowIndexChange != 0) || (columnIndexChange != 0)) { if (this.CheckDirection(rowIndex, columnIndex, rowIndexChange, columnIndexChange, color)) { return(true); } } } } } } return(false); }
private List <int[]> GetPossibleMoves(Board board, DiscColor color) { List <int[]> possibleMoves = new List <int[]>(); for (int rowIndex = 0; rowIndex < board.Size; rowIndex++) { for (int columnIndex = 0; columnIndex < board.Size; columnIndex++) { if (board.CanSetFieldColor(rowIndex, columnIndex, color)) { possibleMoves.Add(new int[2] { rowIndex, columnIndex }); } } } List <int> indexes = this.GetRandomIndexes(possibleMoves.Count); List <int[]> result = new List <int[]>(); foreach (int index in indexes) { result.Add(possibleMoves[index]); } return(result); }
private void OnMoveFinished(int rowIndex, int columnIndex, DiscColor color) { if (!this.mIsStopped) { if (this.MoveFinished != null) { this.MoveFinished(rowIndex, columnIndex, color); } Player opositePlayer = (this.CurrentPlayer == this.Player1) ? this.Player2 : this.Player1; if (opositePlayer.CanMove()) { this.SetPlayerToMove(opositePlayer); } else if (this.CurrentPlayer.CanMove()) { this.SetPlayerToMove(this.CurrentPlayer); } else { this.mCurrentPlayer = null; this.mIsFinished = true; if (this.Finished != null) { this.Finished(); } } } }
//возвращает количество стабильных фишек (фишки, которые не могут быть перевернуты в дальнейшей игре никаким образом) public int GetStableDiscsCount(Board board, DiscColor color) { return(this.GetStableDiscsFromCorner(board, color, 0, 0) //левый верхний угол + this.GetStableDiscsFromCorner(board, color, 0, board.Size - 1) //правый верхний + this.GetStableDiscsFromCorner(board, color, board.Size - 1, 0) //левый нижний + this.GetStableDiscsFromCorner(board, color, board.Size - 1, board.Size - 1) //правый нижний + this.GetStableDiscsFromEdge(board, color, 0, true) //верх + this.GetStableDiscsFromEdge(board, color, board.Size - 1, true) //низ + this.GetStableDiscsFromEdge(board, color, 0, false) //левая граница + this.GetStableDiscsFromEdge(board, color, board.Size - 1, false)); //правая граница }
public int GetStableDiscsCount(Board board, DiscColor color) { return(this.GetStableDiscsFromCorner(board, color, 0, 0) + this.GetStableDiscsFromCorner(board, color, 0, board.Size - 1) + this.GetStableDiscsFromCorner(board, color, board.Size - 1, 0) + this.GetStableDiscsFromCorner(board, color, board.Size - 1, board.Size - 1) + this.GetStableDiscsFromEdge(board, color, 0, true) + this.GetStableDiscsFromEdge(board, color, board.Size - 1, true) + this.GetStableDiscsFromEdge(board, color, 0, false) + this.GetStableDiscsFromEdge(board, color, board.Size - 1, false)); }
public void SetFieldColor(int rowIndex, int columnIndex, DiscColor color) { if (this.CanSetFieldColor(rowIndex, columnIndex, color)) { this.mFieldColors[rowIndex, columnIndex] = color; this.InvertOpponentDisks(rowIndex, columnIndex, color, out this.mInvertedDiscsLastMove); if (this.MoveFinished != null) { this.MoveFinished(rowIndex, columnIndex, color); } } }
private Player CreatePlayer(PlayerProperties properties, DiscColor color) { switch (properties.Type) { case PlayerType.Human: return(new HumanPlayer(this, color, properties.Name)); case PlayerType.Computer: return(new ComputerPlayer(this, color, properties.Name, properties.MaxDepth)); } return(null); }
public bool CanSetAnyField(DiscColor color) { for (int rowIndex = 0; rowIndex < this.Size; rowIndex++) { for (int columnIndex = 0; columnIndex < this.Size; columnIndex++) { if (this.CanSetFieldColor(rowIndex, columnIndex, color)) { return(true); } } } return(false); }
private int GetStableDiscsFromCorner(Board board, DiscColor color, int cornerRowIndex, int cornerColumnIndex) { int result = 0; int rowIndexChange = (cornerRowIndex == 0) ? 1 : -1; int columnIndexChange = (cornerColumnIndex == 0) ? 1 : -1; int rowIndex = cornerRowIndex; int rowIndexLimit = (cornerRowIndex == 0) ? board.Size : 0; int columnIndexLimit = (cornerColumnIndex == 0) ? board.Size : 0; for (rowIndex = cornerRowIndex; rowIndex != rowIndexLimit; rowIndex += rowIndexChange) { int columnIndex; for (columnIndex = cornerColumnIndex; columnIndex != columnIndexLimit; columnIndex += columnIndexChange) { if (board[rowIndex, columnIndex] == color) { result++; } else { break; } } if ((columnIndexChange > 0 && columnIndex < board.Size) || (columnIndexChange < 0 && columnIndex > 0)) { columnIndexLimit = columnIndex - columnIndexChange; if (columnIndexChange > 0 && columnIndexLimit == 0) { columnIndexLimit++; } else if (columnIndexChange < 0 && columnIndexLimit == board.Size - 1) { columnIndexLimit--; } if ((columnIndexChange > 0 && columnIndexLimit < 0) || (columnIndexChange < 0 && columnIndexLimit > board.Size - 1)) { break; } } } return(result); }
// Области в углу и по краям позволят легко завладеть большим //количеством фишек противника сразу.Таким образом, ии //должен занимать эти позиции, если это возможно. //Получить стабильные фишки из углов private int GetStableDiscsFromCorner(Board board, DiscColor color, int cornerRowIndex, int cornerColumnIndex) { int result = 0; int rowIndexChange = (cornerRowIndex == 0) ? 1 : -1; int columnIndexChange = (cornerColumnIndex == 0) ? 1 : -1; int rowIndex = cornerRowIndex; int rowIndexLimit = (cornerRowIndex == 0) ? board.Size : 0; int columnIndexLimit = (cornerColumnIndex == 0) ? board.Size : 0; for (rowIndex = cornerRowIndex; rowIndex != rowIndexLimit; rowIndex += rowIndexChange) { int columnIndex; for (columnIndex = cornerColumnIndex; columnIndex != columnIndexLimit; columnIndex += columnIndexChange) //цикл подсчета результата { if (board[rowIndex, columnIndex] == color) //если цвет поля нужный { result++; } else {//иначе переход на следующую строку (выход из цикла) break; } } if ((columnIndexChange > 0 && columnIndex < board.Size) || (columnIndexChange < 0 && columnIndex > 0)) { columnIndexLimit = columnIndex - columnIndexChange; if (columnIndexChange > 0 && columnIndexLimit == 0) { columnIndexLimit++; } else if (columnIndexChange < 0 && columnIndexLimit == board.Size - 1) { columnIndexLimit--; } if ((columnIndexChange > 0 && columnIndexLimit < 0) || (columnIndexChange < 0 && columnIndexLimit > board.Size - 1)) { break; } } } return(result); }
private int GetPossibleConvertions(Board board, DiscColor color, List <int[]> possibleMoves) { int result = 0; foreach (int[] move in possibleMoves) { Board newBoard = board.Clone() as Board; int rowIndex = move[0]; int columnIndex = move[1]; newBoard.SetFieldColor(rowIndex, columnIndex, color); result += newBoard.InvertedDiscsLastMove; } return(result); }
//подсчет количества фишек public int GetDiscsCount(DiscColor color) { int result = 0; for (int rowIndex = 0; rowIndex < this.Size; rowIndex++) { for (int columnIndex = 0; columnIndex < this.Size; columnIndex++) { if (this[rowIndex, columnIndex] == color) { result++; } } } return result; }
//замена цвета противоположным public static DiscColor GetOpposite(DiscColor color) { if (color == DiscColor.Black) { return(DiscColor.White); } else if (color == DiscColor.White) { return(DiscColor.Black); } else { return(DiscColor.None); } }
private void InvertOpponentDisks(int rowIndex, int columnIndex, DiscColor color, out int invertedDiscsCount) { invertedDiscsCount = 0; for (int rowIndexChange = -1; rowIndexChange <= 1; rowIndexChange++) { for (int columnIndexChange = -1; columnIndexChange <= 1; columnIndexChange++) { if ((rowIndexChange != 0) || (columnIndexChange != 0)) { if (this.CheckDirection(rowIndex, columnIndex, rowIndexChange, columnIndexChange, color)) { this.InvertDirection(rowIndex, columnIndex, rowIndexChange, columnIndexChange, color, ref invertedDiscsCount); } } } } }
private void InvertDirection( int rowIndex, int columnIndex, int rowIndexChange, int columnIndexChange, DiscColor color, ref int invertedDiscsCount) { DiscColor opositeColor = DiscColor.GetOpposite(color); rowIndex += rowIndexChange; columnIndex += columnIndexChange; while (this[rowIndex, columnIndex] == opositeColor) { this.mFieldColors[rowIndex, columnIndex] = color; invertedDiscsCount++; rowIndex += rowIndexChange; columnIndex += columnIndexChange; } }
private int GetStableDiscsFromEdge(Board board, DiscColor color, int edgeCoordinate, bool isHorizontal) { int result = 0; if (IsEdgeFull(board, edgeCoordinate, isHorizontal)) { bool oppositeColorDiscsPassed = false; for (int otherCoordinate = 0; otherCoordinate < board.Size; otherCoordinate++) { DiscColor fieldColor = (isHorizontal) ? board[edgeCoordinate, otherCoordinate] : board[otherCoordinate, edgeCoordinate]; if (fieldColor != color) { oppositeColorDiscsPassed = true; } else if (oppositeColorDiscsPassed) { int consecutiveDiscsCount = 0; while ((otherCoordinate < board.Size) && (fieldColor == color)) { consecutiveDiscsCount++; otherCoordinate++; if (otherCoordinate < board.Size) { fieldColor = (isHorizontal) ? board[edgeCoordinate, otherCoordinate] : board[otherCoordinate, edgeCoordinate]; } } if (otherCoordinate != board.Size) { result += consecutiveDiscsCount; oppositeColorDiscsPassed = true; } } } } return(result); }
// kiem tra xem co danh dc khong private bool CheckDirection(int rowIndex, int columnIndex, int rowIndexChange, int columnIndexChange, DiscColor color) { bool areOpositeColorDiscsFound = false; rowIndex += rowIndexChange; columnIndex += columnIndexChange; while ((rowIndex >= 0) && (rowIndex < this.Size) && (columnIndex >= 0) && (columnIndex < this.Size)) { if (areOpositeColorDiscsFound) { if (this[rowIndex, columnIndex] == color) { return(true); } else if (this[rowIndex, columnIndex] == DiscColor.None) { return(false); } } else { DiscColor opositeColor = DiscColor.GetOpposite(color); if (this[rowIndex, columnIndex] == opositeColor) { areOpositeColorDiscsFound = true; } else { return(false); } } rowIndex += rowIndexChange; columnIndex += columnIndexChange; } return(false); }
//проверка направлени¤ private bool CheckDirection(int rowIndex, int columnIndex, int rowIndexChange, int columnIndexChange, DiscColor color) { bool areOpositeColorDiscsFound = false;//поверка найден ли противоположный цвет rowIndex += rowIndexChange; columnIndex += columnIndexChange; while ((rowIndex >= 0) && (rowIndex < this.Size) && (columnIndex >= 0) && (columnIndex < this.Size)) { if (areOpositeColorDiscsFound)//если найдена клетка с противоположным цветом { if (this[rowIndex, columnIndex] == color) { return true; } else if (this[rowIndex, columnIndex] == DiscColor.None) { return false; } } else { DiscColor opositeColor = DiscColor.GetOpposite(color); if (this[rowIndex, columnIndex] == opositeColor) { areOpositeColorDiscsFound = true; } else { return false; } } rowIndex += rowIndexChange; columnIndex += columnIndexChange; } return false; }
internal Player(Game game, DiscColor color, string name) { this.mGame = game; this.mColor = color; this.mName = name; }
public HumanPlayer(Game game, DiscColor color, string name) : base(game, color, name) { }
private int GetNextMove(Board board, bool isMaximizing, int currentDepth, int alpha, int beta, out int resultRowIndex, out int resultColumnIndex) { resultRowIndex = 0; resultColumnIndex = 0; DiscColor color = isMaximizing ? this.Player.Color : DiscColor.GetOpposite(this.Player.Color); bool playerSkipsMove = false; List <int[]> possibleMoves = new List <int[]>(); bool isFinalMove = (currentDepth >= this.MaxDepth) || this.Player.Game.IsStopped || this.Player.Game.IsPaused; if (!isFinalMove) { possibleMoves = this.GetPossibleMoves(board, color); if (possibleMoves.Count == 0) { playerSkipsMove = true; possibleMoves = this.GetPossibleMoves(board, DiscColor.GetOpposite(color)); } isFinalMove = (possibleMoves.Count == 0); } if (isFinalMove) { resultRowIndex = -1; resultColumnIndex = -1; return(this.EvaluateBoard(board)); } else { int bestBoardValue = isMaximizing ? MIN_BOARD_VALUE : MAX_BOARD_VALUE; int bestMoveRowIndex = -1; int bestMoveColumnIndex = -1; foreach (int[] nextMove in possibleMoves) { int rowIndex = nextMove[0]; int columnIndex = nextMove[1]; Board nextBoard = (Board)board.Clone(); nextBoard.SetFieldColor(rowIndex, columnIndex, color); bool nextIsMaximizing = playerSkipsMove ? isMaximizing : !isMaximizing; int dummyIndex; // values of resultRowIndex and resultColumnIndex are not needed in recursive function calls int currentBoardValue = this.GetNextMove(nextBoard, nextIsMaximizing, currentDepth + 1, alpha, beta, out dummyIndex, out dummyIndex); if (isMaximizing) { if (currentBoardValue > bestBoardValue) { bestBoardValue = currentBoardValue; bestMoveRowIndex = rowIndex; bestMoveColumnIndex = columnIndex; if (bestBoardValue > alpha) { alpha = bestBoardValue; } if (bestBoardValue >= beta) { break; } } } else { if (currentBoardValue < bestBoardValue) { bestBoardValue = currentBoardValue; bestMoveRowIndex = rowIndex; bestMoveColumnIndex = columnIndex; if (bestBoardValue < beta) { beta = bestBoardValue; } if (bestBoardValue <= alpha) { break; } } } } resultRowIndex = bestMoveRowIndex; resultColumnIndex = bestMoveColumnIndex; return(bestBoardValue); } }
//процедура для осуществления следующего хода. алгоритм АВ-отсечения. ищет лучший ход. возвращает BestBoardValue в виде числа (вероятности) private int GetNextMove(Board board, bool isMaximizing, int currentDepth, int alpha, int beta, out int resultRowIndex, out int resultColumnIndex) { resultRowIndex = 0; resultColumnIndex = 0; //цвет фишки DiscColor color = isMaximizing ? this.Player.Color : DiscColor.GetOpposite(this.Player.Color);//если true DiskColor = Player.Color иначе противоположный цвет bool playerSkipsMove = false; List <int[]> possibleMoves = new List <int[]>(); //проверка на последний ход bool isFinalMove = (currentDepth >= this.MaxDepth) || this.Player.Game.IsStopped || this.Player.Game.IsPaused; //если ход не последний if (!isFinalMove) { //получение возможных ходов. possibleMoves = this.GetPossibleMoves(board, color); if (possibleMoves.Count == 0) //если нет возможных ходов { playerSkipsMove = true; possibleMoves = this.GetPossibleMoves(board, DiscColor.GetOpposite(color));//получить список возможных ходов для противоположного игрока } isFinalMove = (possibleMoves.Count == 0); //проверка на последний ход (если список пуст вернет true) } //если используем на самом "дне" глубины подсчета if (isFinalMove) { resultRowIndex = -1; resultColumnIndex = -1; return(this.EvaluateBoard(board));//возвращает оценочную эвристическую функцию (mobility+stability) } else//если макс. глубине не достигнута { //поиск наилучшего хода (просмотр дерева) int bestBoardValue = isMaximizing ? MIN_BOARD_VALUE : MAX_BOARD_VALUE; //в начале bestBoardValue присваивается + или - бесконечность int bestMoveRowIndex = -1; //инициализация координат лучшего хода int bestMoveColumnIndex = -1; foreach (int[] nextMove in possibleMoves) { int rowIndex = nextMove[0]; int columnIndex = nextMove[1]; Board nextBoard = (Board)board.Clone(); //копия доски nextBoard.SetFieldColor(rowIndex, columnIndex, color); //установка фишек на доску bool nextIsMaximizing = playerSkipsMove ? isMaximizing : !isMaximizing; int dummyIndex; // значения resultRowIndex и resultColumnIndex не нуждаются в рекурсивном вызове функции //рекурсивный вызов int currentBoardValue = this.GetNextMove(nextBoard, nextIsMaximizing, currentDepth + 1, alpha, beta, out dummyIndex, out dummyIndex); if (isMaximizing) { if (currentBoardValue > bestBoardValue) { bestBoardValue = currentBoardValue; bestMoveRowIndex = rowIndex; bestMoveColumnIndex = columnIndex; //Найти оценку текущей ситуации, в предположении, что она находится между alpha и beta. if (bestBoardValue > alpha) { alpha = bestBoardValue; } if (bestBoardValue >= beta) { break; } } } else { if (currentBoardValue < bestBoardValue) { bestBoardValue = currentBoardValue; bestMoveRowIndex = rowIndex; bestMoveColumnIndex = columnIndex; if (bestBoardValue < beta) { beta = bestBoardValue; } if (bestBoardValue <= alpha) { break; } } } } resultRowIndex = bestMoveRowIndex; resultColumnIndex = bestMoveColumnIndex; return(bestBoardValue); } }
//конструктор класса public ComputerPlayer(Game game, DiscColor color, string name, int maxDepth) : base(game, color, name) { this.mMaxDepth = maxDepth; }