// 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 OnLinesCleared; // public event Action OnGameOver; // public event Action> OnLinesCompleted; // 传递被消除的行号 public void LoadData(List 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(""); for (var j = 0; j < Width; j++) { sb.Append((int)_grid[i,j]); if (j < Width - 1) sb.Append(", "); } // sb.AppendLine(); sb.Append(" \n"); } ELog(sb.ToString()); } #region 方块操作 public void SpawnNewPiece() { ELog("SpawnNewPiece()"); } #endregion #region 游戏逻辑 private void CheckLineClears() { var completedRows = new List(); // 检查所有行 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 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 ClearRowsAndCols(List rows, List cols,out List<(int row ,int col ,int value )> hasElemGrid) { // 收集者 var values = new List(); //TODO:CM 容易产生碎片的操作,后期整理 var hasGrid = new List(); 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 GetClearRows(bool withGhostCheck = false) { // Log("GetClearRows-> "); var completedRows = new List(); // 检查所有行 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(); 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 GetClearCols(bool withGhostCheck = false) { // Log("GetClearCols-> "); var completeCols = new List(); // 检查所有行 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(); 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($" {nameof(TetrisBoardData)} => {message} "); #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 GetBoardData() { var list = new List(); 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> 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 boardData) { ELog($"ReSizeWithData -> {newHeight} {newWidth}"); var bResized = false; if (newWidth != Width || newHeight != Height) { Width = newWidth; Height = newHeight; Initialize(); bResized = true; } LoadData(boardData); return bResized; } } }