231 lines
7.1 KiB
C#
231 lines
7.1 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
|
||
public enum CellState
|
||
{
|
||
Empty, Player1, Player2
|
||
}
|
||
public class FieldModel
|
||
{
|
||
//двумерный массив для поля
|
||
public CellState[,] FieldArray;
|
||
public int Height;
|
||
public int Width;
|
||
public int TargetLineLength = 4;
|
||
|
||
public FieldModel(int width, int height)
|
||
{
|
||
//0 индекс для строк, 1 индекс для столбцов
|
||
Width = width;
|
||
Height = height;
|
||
makeEmptyField(width, height);
|
||
}
|
||
|
||
public void makeEmptyField(int width, int height)
|
||
{
|
||
FieldArray = new CellState[height, width];
|
||
Array.Clear(FieldArray, 0, FieldArray.Length);
|
||
}
|
||
|
||
public CheckResult Check(CellState lastPlayer)
|
||
{
|
||
HashSet<(int row, int col)> WinningIndexes = new();
|
||
|
||
var rowsResult = checkAllRows(lastPlayer);
|
||
var colsResult = checkAllColumns(lastPlayer);
|
||
var diagonalsResult = checkAllDiagonals(lastPlayer);
|
||
|
||
bool hasWin = rowsResult.hasWin ||
|
||
colsResult.hasWin ||
|
||
diagonalsResult.hasWin;
|
||
|
||
if (hasWin)
|
||
{
|
||
WinningIndexes.UnionWith(rowsResult.winningIndexes);
|
||
WinningIndexes.UnionWith(colsResult.winningIndexes);
|
||
WinningIndexes.UnionWith(diagonalsResult.winningIndexes);
|
||
|
||
return new CheckResult(end: true,
|
||
isDraw: false,
|
||
WinningIndexes);
|
||
}
|
||
else if (checkDraw())
|
||
{
|
||
return new CheckResult(end: true,
|
||
isDraw: true,
|
||
WinningIndexes);
|
||
}
|
||
else
|
||
{
|
||
return new CheckResult(end: false,
|
||
isDraw: false,
|
||
WinningIndexes);
|
||
}
|
||
|
||
|
||
|
||
}
|
||
private (bool hasWin, HashSet<(int row, int col)> winningIndexes) checkAllRows(CellState targetCellState)
|
||
{
|
||
var winningCells = new HashSet<(int row, int col)>();
|
||
|
||
for (int row = 0; row < Height; row++)
|
||
{
|
||
int start = -1;
|
||
|
||
for (int col = 0; col <= Width; col++) // теперь проходим до Width включительно
|
||
{
|
||
// Проверяем, не вышли ли за границы массива
|
||
bool isMatch = col < Width && FieldArray[row, col] == targetCellState;
|
||
|
||
if (isMatch && start == -1)
|
||
start = col;
|
||
|
||
if ((!isMatch || col == Width) && start != -1)
|
||
{
|
||
int length = col - start;
|
||
if (length >= TargetLineLength)
|
||
{
|
||
for (int c = start; c < col; c++)
|
||
winningCells.Add((row, c));
|
||
}
|
||
start = -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
return (winningCells.Count > 0, winningCells);
|
||
}
|
||
private (bool hasWin, HashSet<(int row, int col)> winningIndexes) checkAllColumns(CellState targetCellState)
|
||
{
|
||
var winningCells = new HashSet<(int row, int col)>();
|
||
|
||
for (int col = 0; col < Width; col++)
|
||
{
|
||
int start = -1;
|
||
|
||
for (int row = 0; row <= Height; row++)
|
||
{
|
||
bool isMatch = row < Height && FieldArray[row, col] == targetCellState;
|
||
|
||
if (isMatch && start == -1)
|
||
start = row;
|
||
|
||
if ((!isMatch || row == Height) && start != -1)
|
||
{
|
||
int length = row - start;
|
||
if (length >= TargetLineLength)
|
||
{
|
||
for (int r = start; r < row; r++)
|
||
winningCells.Add((r, col));
|
||
}
|
||
start = -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
return (winningCells.Count > 0, winningCells);
|
||
}
|
||
|
||
private (bool hasWin, HashSet<(int row, int col)> winningIndexes) checkAllDiagonals(CellState targetCellState)
|
||
{
|
||
var winningCells = new HashSet<(int row, int col)>();
|
||
|
||
// Проверка главных диагоналей
|
||
for (int row = 0; row < Height; row++)
|
||
{
|
||
CheckDiagonalStartingAt(row, 0, 1, 1, targetCellState, winningCells);
|
||
}
|
||
for (int col = 1; col < Width; col++)
|
||
{
|
||
CheckDiagonalStartingAt(0, col, 1, 1, targetCellState, winningCells);
|
||
}
|
||
|
||
// Проверка побочных диагоналей
|
||
for (int row = 0; row < Height; row++)
|
||
{
|
||
CheckDiagonalStartingAt(row, Width - 1, 1, -1, targetCellState, winningCells);
|
||
}
|
||
for (int col = Width - 2; col >= 0; col--)
|
||
{
|
||
CheckDiagonalStartingAt(0, col, 1, -1, targetCellState, winningCells);
|
||
}
|
||
|
||
return (winningCells.Count > 0, winningCells);
|
||
}
|
||
|
||
private void CheckDiagonalStartingAt(int startRow, int startCol, int rowStep, int colStep,
|
||
CellState targetCellState, HashSet<(int, int)> winningCells)
|
||
{
|
||
int rows = Height;
|
||
int cols = Width;
|
||
int currentRow = startRow;
|
||
int currentCol = startCol;
|
||
int sequenceLength = 0;
|
||
int sequenceStartRow = startRow;
|
||
int sequenceStartCol = startCol;
|
||
|
||
while (currentRow >= 0 && currentRow < rows &&
|
||
currentCol >= 0 && currentCol < cols)
|
||
{
|
||
bool isMatch = FieldArray[currentRow, currentCol] == targetCellState;
|
||
|
||
if (isMatch)
|
||
{
|
||
sequenceLength++;
|
||
|
||
if (sequenceLength >= TargetLineLength)
|
||
{
|
||
// Добавляем ВСЕ ячейки из текущей последовательности
|
||
for (int i = 0; i < sequenceLength; i++)
|
||
{
|
||
int r = sequenceStartRow + i * rowStep;
|
||
int c = sequenceStartCol + i * colStep;
|
||
winningCells.Add((r, c));
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
sequenceLength = 0;
|
||
sequenceStartRow = currentRow + rowStep;
|
||
sequenceStartCol = currentCol + colStep;
|
||
}
|
||
|
||
currentRow += rowStep;
|
||
currentCol += colStep;
|
||
}
|
||
}
|
||
public int GetColumnHeight(int colIndex)
|
||
{
|
||
for (int row = Height - 1; row >= 0; row--)
|
||
{
|
||
if (FieldArray[row, colIndex] == CellState.Empty)
|
||
{
|
||
return row;
|
||
}
|
||
}
|
||
return -1;//если не нашли, то больше добавить нельзя
|
||
}
|
||
|
||
public bool checkDraw()
|
||
{
|
||
for (int col = 0; col < Width; col++)
|
||
{
|
||
if (GetColumnHeight(col) != -1) return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
public List<int> CollectNonFullCols()
|
||
{
|
||
var positions = new List<int>();
|
||
for (int i = 0; i < Width; i++)
|
||
if (GetColumnHeight(i) != -1)
|
||
positions.Add(i);
|
||
return positions;
|
||
}
|
||
}
|
||
|
||
|