Files
ft/Client/Assets/Scripts/EventCubeMelt/Data/TetrisBoardData.cs
2026-06-29 21:18:33 +08:00

520 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// BoardData
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EventCubeMelt;
using EventCubeMelt.Utils;
using UnityEngine;
namespace EventCubeMelt.Data
{
[Serializable]
public class TetrisBoardData
{
#region
// 放在Config 中好不好???
public const int WIDTH = 8;
public const int HEIGHT = 8;
#endregion
#region
private CellType[,] _grid;
private CellType[,] _ghostGrid;
// 游戏状态
private int _score;
private int _level;
private int _linesCleared;
private int _combo;
private bool _isGameOver;
private bool _isPaused;
public TetrisBoardData(int height, int width)
{
Width = width;
Height = height;
Initialize();
}
#endregion
#region
public int Width { get; private set; } = WIDTH;
public int Height { get; private set; } = HEIGHT;
#endregion
// 索引
public CellType this[int row, int col]
{
get
{
if (row < 0 || row >= Height || col < 0 || col >= Width)
return CellType.Empty;
return _grid[row, col];
}
set
{
if (row >= 0 && row < Height && col >= 0 && col < Width)
_grid[row, col] = value;
}
}
// 事件系统Unity事件
// public event Action<int> OnLinesCleared;
// public event Action<bool> OnGameOver;
// public event Action<List<int>> OnLinesCompleted; // 传递被消除的行号
public void LoadData(List<int> boardData)
{
ELog($"LoadData -> {Height} {Width}");
if (boardData is not { Count: > 0 })
{
return;
}
for (int row = 0; row < Height; row++)
{
for (int col = 0; col < Width; col++)
{
_grid[row, col] = (CellType)boardData[row * Width + col];
}
}
}
//
public void Initialize()
{
ELog($"Initialize() -> {Height} {Width}");
// 初始化网格
_grid = new CellType[Height, Width];
_ghostGrid = new CellType[Height, Width];
ClearGrid();
ClearGhostGrid();
}
private void ClearGrid()
{
for (int row = 0; row < Height; row++)
{
for (int col = 0; col < Width; col++)
{
_grid[row, col] = CellType.Empty;
}
}
}
public void PrintShape()
{
if (!ShouldBuildELog())
return;
var sb = new StringBuilder();
sb.Append("PrintShape-Board");
for (var i = 0; i < Height; i++)
{
sb.Append("<color=red>");
for (var j = 0; j < Width; j++)
{
sb.Append((int)_grid[i,j]);
if (j < Width - 1)
sb.Append(", ");
}
// sb.AppendLine();
sb.Append("</color> \n");
}
ELog(sb.ToString());
}
#region
public void SpawnNewPiece()
{
ELog("SpawnNewPiece()");
}
#endregion
#region
private void CheckLineClears()
{
var completedRows = new List<int>();
// 检查所有行
for (int row = 0; row < Height; row++)
{
bool isFull = true;
for (int col = 0; col < Width; col++)
{
if (_grid[row, col] == CellType.Empty)
{
isFull = false;
break;
}
}
if (isFull)
{
completedRows.Add(row);
}
}
if (completedRows.Count > 0)
{
// 消除行
ClearRows(completedRows);
// 更新分数
// UpdateScore(completedRows.Count);
// 触发事件
// OnLinesCompleted?.Invoke(completedRows);
// OnLinesCleared?.Invoke(_linesCleared);
_combo++;
}
else
{
_combo = 0; // 重置连击
}
}
private void ClearRows(List<int> rows)
{
// 从下往上排序
rows.Sort((a, b) => b.CompareTo(a));
foreach (int row in rows)
{
// 向下移动上方的行
for (int r = row; r > 0; r--)
{
for (int c = 0; c < Width; c++)
{
_grid[r, c] = _grid[r - 1, c];
}
}
// 清空顶部行
for (int c = 0; c < Height; c++)
{
_grid[0, c] = CellType.Empty;
}
}
_linesCleared += rows.Count;
_level = Mathf.Max(1, (_linesCleared / 10) + 1);
// OnLevelChanged?.Invoke(_level);
}
public Dictionary<int, int> ClearRowsAndCols(List<int> rows, List<int> cols,out List<(int row ,int col ,int value )> hasElemGrid)
{
// 收集者
var values = new List<int>();
//TODO:CM 容易产生碎片的操作,后期整理
var hasGrid = new List<string>();
hasElemGrid = new List<(int, int,int)>();
foreach (var row in rows)
{
for (var i = 0; i < Width; i++)
{
var key = $"{row}_{i}";
if (!hasGrid.Contains(key) && _grid[row,i] != CellType.Ct99 && _grid[row,i] != CellType.Empty)
{
hasGrid.Add(key);
values.Add((int)_grid[row,i]);
hasElemGrid.Add((row,i,(int)_grid[row,i]));
}
_grid[row, i] = CellType.Empty;
}
}
//ClearCol
foreach (var col in cols)
{
for (var i = 0; i < Height; i++)
{
var key = $"{i}_{col}";
// 注意,有可能 会和上面重复遍历到一个格子但是这个格子已经变为Empty可能会被添加
if (!hasGrid.Contains(key) && _grid[i,col] != CellType.Ct99 && _grid[i,col] != CellType.Empty)
{
hasGrid.Add(key);
values.Add((int)_grid[i,col]);
hasElemGrid.Add((i,col,(int)_grid[i,col]));
}
_grid[i, col] = CellType.Empty;
}
}
// PrintShape();
//
// _thisEventManager.UpdateGridData(GetBoardData());
if (ShouldBuildELog())
{
if (hasElemGrid.Count > 0)
ELog($"LCM-> ClearRowsAndCols => hasElemGrid {string.Join(", ", hasElemGrid)}");
if (values.Count > 0)
ELog($"LCM-> ClearRowsAndCols => values {string.Join(", ", values)}");
}
var countDictionary = values.GroupBy(n => n)
.ToDictionary(g => g.Key, g => g.Count());
return countDictionary;
}
public List<int> GetClearRows(bool withGhostCheck = false)
{
// Log("GetClearRows-> ");
var completedRows = new List<int>();
// 检查所有行
for (int row = 0; row < Height; row++)
{
var isFull = true;
for (int col = 0; col < Width; col++)
{
var condition = withGhostCheck ? _grid[row, col] == CellType.Empty && _ghostGrid[row, col] == CellType.Empty
: _grid[row, col] == CellType.Empty;
if(condition)
{
isFull = false;
break;
}
}
if (isFull)
{
if (!withGhostCheck)
{
if (ShouldBuildELog())
{
var list = new List<int>();
for (int col = 0; col < Width; col++)
{
list.Add((int)_grid[row, col]);
}
ELog($"GetClearRows row={row} cells={string.Join(",", list)}");
}
}
completedRows.Add(row);
}
}
//
// foreach (var row in completedRows)
// {
// for (var col = 0; col < Width; col++)
// {
//
// }
// }
return completedRows;
}
public List<int> GetClearCols(bool withGhostCheck = false)
{
// Log("GetClearCols-> ");
var completeCols = new List<int>();
// 检查所有行
for (int col = 0; col < Width; col++)
{
var isFull = true;
for (int row = 0; row < Height; row ++)
{
// if (_grid[row, col] == CellType.Empty)
var condition = withGhostCheck
? _grid[row, col] == CellType.Empty && _ghostGrid[row, col] == CellType.Empty
: _grid[row, col] == CellType.Empty;
if(condition)
{
isFull = false;
break;
}
}
if (isFull)
{
if (!withGhostCheck)
{
if (ShouldBuildELog())
{
var list = new List<int>();
for (var row = 0; row < Height; row++)
{
list.Add((int)_grid[row, col]);
}
ELog($"GetClearCols col={col} cells={string.Join(",", list)}");
}
}
completeCols.Add(col);
}
}
return completeCols;
}
// 显示消除方法
public void SetGhostValue(int row, int col, CellType cellType)
{
if (row < 0 || row >= Height || col < 0 || col >= Width)
return;
_ghostGrid[row, col] = cellType;
}
public CellType GetGhostValue(int row, int col)
{
if (row < 0 || row >= Height || col < 0 || col >= Width)
return CellType.Illegal;
return _ghostGrid[row, col];
}
//
public void ClearGhostGrid()
{
for (int row = 0; row < Height; row++)
{
for (int col = 0; col < Width; col++)
{
_ghostGrid[row, col] = CellType.Empty;
}
}
}
#endregion
#region Log
private static bool ShouldBuildELog()
{
#if UNITY_EDITOR
return EventCubeMeltLogPolicy.EnableELog;
#else
return false;
#endif
}
public static void ELog(string message)
{
#if UNITY_EDITOR
if (!EventCubeMeltLogPolicy.EnableELog) return;
Debug.Log($"<color=red> {nameof(TetrisBoardData)} => {message} </color>");
#endif
}
#endregion
#region
public int[,] GetGrid()
{
var intGrid = new int[Height, Width];
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)
{
intGrid[i, j] = (int)_grid[i, j];
}
}
return intGrid;
}
public CellType GetGridValue(int row, int col)
{
if (row < 0 || row >= Width || col < 0 || col >= Height)
return CellType.Illegal;
return this[row, col];
}
public List<int> GetBoardData()
{
var list = new List<int>();
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)
{
list.Add((int)_grid[i, j]);
}
}
return list;
}
#endregion
public void UpdateDataByPiece(TetrominoData tetrominoData,Vector2Int startPos)
{
for (var row = 0; row < tetrominoData.Height; row++)
{
for (var col = 0; col < tetrominoData.Width; col++)
{
if (tetrominoData[row, col] != CellType.Empty)
{
var gridRow = startPos.y + row;
var gridCol = startPos.x + col;
_grid[gridRow, gridCol] = tetrominoData[row, col];
}
}
}
}
public List<(int row, int col, int value,int newR,int newC)> UpdateBombValue(
List<(int row, int col, int value)> listBomb,
List<List<int>> bombValues)
{
var ls = new List<(int, int, int, int, int)>();
var m = Height;
var n = Width;
// 1. 先扫一遍,把所有 99 的坐标收集起来
var list = new List<(int, int)>();
for (var i = 0; i < m; i++)
{
for (var j = 0; j < n; j++)
{
if (this[i, j] == CellType.Ct99)
list.Add((i, j));
}
}
// 2. 如果没有 99返回无效坐标
// if (list.Count == 0)
// return ls;
var finder = new UniqueRandom99(GetGrid());
var listCount = listBomb.Count;
for(int i = 0;i < listCount; ++i)
{
var bomb = listBomb[i];
var bombValue = bombValues[i];
for (var k = 0; k < bombValue.Count; ++k)
{
var (r, c) = finder.Next();
if (r >= 0 && r < m && c >= 0 && c < n)
{
this[r, c] = (CellType)bombValue[k];
ls.Add(new (bomb.row,bomb.col,bombValue[k],r,c));
}
else
{
ls.Add(new (bomb.row,bomb.col,bombValue[k],-1,-1));
}
}
}
return ls;
}
//- 更新
public bool ReSizeWithData(int newHeight , int newWidth ,List<int> boardData)
{
ELog($"ReSizeWithData -> {newHeight} {newWidth}");
var bResized = false;
if (newWidth != Width || newHeight != Height)
{
Width = newWidth;
Height = newHeight;
Initialize();
bResized = true;
}
LoadData(boardData);
return bResized;
}
}
}