Загрузка данных
using System;
using System.Collections.Generic;
using System.Linq;
namespace Tetris.Dto
{
// ─── 1. Point / CellDTO ─────────────────────────────────────────────
public readonly record struct Point(int X, int Y);
// ─── 2. Piece ───────────────────────────────────────────────────────
public class Piece
{
private readonly List<Point> _initialCells;
public Piece(IEnumerable<Point> initialCells, char color, int rotationStates = 4)
{
_initialCells = initialCells.ToList();
RelativeCells = new List<Point>(_initialCells);
Color = color;
RotationStates = Math.Max(1, rotationStates);
}
public Point Center { get; set; }
public char Color { get; }
public int RotationStates { get; } // 2 для I, S, Z; 1 для O; 4 для T, J, L
public int RotationIndex { get; private set; }
public List<Point> RelativeCells { get; private set; }
public IEnumerable<Point> GetAbsoluteCells() =>
RelativeCells.Select(c => new Point(Center.X + c.X, Center.Y + c.Y));
// Единая математическая операция поворота (X, Y) -> (-Y, X)
public void Rotate()
{
RotationIndex = (RotationIndex + 1) % RotationStates;
RecomputeCells();
}
public (List<Point> cells, int index) Snapshot() =>
(new List<Point>(RelativeCells), RotationIndex);
public void Restore((List<Point> cells, int index) snap)
{
RelativeCells = snap.cells;
RotationIndex = snap.index;
}
private void RecomputeCells()
{
var cells = _initialCells;
for (int i = 0; i < RotationIndex; i++)
cells = cells.Select(p => new Point(-p.Y, p.X)).ToList();
// Нормализация: минимальные X,Y сдвигаем к 0
int minX = cells.Min(p => p.X);
int minY = cells.Min(p => p.Y);
RelativeCells = cells.Select(p => new Point(p.X - minX, p.Y - minY)).ToList();
}
}
// ─── 3. Фабрика фигур ───────────────────────────────────────────────
public static class PieceFactory
{
public enum PieceType { I, O, T, S, Z, J, L }
private static readonly Dictionary<PieceType, (Point[] cells, char color, int rotations)> Templates = new()
{
[PieceType.I] = (new[] { new Point(0,0), new Point(1,0), new Point(2,0), new Point(3,0) }, 'I', 2),
[PieceType.O] = (new[] { new Point(0,0), new Point(1,0), new Point(0,1), new Point(1,1) }, 'O', 1),
[PieceType.T] = (new[] { new Point(0,0), new Point(1,0), new Point(2,0), new Point(1,1) }, 'T', 4),
[PieceType.S] = (new[] { new Point(1,0), new Point(2,0), new Point(0,1), new Point(1,1) }, 'S', 2),
[PieceType.Z] = (new[] { new Point(0,0), new Point(1,0), new Point(1,1), new Point(2,1) }, 'Z', 2),
[PieceType.J] = (new[] { new Point(0,0), new Point(0,1), new Point(1,1), new Point(2,1) }, 'J', 4),
[PieceType.L] = (new[] { new Point(2,0), new Point(0,1), new Point(1,1), new Point(2,1) }, 'L', 4),
};
public static Piece Create(PieceType type, int boardWidth)
{
var (cells, color, rotations) = Templates[type];
int width = cells.Max(p => p.X) + 1;
int startX = Math.Max(0, (boardWidth - width) / 2);
return new Piece(cells, color, rotations)
{
Center = new Point(startX, 0)
};
}
public static Piece CreateRandom(int boardWidth) =>
Create((PieceType)Random.Shared.Next(0, 7), boardWidth);
}
// ─── 4. Board ───────────────────────────────────────────────────────
public class Board
{
public int Width { get; }
public int Height { get; }
public char[,] Grid { get; } // '\0' = пусто
public Board(int width, int height)
{
Width = width;
Height = height;
Grid = new char[width, height];
}
public bool IsInside(int x, int y) => x >= 0 && x < Width && y >= 0 && y < Height;
public bool IsEmpty(int x, int y) => Grid[x, y] == '\0';
public bool IsOccupied(int x, int y) => IsInside(x, y) && Grid[x, y] != '\0';
public void Set(int x, int y, char c) => Grid[x, y] = c;
}
// ─── 5. BoardProcessor / GameEngine ─────────────────────────────────
public class BoardProcessor
{
public bool CanPlace(Board b, Piece p)
{
foreach (var c in p.GetAbsoluteCells())
if (!b.IsInside(c.X, c.Y) || b.IsOccupied(c.X, c.Y))
return false;
return true;
}
public bool TryMove(Board b, Piece p, int dx, int dy)
{
var old = p.Center;
p.Center = new Point(old.X + dx, old.Y + dy);
if (!CanPlace(b, p)) { p.Center = old; return false; }
return true;
}
public bool TryRotate(Board b, Piece p)
{
var snap = p.Snapshot();
p.Rotate();
if (!CanPlace(b, p)) { p.Restore(snap); return false; }
return true;
}
public void Lock(Board b, Piece p)
{
foreach (var c in p.GetAbsoluteCells())
b.Set(c.X, c.Y, p.Color);
ClearFullLines(b);
}
public int ClearFullLines(Board b)
{
int cleared = 0;
for (int y = b.Height - 1; y >= 0; y--)
{
bool full = true;
for (int x = 0; x < b.Width; x++)
if (b.IsEmpty(x, y)) { full = false; break; }
if (!full) continue;
for (int yy = y; yy > 0; yy--)
for (int x = 0; x < b.Width; x++)
b.Set(x, yy, b.Grid[x, yy - 1]);
for (int x = 0; x < b.Width; x++)
b.Set(x, 0, '\0');
cleared++;
y++;
}
return cleared;
}
}
}