520 lines
16 KiB
C#
520 lines
16 KiB
C#
// 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;
|
||
}
|
||
}
|
||
} |