123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- using System.Text;
- using MinMaxAB;
- namespace Connect4;
- /// <summary>
- /// Доска для игры в 4 в ряд
- /// </summary>
- public class GameBoard
- {
- public Player[][] cells;
- public readonly GameDimensions dimensions;
- public GameBoard(GameDimensions dim)
- {
- cells = Extensions.Fill(dim.Columns, dim.Rows, Player.None);
- dimensions = dim;
- }
- public GameBoard(GameDimensions dim, Player[][] data)
- {
- dimensions = dim;
- cells = data;
- }
- private bool cacheInvalidated = false;
- private Player winnerCache = Player.None;
- /// <summary>
- /// Закинуть монетку сверху
- /// </summary>
- /// <param name="player">Игрок, закинувший монетку</param>
- /// <param name="column">Столбец, в который закинуть монетку</param>
- /// <returns>Получилось ли поместить монетку</returns>
- public bool PlaceCoin(Player player, int column)
- {
- int row = 0;
- while (row < dimensions.Rows && cells[column][row] == Player.None)
- {
- row++;
- }
- if (row == 0)
- return false;
- cells[column][row - 1] = player;
- cacheInvalidated = true;
- return true;
- }
- /// <summary>
- /// Убрать верхнюю монетку
- /// </summary>
- /// <param name="column">Столбец, из которого убрать монетку</param>
- /// <returns>Удалось ли убрать монетку</returns>
- public bool RemoveFromTop(int column)
- {
- int row = 0;
- while (row < dimensions.Rows && cells[column][row] == Player.None)
- {
- row++;
- }
- if (row == dimensions.Rows)
- return false;
- cells[column][row] = Player.None;
- cacheInvalidated = true;
- return true;
- }
- /// <summary>
- /// Найти победители
- /// </summary>
- /// <returns>Игрок-победитель</returns>
- public Player? GetWinner()
- {
- if (!cacheInvalidated) return winnerCache;
- cacheInvalidated = false;
- for (int i = 0; i < dimensions.Columns; i++)
- {
- for (int j = 0; j < dimensions.Rows; j++)
- {
- if (cells[i][j] == Player.None)
- continue;
- // Горизонталь
- bool horizontal = i + 3 < dimensions.Columns;
- // Вертикаль
- bool vertical = j + 3 < dimensions.Rows;
- if (!horizontal && !vertical)
- continue;
- // Диагональ
- bool diagonal = horizontal && vertical;
- // Вторая диагональ
- bool reverseDiagonal = vertical && i - 3 >= 0;
- // Проходим по всем возможным длинам последовательности
- for (int k = 1; k < 4; k++)
- {
- horizontal = horizontal && cells[i][j] == cells[i + k][j];
- vertical = vertical && cells[i][j] == cells[i][j + k];
- diagonal = diagonal && cells[i][j] == cells[i + k][j + k];
- reverseDiagonal = reverseDiagonal && cells[i][j] == cells[i - k][j + k];
- if (!horizontal && !vertical && !diagonal && !reverseDiagonal)
- break;
- }
- if (horizontal || vertical || diagonal || reverseDiagonal)
- {
- winnerCache = cells[i][j];
- return cells[i][j];
- }
- }
- }
- winnerCache = Player.None;
- return Player.None;
- }
- /// <summary>
- /// Проверить, есть ли пустые столбцы
- /// </summary>
- /// <returns>true если есть пустые столбцы</returns>
- public bool HasEmptyColumns()
- {
- for (int x = 0; x < dimensions.Columns; x++)
- {
- if (cells[x][0] == Player.None) return true;
- }
- return false;
- }
- /// <summary>
- /// Найти символ, обозначающий игрока
- /// </summary>
- /// <param name="player">Игрок</param>
- /// <returns>Символ, обозначающий данного игрока</returns>
- protected char PlayerChar(Player player) => player switch
- {
- Player.Human => 'O',
- Player.Computer => 'X',
- _ => ' ',
- };
- /// <summary>
- /// Сериализовать доску
- /// </summary>
- /// <returns>Строка, содержащая отформатированное представление доски</returns>
- public override string ToString()
- {
- string sep = $"\n{new('-', dimensions.Columns * 4)}";
- var builder = new StringBuilder(sep);
- builder.Append("\n| ");
- for (int column = 0; column < dimensions.Columns; column++)
- {
- builder.Append(column + 1).Append(" | ");
- }
- builder.AppendLine(sep);
- for (int row = 0; row < dimensions.Rows; row++)
- {
- builder.Append("| ");
- for (int column = 0; column < dimensions.Columns; column++) builder.Append(PlayerChar(cells[column][row])).Append(" | ");
- builder.AppendLine(sep);
- }
- builder.Append("| ");
- for (int column = 0; column < dimensions.Columns; column++)
- {
- builder.Append(column + 1).Append(" | ");
- }
- builder.AppendLine(sep);
- return builder.ToString();
- }
- /// <summary>
- /// Скопировать доску вместе с ее содержимым
- /// </summary>
- /// <returns>Копия доски с таким же содержимым</returns>
- public GameBoard Clone()
- {
- var newBoard = new GameBoard(dimensions, cells.DeepCopy());
- return newBoard;
- }
- }
|