185 lines
6.8 KiB
C#
185 lines
6.8 KiB
C#
using System.Collections.Generic;
|
||
|
||
public enum Team
|
||
{
|
||
None,
|
||
Player1,
|
||
Player2
|
||
}
|
||
|
||
public static class FieldChecker
|
||
{
|
||
public static Chip[,] FieldArray => Board.Instance.Grid;
|
||
public static int Height => Board.Instance.Rows;
|
||
public static int Width => Board.Instance.Columns;
|
||
public static int TargetLineLength = 4;
|
||
public static CheckResult lastResult;
|
||
|
||
/// <summary>
|
||
/// Проверяет, привела ли последняя фишка (установленная командой lastPlayer в клетку (row, col)) к победе.
|
||
/// </summary>
|
||
/// <param name="lastPlayer">Команда, которая только что сделала ход.</param>
|
||
/// <param name="row">Строка (индекс по вертикали).</param>
|
||
/// <param name="col">Столбец (индекс по горизонтали).</param>
|
||
/// <returns>Результат проверки (окончание игры, ничья, множество победных клеток).</returns>
|
||
public static CheckResult Check(Team lastPlayer, int row, int col)
|
||
{
|
||
var winningCells = new HashSet<(int row, int col)>();
|
||
|
||
// Проверяем четыре направления, проходящие через (row, col)
|
||
CheckLine(row, col, 0, 1, lastPlayer, winningCells); // горизонталь
|
||
CheckLine(row, col, 1, 0, lastPlayer, winningCells); // вертикаль
|
||
CheckLine(row, col, 1, 1, lastPlayer, winningCells); // главная диагональ
|
||
CheckLine(row, col, 1, -1, lastPlayer, winningCells); // побочная диагональ
|
||
|
||
bool hasWin = winningCells.Count > 0;
|
||
|
||
if (hasWin)
|
||
{
|
||
lastResult = new CheckResult(end: true, isDraw: false, winningCells);
|
||
return lastResult;
|
||
}
|
||
|
||
if (CheckDraw())
|
||
{
|
||
lastResult = new CheckResult(end: true, isDraw: true, winningCells);
|
||
return lastResult;
|
||
}
|
||
|
||
lastResult = new CheckResult(end: false, isDraw: false, winningCells);
|
||
return lastResult;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет линию, заданную направлением (rowStep, colStep), проходящую через клетку (row, col).
|
||
/// Если найдена непрерывная последовательность из фишек lastPlayer длиной >= TargetLineLength,
|
||
/// все клетки этой последовательности добавляются в winningCells.
|
||
/// </summary>
|
||
private static void CheckLine(int row, int col, int rowStep, int colStep, Team team, HashSet<(int, int)> winningCells)
|
||
{
|
||
// Движение назад (против направления)
|
||
int r = row, c = col;
|
||
int backCount = 0;
|
||
while (true)
|
||
{
|
||
int prevR = r - rowStep;
|
||
int prevC = c - colStep;
|
||
if (prevR < 0 || prevR >= Height || prevC < 0 || prevC >= Width)
|
||
break;
|
||
var chip = FieldArray[prevC, prevR]; // массив индексируется [x=col, y=row]
|
||
if (chip == null || chip.team != team)
|
||
break;
|
||
backCount++;
|
||
r = prevR;
|
||
c = prevC;
|
||
}
|
||
int startR = r, startC = c;
|
||
|
||
// Движение вперёд (по направлению)
|
||
r = row;
|
||
c = col;
|
||
int forwardCount = 0;
|
||
while (true)
|
||
{
|
||
int nextR = r + rowStep;
|
||
int nextC = c + colStep;
|
||
if (nextR < 0 || nextR >= Height || nextC < 0 || nextC >= Width)
|
||
break;
|
||
var chip = FieldArray[nextC, nextR];
|
||
if (chip == null || chip.team != team)
|
||
break;
|
||
forwardCount++;
|
||
r = nextR;
|
||
c = nextC;
|
||
}
|
||
int endR = r, endC = c;
|
||
|
||
int totalLength = backCount + 1 + forwardCount; // +1 за саму клетку
|
||
if (totalLength >= TargetLineLength)
|
||
{
|
||
// Добавляем все клетки от (startR, startC) до (endR, endC) с шагом (rowStep, colStep)
|
||
for (int i = 0; i < totalLength; i++)
|
||
{
|
||
int curR = startR + i * rowStep;
|
||
int curC = startC + i * colStep;
|
||
winningCells.Add((curR, curC));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет, заполнено ли всё поле (ничья).
|
||
/// </summary>
|
||
public static bool CheckDraw()
|
||
{
|
||
for (int col = 0; col < Width; col++)
|
||
{
|
||
if (Board.Instance.GetLowestEmptyRow(col) != -1)
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
public static bool IsWinningMove(Team[,] board, int row, int col, Team player)
|
||
{
|
||
int height = board.GetLength(0);
|
||
int width = board.GetLength(1);
|
||
|
||
// Проверяем четыре направления
|
||
return CheckLine(board, row, col, 0, 1, player, height, width) ||
|
||
CheckLine(board, row, col, 1, 0, player, height, width) ||
|
||
CheckLine(board, row, col, 1, 1, player, height, width) ||
|
||
CheckLine(board, row, col, 1, -1, player, height, width);
|
||
}
|
||
|
||
// Вспомогательный метод для проверки одной линии
|
||
private static bool CheckLine(Team[,] board, int row, int col, int dr, int dc, Team player, int height, int width)
|
||
{
|
||
int count = 1;
|
||
// вперёд
|
||
int r = row + dr, c = col + dc;
|
||
while (r >= 0 && r < height && c >= 0 && c < width && board[r, c] == player)
|
||
{
|
||
count++;
|
||
r += dr;
|
||
c += dc;
|
||
}
|
||
// назад
|
||
r = row - dr; c = col - dc;
|
||
while (r >= 0 && r < height && c >= 0 && c < width && board[r, c] == player)
|
||
{
|
||
count++;
|
||
r -= dr;
|
||
c -= dc;
|
||
}
|
||
return count >= 4;
|
||
}
|
||
|
||
// Метод для проверки, есть ли у оппонента немедленная победа в один ход
|
||
public static bool HasImmediateWin(Team[,] board, Team player)
|
||
{
|
||
int height = board.GetLength(0);
|
||
int width = board.GetLength(1);
|
||
for (int col = 0; col < width; col++)
|
||
{
|
||
int row = GetLowestEmptyRow(board, col);
|
||
if (row == -1) continue;
|
||
board[row, col] = player;
|
||
bool win = IsWinningMove(board, row, col, player);
|
||
board[row, col] = Team.None;
|
||
if (win) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private static int GetLowestEmptyRow(Team[,] board, int col)
|
||
{
|
||
int height = board.GetLength(0);
|
||
for (int r = height - 1; r >= 0; r--)
|
||
if (board[r, col] == Team.None)
|
||
return r;
|
||
return -1;
|
||
}
|
||
}
|