// EventCubeMeltManager.cs // 锻刀大赛 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using asap.core; using cfg; using EventCubeMelt; using EventCubeMelt.Data; using EventCubeMelt.ShapePlacement; using EventCubeMelt.UI; using EventCubeMelt.Utils; using game; using GameCore; using Newtonsoft.Json; using UnityEngine; using Random = UnityEngine.Random; public class EventCubeMeltManager { // public const string SaveKey = "EventCubeMeltData"; #region 与外部交互函数 public void UpdateEventData(FishingEvent fishingEvent) { _fishingEvent = fishingEvent; if (EventData != null && _fishingEvent != null && _fishingEvent.ID != EventData.EventId) { ClearData(); } if (!_isInit || EventData == null) { _isInit = false; Init(); } else { UpdateTime(); SetRedPoint(); UpdateLocalData(); } } public void LoadData(string dataValue) { ELog($"LoadData-> {dataValue}"); if (!string.IsNullOrEmpty(dataValue)) { EventData = JsonConvert.DeserializeObject(dataValue); // 可调试的在这里放数据 if (true) { // EventData.CurrentStagetId = 15; // EventData.IsFinished = false; // EventData.BoardData = new List // { // 0,99,0,99,99,0,99,0, // 99,0,99,99,0,99,0,99, // 0,99,0,99,99,0,99,99, // 99,0,99,0,99,0,99,99, // 0,99,99,99,99,99,99,99, // 99,0,99,99,99,99,99,99, // 0,99,99,99,99,99,99,99, // 99,0,99,0,99,99,5,99 // }; // EventData.CollectRewardsId.Remove(14); // EventData.IsFinalRewardGet = false; // EventData.CollectItems = new List // { // new() // { // ItemId = 1, Count = 20, MaxCount = 20, Icon = null, // }, // new() // { // ItemId = 4, Count = 15, MaxCount = 15, Icon = null, // }, // new() // { // ItemId = 5, Count = 10, MaxCount = 10, Icon = null, // } // }; // EventData.BoardData = new List() // { // 0, 0, 99, 99, 99, 0, 0, 0, // 1, 0, 99, 0, 0, 99, 99, 2, // 99, 99, 99, 88, 88, 2, 0, 99, // 99, 2, 99, 99, 99, 99, 0, 99, // 99, 99, 2, 99, 99, 0, 0, 0, // 0,0,0,0, 0, 2, 0, 0, // 99, 0, 0, 99, 99, 99, 2, 0, // 99, 0, 0, 99, 99, 99, 2, 0 // }; // EventData.BoardData = new List // { // 99, 99, 99, 99, 0, 99, 0, 0, // 99, 99, 99, 99, 0, 99, 0, 0, // 99, 99, 99, 99, 0, 99, 0, 0, // 99, 99, 99, 99, 0, 99, 0, 0, // 99, 99, 99, 99, 0, 99, 0, 0, // 0,0,0,0, 0, 0, 0, 0, // 99, 99, 99, 99, 0, 99, 0, 0, // 99, 99, 99, 99, 0, 99, 0, 0, // }; // EventData.PieceDatas = new List> // { // new() {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,99,99,99,99}, // new() {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0}, // new() {0,0,0,0,0,0,0,0,0,0,0,0,99,99,0,0,0,99,0,0,0,0,99,0,0}, // }; } } } public void ClearData() { EventData = null; _isInit = false; SaveData(); } private void SaveData() { var eventDataJson = JsonConvert.SerializeObject(EventData); if (ShouldBuildELog()) { ELog($"SaveData()-> {eventDataJson}"); } PlayFabMgr.Instance.UpdateUserDataValue(SaveKey, eventDataJson); } public void AddTicket(int costValue) { if (EventData != null) { EventData.TicketCount += costValue; if (EventData.TicketCount < 0) { EventData.TicketCount = 0; } GContext.container.Resolve().SaveTransitionData(EventData.EventId, EventData.TicketCount); GContext.Publish(new EventCubeMeltUpdateTicketCount()); SaveData(); } SetRedPoint(); } public int GetTicketCount() { return EventData.TicketCount; } #endregion private bool _isInit; private FishingEvent _fishingEvent; [Inject] public Tables _tables { get; set; } public EventCubeMeltData EventData { get; private set; } public FishingEvent FishingEvent {get => _fishingEvent;set => _fishingEvent = value;} // 锻刀大赛数据 public EventCubeMeltMain EventCubeMeltMainData { get; private set; } // 配置 private List> EventCubeMeltConfig { get; set; } // 当前的配置数据 public EventCubeMeltStage EventCubeMeltStageData { get; private set; } // 当前收集数据 public EventCubeMeltCollection EventCubeMeltCollectionData { get; private set; } // 当前的Piece public List EventCubeMeltPieces { get; private set; } // // 读取表格,生成所有的Piece private List EventCubeMeltPieceItems { get; set; } = new(); // 当前的Item public List EventCubeMeltItems { get; private set; } // 收集的列表 public List EventCubeMeltCollections { get; private set; } //- 当前的StageList public List EventStageList { get; private set; } // public int StageCount { get; set; } //- // public Vector2Int BoardViewSize { get; private set; } public DateTime StartTime; // 结束时间 public DateTime EndTime; public void Init() { if (_fishingEvent == null) return; if (_isInit) return; ELog("Init()-> "); if (EventData == null) { CreateNewData(); } UpdateLocalData(); UpdateTime(); SetRedPoint(); _isInit = true; } public void SetRedPoint() { if (EventData != null) { RedPointManager.Instance.SetRedPointState(RedPointName.Home_CubeMelt, EventData.TicketCount > 0, EventData.TicketCount); } else { RedPointManager.Instance.SetRedPointState(RedPointName.Home_CubeMelt, false); } } private void UpdateTime() { if (_fishingEvent != null) { var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition; var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow()); var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow().AddDays(-1)); if (EventData != null) { if (EventData.EventId == _fishingEvent.ID) { EventData.StartTime = startTime; EventData.EndTime = endTime; } else { EventData.EndTime = DateTime.Now.AddDays(-1); } } EndTime = endTime; StartTime = startTime; } } private void UpdateLocalData() { ELog("UpdateLocalData() -> "); if (_fishingEvent == null) return; var fishingEventRedirectID = _fishingEvent.RedirectID; var cycleItemData = _tables.TbFishingEventCycleItem.GetOrDefault(fishingEventRedirectID); var dataList = _tables.TbEventCubeMeltMain.DataList; // 设置内容 EventCubeMeltMainData = dataList.FirstOrDefault(data => data.ID == cycleItemData.RedirectID); // 道具 EventCubeMeltItems = _tables.TbEventCubeMeltItem.DataList; EventCubeMeltPieces = _tables.TbEventCubeMeltPiece.DataList; EventCubeMeltCollections = _tables.TbEventCubeMeltCollection.DataList; //- StageList EventStageList = EventData.Cycle == 0 ? EventCubeMeltMainData?.StageFirstList :EventCubeMeltMainData?.StageLoopList ?? new List(); // var stageListCount = EventData.Cycle == 0 ? EventCubeMeltMainData?.StageFirstList.Count : EventCubeMeltMainData?.StageLoopList.Count; // var stageListCount = EventStageList ?.Count ?? 0; StageCount = EventStageList ?.Count ?? 0; // PieceItems InitPieceItems(); SyncEventDataChange(); } private void SyncEventDataChange() { //- EventData 结构变化了,放在 这里 if (EventData.CollectRewardsId is not { Count: > 0 }) { EventData.CollectRewardsId = new List(); SaveData(); } } public async Task PrepareData() { ELog("PrepareData-> "); if(EventData.CurrentStagetId > 0) SetIsInGuideAnim(false); // if (true) // { // if (EventData.CurrentStagetId == 2) // { // EventData.CurrentStagetId = 1; // GenerateNextStageData(); // } // } FixOverStage(); UpdateCurrentStage(); await UpdateBoardData(); } private async Task UpdateBoardData() { var isSaveDataRequired = false; if (EventData.BoardData is not { Count: > 0 }) { CreateNewBoard(); isSaveDataRequired = true; } // if (EventData.PieceItems is not { Count: > 0 }) if(EventData.PieceDatas is not {Count: > 0}) { await CreateNewPieces(); isSaveDataRequired = true; } if (EventData.CollectItems is not { Count: > 0 }) { UpdateNewCollect(); isSaveDataRequired = true; } if (isSaveDataRequired) { SaveData(); } for (var i = CurrentPieceItems.Count; i < EventData.PieceDatas.Count; ++i) { CurrentPieceItems.Add(new PieceItem()); } } // 生成Item private void InitPieceItems() { EventCubeMeltPieceItems.Clear(); foreach (var eventCubeMeltPiece in EventCubeMeltPieces) { foreach (var pc in eventCubeMeltPiece.PieceConfig) { EventCubeMeltPieceItems.Add(new PieceItem { MeltId = eventCubeMeltPiece.ID, Weight = eventCubeMeltPiece.Weight, PieceConfig = pc, }); } } var valueBuilder = new StringBuilder(); foreach (var values in EventCubeMeltPieceItems) { valueBuilder.Append($"{values.MeltId},"); } ELog($"UpdatePieceItems - >{valueBuilder}"); } //TODO:CM 改成一次更新,多次返回的情况,每次更新 stageIndex 数据全部更新 /// /// Key 为盘面 CellType(typeItem.Type),Value 含配置 TypeKey 与需求数量。 /// public Dictionary GetCollectionItems() { var ls = new Dictionary(); for (var i = 0; i < EventCubeMeltCollectionData!.CollectingCountRequirement.Count; ++i) { var typeKey = EventCubeMeltCollectionData.CollectingItemRequirement[i]; var typeItem = EventCubeMeltItems.FirstOrDefault(elem => elem.ID == typeKey); if (typeItem is { Type: > 0 }) { var count = EventCubeMeltCollectionData.CollectingCountRequirement[i]; ls[typeItem.Type] = new EventCubeMeltCollectionItemInfo(typeKey, count); } } var valueBuilder = new StringBuilder(); foreach (var kv in ls) { valueBuilder.Append($"{kv.Key}(key:{kv.Value.TrueId}):{kv.Value.RequiredCount} "); } ELog($"GetCollectionItems - >{valueBuilder}"); return ls; } private void CreateNewBoard() { var idx = Random.Range(0, EventCubeMeltConfig.Count); ELog($"CreateNewBoard {idx}"); EventData.BoardData = EventCubeMeltConfig[idx]; var size = EventCubeMeltStageData.BoardSize; EventData.BoardViewSize= new Vector2Int(size[0], size[1]); } private readonly int _pieceCount = 3; private List CurrentPieceItems { get; set; } = new(); private async Task CreateNewPieces() { ELog("CreateNewPieces"); // EventData.PieceItems.Clear(); CurrentPieceItems.Clear(); EventData.PieceDatas ??= new List>(); EventData.PieceDatas.Clear(); if (EventCubeMeltStageData.InitItem is { Count: > 0 }) { //TODO:注意保证InitItem的有效 var pieceIds = EventCubeMeltStageData.InitItem[0]; var repValueIndexs = EventCubeMeltStageData.InitItem[1]; var ids = EventCubeMeltStageData.InitItem[2]; var hasPieceIds = new List(pieceIds.Where(id => id != 0)); for (var i = 0; i < _pieceCount; ++i) { var validMeltPieces = await GetValidMeltPieceItem(); var pieceId = pieceIds?[i]??0; if (pieceId == 0) { validMeltPieces = validMeltPieces.Where(item => !hasPieceIds.Contains(item.MeltId)).ToList(); validMeltPieces = DoFilterSecond(validMeltPieces, EventData.PieceDatas); var randomItem = WeightedRandom(validMeltPieces); pieceId = randomItem.MeltId; hasPieceIds.Add(pieceId); } var pieceItems = validMeltPieces.Where(item => item.MeltId == pieceId).ToList(); if (pieceItems.Count == 0) { LogError($"CreateNewPiecesInvalid pieceId = {pieceId}"); pieceItems = await GetValidMeltPieceItem(); } var repValueIndex = repValueIndexs[i] ; if (repValueIndex >= pieceItems.Count) { LogError($"CreateNewPiecesInvalid pieceId = {pieceId} repValueIndex={repValueIndex} validItemCount={pieceItems.Count}"); } if (repValueIndex >= 0 && repValueIndex < pieceItems.Count) { var entity = pieceItems[repValueIndex]; CurrentPieceItems.Add(entity); } else { var entity = pieceItems[Random.Range(0, pieceItems.Count)]; CurrentPieceItems.Add(entity); } EventData.PieceDatas.Add(GetMeltPieceReal(i,ids[i],isPowerSet:true)); } } else { var validMeltPieces = await GetValidMeltPieceItem(); var hasPieceIds = new List(); for (var i = 0; i < _pieceCount; ++i) { validMeltPieces = validMeltPieces.Where(item => !hasPieceIds.Contains(item.MeltId)).ToList(); validMeltPieces = DoFilterSecond(validMeltPieces,EventData.PieceDatas); var entity = WeightedRandom(validMeltPieces); hasPieceIds.Add(entity.MeltId); // var entity = validMeltPieces.FirstOrDefault(item => item.MeltId == pieceId); // var entities = validMeltPieces.Where(item => item.MeltId == pieceId).ToList(); // var entity = entities[Random.Range(0, entities.Count)]; if (ShouldBuildELog()) { ELog($"CreateNewPiece -> {entity?.MeltId} {string.Join(",", entity?.PieceConfig ?? new List())}"); } // EventData.PieceItems.Add(entity); CurrentPieceItems.Add(entity); EventData.PieceDatas.Add(GetMeltPieceReal(i)); } } // validMeltPieces = GetValidMeltPieceItem(); // for (var i = 0; i < _pieceCount; ++i) // { // validMeltPieces = DoFilterSecond(validMeltPieces,EventData.PieceDatas); // // var pieceId = WeightedRandom(validMeltPieces); // // var entity = validMeltPieces.FirstOrDefault(item => item.MeltId == pieceId); // // var entities = validMeltPieces.Where(item => item.MeltId == pieceId).ToList(); // var entity = entities[Random.Range(0, entities.Count)]; // ELog($"CreateNewPiece -> {entity?.MeltId} {string.Join(",",entity?.PieceConfig ?? new List())}"); // // EventData.PieceItems.Add(entity); // CurrentPieceItems.Add(entity); // EventData.PieceDatas.Add(GetMeltPieceReal(i)); // } } //- private PieceItem WeightedRandom(List pieceItems) { // return UnityEngine.Random.Range(0, pieceItems.Count); // var totalWeight = pieceItems.Sum(pieceItem => pieceItem.Weight); var totalWeight = pieceItems.Sum(item => item.ComplexityWeight); // double min = 0.0; // double max = 100.0; var random = new System.Random(); double randomValue = (random.NextDouble() * (totalWeight - 0)); // var randomValue = Random.Range(0f, totalWeight); var currentWeight = 0F; foreach (var pieceItem in pieceItems) { currentWeight += (float)pieceItem.ComplexityWeight; if (randomValue < currentWeight) return pieceItem; } var item101 = pieceItems.Find(item => item.MeltId == 101); // 默认返回一个点 // return 101 ; // 理论上不会执行到这里 return item101; } private void CreateNewData() { ELog("CreateNewData-> "); var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition; var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow()); var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow()); var ticketCount = GContext.container.Resolve().GetInitWelcomeGift(_fishingEvent.ID); // var ticketCount = GContext.container.Resolve().GetInitWelcomeGift(_fishingEvent.ID); EventData = new EventCubeMeltData { FirstOpen = true, EventId = _fishingEvent.ID, TicketCount = ticketCount, StartTime = startTime, EndTime = endTime, IsFinished = false, CurrentStagetId = 0, CollectItems = new List(), // PieceItems = new List(), PieceDatas = new List>(), CollectRewardsId = new List(), InflationRate = GContext.container.Resolve().InflationRate }; GContext.container.Resolve().SaveTransitionData(EventData.EventId, EventData.TicketCount); // GContext.container.Resolve().ClearGuide(nameof(GroupName.Block01)); } #region Log private static void LogError(object message) { Debug.LogError($"EventCubeMeltManager => {message} "); } private static bool ShouldBuildELog() { #if UNITY_EDITOR return EventCubeMeltLogPolicy.EnableELog; #else return false; #endif } private static void ELog(object message) { #if UNITY_EDITOR if (!EventCubeMeltLogPolicy.EnableELog) return; Debug.Log($" EventCubeMeltManager => {message} "); #endif } #endregion #region 时间相关 public bool CheckOpen() { // if (EventData == null) return false; // if (!IsTimeAllow()) return false; return true; } public bool IsTimeAllow() { var now = ZZTimeHelper.UtcNow(); return now >= StartTime && now <= EndTime; } public TimeSpan GetRemainingTime() { if (EventData == null) return TimeSpan.Zero; return EndTime - ZZTimeHelper.UtcNow(); } #endregion public void OnSettle() { //throw new NotImplementedException(); ELog("OnSettle"); } private void AddItem(int itemId, int count) { if (EventData?.CollectItems == null) return; var item = EventData.CollectItems.FirstOrDefault(x => x.ItemId == itemId); if (item != null) { item.Count += count; if (item.Count >= item.MaxCount) { item.Count = item.MaxCount; } } } //- [0,1] public float GetCollectProgress() { var iCount = EventData.CollectItems.Count; var iMaxCount = 0; var iCollCount = 0; // for (var i = 0; i < iCount; ++i) foreach (var item in EventData.CollectItems) { iCollCount += item.Count; iMaxCount += item.MaxCount; } return iCollCount * 1f / iMaxCount; } // 获取数据 public async Task SpawnNewPieceData() { } public async Task SpawnNewPiece(int index,int clearCount) { ELog("SpawnNewPiece-> "); var validMeltPieces = await GetValidNewPiece(index,clearCount); var pieceItem = WeightedRandom(validMeltPieces); if (pieceItem != null && ShouldBuildELog()) { ELog( $"SpawnNewPiece slotIndex={index} meltId={pieceItem.MeltId} row={pieceItem?.Row} col={pieceItem?.Col} complexity={pieceItem?.Complexity} complexityWeight={pieceItem.ComplexityWeight} config={string.Join(",", pieceItem.PieceConfig)}"); } // var entities = validMeltPieces.Where(item => item.MeltId == pieceId).ToList(); // var entity = entities[Random.Range(0, entities.Count)]; // var entity = validMeltPieces.Find(item => item.MeltId == pieceId); // ELog($"TCM: SpawnNewPiece -> {index} {pieceId} "); // EventData.PieceItems[index] = entity; // await Task.Run(() => { CurrentPieceItems[index] = pieceItem; EventData.PieceDatas[index] = GetMeltPieceReal(index); SaveData(); } ELog("SpawnNewPiece-> Finished-> "); } private async Task> GetValidNewPiece(int index,int clearCount) { var validMeltPieces = await GetValidMeltPieceItem(clearCount); var lastMeltPiece = EventData.PieceDatas[index]; // 和上一个不同 var validMeltPieces1 = DoFilterFirst(validMeltPieces,lastMeltPiece); if (validMeltPieces1.Count <= 0) { return validMeltPieces; } //- 和全部不同 var validMeltPieces2 = DoFilterSecond(validMeltPieces1, EventData.PieceDatas); if (validMeltPieces2.Count <= 0) { return validMeltPieces1; } return validMeltPieces2; } private List DoFilterFirst(List pieceItems, List ints) { var checkInts = ints.Select(value => value == 0 ? 0 : 99); return pieceItems.Where(p => !p.PieceConfig.SequenceEqual(checkInts)).ToList(); } private List DoFilterSecond(List pieceItems, List> eventDataPieceDatas) { return pieceItems?.Where(p => !eventDataPieceDatas?.Any(target => { var checkInts = target.Select(value => value == 0 ? 0 : 99); return p.PieceConfig.SequenceEqual(checkInts); }) ?? true) .ToList() ?? new List(); } // private List GetValidMeltPieceItemByInitItem(List pieceId, List repValues) // { // // throw new NotImplementedException(); // var list = new List(); // var grid = ConvertListIntToShape(EventData.BoardData,_boardDimension.x ,_boardDimension.y); // // foreach (var piece in EventCubeMeltPieceItems) // { // if (CanPlace(grid, ConvertListIntToShape(piece.PieceConfig,_pieceDimension.x ,_pieceDimension.y),piece.MeltId,out var p )) // { // piece.Complexity = p.Complexity; // list.Add(piece); // } // } // return list; // } public static double GaussianWeight(float x, double center = 0.5, double sigma = 1.0) { var diff = x - center; var exponent = -(diff * diff) / (2 * sigma * sigma); return Math.Exp(exponent); } //[0,1]-> [1,100] public static float CosineWeight(float x, float center = 0.5f, double frequency = 2.0 * Math.PI) { var scaledX = (x - center) * frequency; var cosineWeight = (float)(Math.Cos(scaledX) + 1.0F) / 2.0F; return cosineWeight * 99.0F + 1.0F; } private async Task> GetValidMeltPieceItem(int clearCount = 0) { var list = new List(); // this.BoardViewSize.x var grid = ConvertListIntToShape(EventData.BoardData,EventData.BoardViewSize.x ,EventData.BoardViewSize.y); ELog($"GetValidMeltPieceItem-> Count = {EventCubeMeltPieceItems.Count}"); var weightDifficultyParam = EventCubeMeltStageData.WeightDifficultyParam; var wdParam1 = weightDifficultyParam[0]; var wdParam2 = weightDifficultyParam[1]; var wdParam3 = weightDifficultyParam[2]; var priorityList1 = EventCubeMeltPieceItems.Where(item => item.MeltId > 300).ToList(); await Task.Run(() => { foreach (var piece in priorityList1) { if (CanPlace(grid, ConvertListIntToShape(piece.PieceConfig, _pieceDimension.x, _pieceDimension.y), piece.MeltId, out var p)) { piece.Complexity = p.Complexity; piece.ComplexityWeight = piece.Weight + (float)wdParam1 * Math.Exp(wdParam2 * p.Complexity + wdParam3 * clearCount); piece.Row = p.Row; piece.Col = p.Col; list.Add(piece); } } //一次性返回 }); // if (list.Count <= 0) { if (ShouldBuildELog()) { ELog($"GetValidMeltPieceItem priority1Empty boardData={string.Join(",", EventData.BoardData)}"); } // list.Add(EventCubeMeltPieceItems.Find(item => item.MeltId == 101)); var priorityList2 = EventCubeMeltPieceItems.Where(item => item.MeltId <= 300).ToList(); // foreach (var piece in priorityList2) // { // if (CanPlace(grid, ConvertListIntToShape(piece.PieceConfig,_pieceDimension.x ,_pieceDimension.y),piece.MeltId,out var p)) // { // piece.Complexity = p.Complexity; // piece.ComplexityWeight = piece.Weight + (float) wdParam1 * Math.Exp(wdParam2 * p.Complexity + wdParam3 * clearCount); // piece.Row = p.Row; // piece.Col = p.Col; // list.Add(piece); // } // } await Task.Run(() => { // var tmp = new List(); //线程内临时列表 foreach (var piece in priorityList2) { if (CanPlace(grid, ConvertListIntToShape(piece.PieceConfig, _pieceDimension.x, _pieceDimension.y), piece.MeltId, out var p)) { piece.Complexity = p.Complexity; piece.ComplexityWeight = piece.Weight + (float)wdParam1 * Math.Exp(wdParam2 * p.Complexity + wdParam3 * clearCount); piece.Row = p.Row; piece.Col = p.Col; list.Add(piece); } } //一次性返回 }); } ELog($"ClearCount count={clearCount}"); // StringBuilder sb; #if UNITY_EDITOR var sb = new StringBuilder(); foreach (var item in list) { sb.Append(item.MeltId + $"({item.Complexity:F3} , {item.ComplexityWeight:F3}) " ); } ELog($"TCM:GetValidMeltPieceItem-> {sb}"); #endif return list; } private int[,] ConvertListIntToShape(List listInt,int gridW,int gridH) { // var width = _pieceDimension.x; // var height = _pieceDimension.y; var width = gridW; var height = gridH; var shape = new int[height,width]; for (var row = 0; row < height; row++) { for (var col = 0; col < width; col++) { shape[row, col] = listInt[row * width + col]; } } return shape; } // private int[,] ConvertToShape(EventCubeMeltPiece piece) // { // var width = _pieceDimension.x; // var height = _pieceDimension.y; // // var shape = new int[height,width]; // var pieceData = piece.PieceConfig[0]; // for (var row = 0; row < height; row++) // { // for (var col = 0; col < width; col++) // { // shape[row, col] = pieceData[row * width + col]; // } // } // return shape; // } // public void PrintShape(int[,] grid, string tag ) // { // var sb = new StringBuilder(); // sb.Append(tag); // var height = grid.GetLength(0); // var width = grid.GetLength(1); // 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.Append(" \n"); // } // Debug.Log(sb.ToString()); // } // 方形的 // private bool CanPlaceDown(int[,] grid, int [,] shape) // { // ELog("CanPlaceDown -> "); // var gridSize = grid.GetLength(0); // var shapeSize = shape.GetLength(0); // if (shapeSize > gridSize ) // return false; // // List<(int row, int col)> shapePositions = new(); // for (int i = 0; i < shapeSize; i++) // { // for (int j = 0; j < shapeSize; j++) // { // if (shape[i, j] != 0 ) // { // shapePositions.Add((i, j)); // } // } // } // // 如果形状是空的,总是可以放入 // if (shapePositions.Count == 0) // return true; // // for (var gridRow = 0; gridRow < gridSize; gridRow++) // { // for (var gridCol = 0; gridCol < gridSize; gridCol++) // { // // 检查是否可以把形状放在某个位置,使得所有有效格子都在网格内且为空 // if (CanPlaceShapeAtPosition(grid, shapePositions, gridRow, gridCol)) // { // return true; // } // } // } // return false; // } private bool CanPlace(int[,] grid, int[,] shape, int pieceId,out ShapePlacement bestPlacementResult) { ELog($" CanPlace -> {pieceId}"); var gridRow = grid.GetLength(0); var gridCol = grid.GetLength(1); var shapeRow = shape.GetLength(0); var shapeCol = shape.GetLength(1); // 收集形状中所有有效格子的位置 var cells = new List<(int r, int c)>(shapeRow * shapeCol); int minR = int.MaxValue, maxR = int.MinValue; int minC = int.MaxValue, maxC = int.MinValue; for (int r = 0; r < shapeRow; r++) { for (int c = 0; c < shapeCol; c++) { if (shape[r, c] != 0) { cells.Add((r, c)); if (r < minR) minR = r; if (r > maxR) maxR = r; if (c < minC) minC = c; if (c > maxC) maxC = c; } } } bestPlacementResult = new ShapePlacement() { Id = pieceId, Shape = (int[,])shape.Clone(), Col = int.MinValue, Row = int.MinValue, Weight = 0, Complexity = float.MaxValue, }; // 若形状没有有效格子,视为可放置 if (cells.Count == 0) { bestPlacementResult.Col = 0; bestPlacementResult.Row = 0; return true; } // 有效格子全部进网格的必要偏移范围: // 0 <= r + dy <= gr-1 对所有有效 r // 0 <= c + dx <= gc-1 对所有有效 c var minDy = -minR; var maxDy = (gridRow - 1) - maxR; var minDx = -minC; var maxDx = (gridCol - 1) - maxC; // 无可行偏移的快速剪枝 if (minDy > maxDy || minDx > maxDx) { return false; } for (int offY = minDy; offY <= maxDy; offY++) { for (int offX = minDx; offX <= maxDx; offX++) { bool ok = true; // 检查所有有效格子在该偏移下是否都落在空位(0) for (int i = 0; i < cells.Count; i++) { var cell = cells[i]; int rr = cell.r + offY; int cc = cell.c + offX; // 这里不需要再做边界判断(已由范围剪枝保证) if (grid[rr, cc] != 0) { ok = false; break; } } if (ok) { var p = CreateNewShapePlacement(offY,offX,shape,grid); p.Id = pieceId; // ELog($" CanPlace -> p = {p} best = {bestPlacementResult}"); if (p.Complexity < bestPlacementResult.Complexity) bestPlacementResult = p; } } } // ELog($" CanPlace -> {pieceId} bestPlacementResult = {bestPlacementResult}"); if (bestPlacementResult.Complexity < float.MaxValue) { // ELog($" CanPlace -> {pieceId} bestPlacementResult = {bestPlacementResult}"); return true; } // dx = 0; dy = 0; return false; } private ShapePlacement CreateNewShapePlacement(int row, int col,int[,] shape,int [,] grid) { var simulatedBoard = SimulatePlacement(grid,shape, row, col); var pf1 = Calculate(grid); var pf = Calculate(simulatedBoard); return new ShapePlacement() { Row = row, Col = col, Shape = shape, Weight = 1, Complexity = pf - pf1 }; } public static float Calculate(int[,] matrix) { if (matrix == null) return 0; int height = matrix.GetLength(0); int width = matrix.GetLength(1); // 创建带边框的矩阵便于计算 int[,] paddedMatrix = new int[height + 2, width + 2]; // 复制原矩阵到中心 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { paddedMatrix[i + 1, j + 1] = matrix[i, j]; } } // 计算边界长度(相邻0-1边的数量) int boundaryEdges = 0; for (int i = 1; i <= height; i++) { for (int j = 1; j <= width; j++) { if (paddedMatrix[i, j] != 0) { // 检查四个邻居 if (paddedMatrix[i - 1, j] == 0) boundaryEdges++; // 上 if (paddedMatrix[i + 1, j] == 0) boundaryEdges++; // 下 if (paddedMatrix[i, j - 1] == 0) boundaryEdges++; // 左 if (paddedMatrix[i, j + 1] == 0) boundaryEdges++; // 右 } } } // 计算面积 int area = 0; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (matrix[i, j] != 0) area++; } } var p = (float)Math.Sqrt(area)* 4F; // var x = Math.Exp(p); // 边界复杂度 = 边界长度 / 面积 return area > 0 ? (float)boundaryEdges / p : 0.0F; } private int[,] SimulatePlacement(int[,] board, int[,] shape, int row, int col) { // 创建棋盘的副本 int[,] simulatedBoard = (int[,])board.Clone(); var height = shape.GetLength(0); var width = shape.GetLength(1); var boardH = simulatedBoard.GetLength(0); var boardW = simulatedBoard.GetLength(1); // 放置形状 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { var rowI = row + i; var colJ = col + j; if (rowI < 0 || rowI >= boardH) continue; if (colJ < 0 || colJ >= boardW) continue; if (shape[i, j] != 0) { simulatedBoard[rowI, colJ] = 1; // 模拟形状,都放置成 1 先 } } } return simulatedBoard; } private static bool CanPlaceShapeAtPosition(int[,] grid, List<(int row, int col)> validCells, int referenceRow, int referenceCol) { var gridSize = grid.GetLength(0); // 我们尝试把形状的某个有效格子对齐到网格的(referenceRow, referenceCol) // 然后检查其他有效格子是否都在网格内且为空 // 对第一个有效格子作为对齐点 var firstCell = validCells[0]; // 计算偏移量:如果要把这个有效格子放在(referenceRow, referenceCol) // 那么形状的左上角应该在: int startRow = referenceRow - firstCell.row; int startCol = referenceCol - firstCell.col; // 检查所有有效格子 foreach (var (shapeRow, shapeCol) in validCells) { int gridRow = startRow + shapeRow; int gridCol = startCol + shapeCol; // 检查是否在网格内 if (gridRow < 0 || gridRow >= gridSize || gridCol < 0 || gridCol >= gridSize) { return false; } // 检查网格位置是否为空 if (grid[gridRow, gridCol] != 0) { return false; } } return true; } // 调试用,看看 public static List<(int row, int col)> FindAllPositions(int[,] grid, int[,] shape) { int M = grid.GetLength(0); int N = shape.GetLength(0); List<(int row, int col)> positions = new List<(int, int)>(); if (N > M) return positions; // 预计算形状的非空位置 List<(int row, int col)> shapePositions = new List<(int, int)>(); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (shape[i, j] != 0) { shapePositions.Add((i, j)); } } } // 遍历所有可能的放置位置 for (int i = 0; i <= M - N; i++) { for (int j = 0; j <= M - N; j++) { bool canPlace = true; foreach (var (si, sj) in shapePositions) { int gi = i + si; int gj = j + sj; if (grid[gi, gj] != 0) { canPlace = false; break; } } if (canPlace) { positions.Add((i, j)); } } } return positions; } public List GetMeltPieceReal(int index,int isRepValue = 1,bool isPowerSet = false) { var pieceItem = CurrentPieceItems[index]; var meltPiece = EventCubeMeltPieces.FirstOrDefault(meltPiece => meltPiece.ID == pieceItem.MeltId ); ELog($"GetMeltPieceReal-> 12 {index} {meltPiece.ID} ,{meltPiece?.PieceConfig} {EventCubeMeltPieces.Count}"); var prop = EventCubeMeltCollectionData.CollectingItemProb; var meltPieceConfig = pieceItem.PieceConfig; var rate = meltPiece?.DoubleItemProb ?? 0; var num = 1; if (isPowerSet) { prop = new List { 1 }; rate = 0; } if (rate > 0) { var r = Random.value; // [0,1) if (r < rate) { num = 2; } } var rand = GetRepValue(); var collectIds = GetCollectionIds(); // var randWeight = GetRepValuesWithWeight(rand); // var thePieceData = (isRepValue == 0) ?meltPieceConfig : SlotsGenerator.GenerateSlots(num,prop,meltPieceConfig, rand); var thePieceData2 = (isRepValue == 0) ?meltPieceConfig : SlotsGenerator.GenerateSlots2(num,prop,meltPieceConfig,rand,collectIds); return thePieceData2; //return meltPieceConfig; } public List GetMeltPieceData(int index) { var pieceData = EventData.PieceDatas.ElementAtOrDefault(index); return pieceData; } private List GetCollectionIds() { var set = new HashSet(); var collectionItems = GetCollectionItems(); foreach (var kv in collectionItems) { for (var i = 0; i < kv.Value.RequiredCount; ++i) { set.Add(kv.Key); } } return set.ToList(); } // 返回可替换的有效材料值 private List GetRepValue() { var set = new HashSet(); var collectionItems = GetCollectionItems(); foreach (var kv in collectionItems) { for (var i = 0; i < kv.Value.RequiredCount; ++i) { set.Add(kv.Key); } } // // 删除已经收集完的Item var hasCollectItems = EventData.CollectItems; foreach (var item in hasCollectItems.Where(item => item.Count >= item.MaxCount)) { set.Remove(item.ItemId); } if (ShouldBuildELog()) { ELog($"GetRepValue candidates={string.Join(",", set)}"); } return set.ToList(); } public void UpdateCurrentStage() { var isFirstLoop = EventData.Cycle == 0 ; var stageList = isFirstLoop ? EventCubeMeltMainData?.StageFirstList : EventCubeMeltMainData?.StageLoopList ?? new List(); var stageId = stageList?.ElementAtOrDefault(EventData.CurrentStagetId); ELog($"UpdateCurrentStage -> {EventData.CurrentStagetId}, {stageId}"); EventCubeMeltStageData = _tables.TbEventCubeMeltStage.DataList.FirstOrDefault(data => data.ID == stageId); EventCubeMeltCollectionData = _tables.TbEventCubeMeltCollection.DataList.FirstOrDefault(data => data.ID == stageId); EventCubeMeltConfig = EventCubeMeltStageData?.StageConfig ?? new List>(); EventStageList = stageList; StageCount = EventStageList?.Count ?? 0; var boardSize = EventCubeMeltStageData?.BoardSize; if (boardSize == null) { LogError("Error Board No BoardSize "); boardSize = new List{8,8}; } // BoardViewSize = new Vector2Int(boardSize[0], boardSize[1]); } private void UpdateNewCollect() { EventData.CollectItems.Clear(); var items = GetCollectionItems(); foreach (var kv in items) { EventData.CollectItems.Add(new CollectItem { ItemId = kv.Key, ItemTrueId = kv.Value.TrueId, Count = 0, MaxCount = kv.Value.RequiredCount }); } } public string GetCollectIconByType(int typeId) { var item = EventCubeMeltItems.FirstOrDefault(elem => elem.Type == typeId); return item != null ? item.Icon : string.Empty; } public string GetCollectIconById(int typeId) { var item = EventCubeMeltItems.FirstOrDefault(elem => elem.ID == typeId); return item != null ? item.Icon : string.Empty; } // 收集 public void AddCollect(Dictionary values) { if (values is not { Count: > 0 }) { return; } if (ShouldBuildELog()) { var valueStrings = string.Join(", ", values.Select(kv => $"{kv.Key}: {kv.Value}")); ELog($"LCM-> AddCollect -> {valueStrings}"); } foreach (var kv in values) { AddItem(kv.Key, kv.Value); } } public bool IsThisCollectFinished() { var items = EventData.CollectItems; var isFinish = items.All(item => item.Count >= item.MaxCount); return isFinish; } public bool IsGetStageReward() { var isGet = EventData.CollectRewardsId.Contains(EventData.CurrentStagetId); return isGet; } public bool IsGetFinalReward() { return EventData.IsFinalRewardGet; } private async Task UpdateNextBoardAndPieces() { ELog("UpdateNextBoardAndPieces()"); CreateNewBoard(); await CreateNewPieces(); UpdateNewCollect(); SaveData(); } public bool IsFinishAllStage() => EventData.CurrentStagetId >= StageCount - 1; public async void UpdateToNextStage() { EventData.CurrentStagetId++; if (EventData.CurrentStagetId > StageCount -1) { OnFinish(); return; } UpdateCurrentStage(); await UpdateNextBoardAndPieces(); GContext.Publish(new EventCubeMeltForNextStage()); } public void GenerateNextStageData() { EventData.CurrentStagetId++; if (EventData.CurrentStagetId > StageCount -1) { // ClearData(); // Init(); GenerateNewLoopFirstData(); } UpdateCurrentStage(); _ = UpdateNextBoardAndPieces(); } private void GenerateNewLoopFirstData() { var tickCount = EventData.TicketCount; var cycle = EventData.Cycle; ClearData(); Init(); EventData.TicketCount = tickCount; EventData.FirstOpen = false; EventData.Cycle = cycle + 1; GContext.container.Resolve().SaveTransitionData(EventData.EventId, EventData.TicketCount); SaveData(); } public bool PlayGetStageReward() { ELog("AddStageRewards"); var dropId = EventCubeMeltCollectionData.CollectingReward; var playerItemData = GContext.container.Resolve(); var changeSource = new ChangeSource(EventData.EventId, "Event_Minigame1", "Event_Minigame1_Block", "StageReward"); playerItemData.AddItemByDropLureInflation(EventData.InflationRate,dropId,changeSource); GContext.Publish(new ShowData()); EventData.CollectRewardsId.Add(EventData.CurrentStagetId); SaveData(); return true; } // public bool PlayGetFinalRewards() // { // ELog("PlayGetFinalRewards-> "); // // var dropId = EventCubeMeltMainData.FinalReward; // var playerItemData = GContext.container.Resolve(); // var changeSource = new ChangeSource(EventData.EventId, "Event_Minigame1", "Event_Minigame1_Block", "StageReward"); // playerItemData.AddItemByDropLureInflation(null, dropId,changeSource); // GContext.Publish(new ShowData()); // // EventData.IsFinalRewardGet = true; // SaveData(); // return true; // } public void OnFinish() { ELog("OnFinsish()"); ReOpen(); } public void ReOpen() { GenerateNewLoopFirstData(); GContext.Publish(new EventCubemeltExitAct()); } public void UpdateGridData(List gridData) { EventData.BoardData = gridData; SaveData(); } //- // private Vector2Int _boardDimension; private Vector2Int _pieceDimension; // public void SetBoardDimension(Vector2Int dimension) // { // _boardDimension = dimension; // } public void SetPieceDimension(Vector2Int dimension) { _pieceDimension = dimension; } private EventCubeMeltData PrevData { get; set; } private EventCubeMeltData NextData { get; set; } //- private List PrevBoardData { get; set; } public List> PrevPieceDatas { set; get; } private List CurrentBoardData { get; set; } public List> CurrentPieceDatas { set; get; } // -当前的索引 public int GuideIndex { get; set; } public void SaveDataForPrev() { // PrevData = EventData.ShallowClone(); //ELog($"SaveDataForPrev()-> {JsonConvert.SerializeObject(PrevData)}"); #if UNITY_EDITOR PrevBoardData = EventData.BoardData.ToList(); PrevPieceDatas = EventData.PieceDatas.Select(innerList => new List(innerList)).ToList(); #endif } public void SaveDataForNow() { #if UNITY_EDITOR CurrentBoardData = EventData.BoardData.ToList(); CurrentPieceDatas = EventData.PieceDatas.Select(innerList => new List(innerList)).ToList(); SaveData(); #endif } public void RestoreData() { EventData.BoardData = CurrentBoardData.ToList(); EventData.PieceDatas = CurrentPieceDatas.Select(innerList => new List(innerList)).ToList(); SaveData(); } public void LoadPrevData() { EventData.BoardData = PrevBoardData.ToList(); EventData.PieceDatas = PrevPieceDatas.Select(innerList => new List(innerList)).ToList(); if (ShouldBuildELog()) { ELog($"LoadPrevData()-> {JsonConvert.SerializeObject(EventData)}"); } SaveData(); } public bool IsUnCollectValue(int valueType) { var typeItem = EventCubeMeltItems.FirstOrDefault(elem => elem.Type == valueType); var param = typeItem?.Param ?? 0; return param > 1; } public List ReplaceValue(int valueType) { var typeItem = EventCubeMeltItems.FirstOrDefault(elem => elem.Type == valueType); var param = typeItem?.Param ?? 0; if (param <= 1) { var values = new List { valueType }; if (ShouldBuildELog()) { ELog($"LCM-> ReplaceValue -> {valueType} -> {string.Join(", ", values)}"); } return values; } // else // { // return new List(); // } var list = new List(); var validList = GetRepValue(); for (var i = 0; i < param; i++) { var va = validList[Random.Range(0, validList.Count)]; list.Add(va); } if (ShouldBuildELog()) { ELog($"LCM-> ReplaceValue -> {valueType} -> {string.Join(", ", list)}"); } return list; } //- public List> GetBombValues(List bombValues) { var p = new List>(); foreach (var bombValue in bombValues) { p.Add(ReplaceValue(bombValue)); } return p; } public void JumpToStage(int newStep) { // // var newStep = newDisStep - 1; ELog($"JumpToStage -> {newStep }"); if (newStep < 0) return; EventData.CurrentStagetId = newStep - 1; GContext.Publish(new EventCubeMeltStageFinish()); UpdateToNextStage(); // EventData.CurrentStagetId = newStep; // if (EventData.CurrentStagetId > StageCount -1) // { // OnFinish(); // return; // } // //- // EventData.IsFinished = false; // UpdateCurrentStage(); // UpdateNextBoardAndPieces(); // GContext.Publish(new EventCubeMeltForNextStage()); } //-检查当前管卡是否已经完 public bool IsOverStage() { return EventData.CurrentStagetId >= StageCount; } public void FixOverStage() { if (IsOverStage()) { EventData.CurrentStagetId = StageCount - 1; } var isFinishThisStage = IsThisCollectFinished(); var isGetStageReward = IsGetStageReward(); if (EventData.CurrentStagetId < StageCount - 1) { if (isFinishThisStage && isGetStageReward) { GenerateNextStageData(); } } else { // var isFinalGet = IsGetFinalReward(); if (isFinishThisStage && isGetStageReward) { GenerateNextStageData(); } } } public bool CheckCollectFinish(List listItems) { var items = EventData.CollectItems; return items.Any(item => listItems.Contains(item.ItemId) && item.Count == item.MaxCount); } public bool CheckBombFinsh(List listBomb) { return true; } public bool IsInGuideAnim() { return EventData?.IsInGuide ?? false ; } public void SetIsInGuideAnim(bool isInGuide) { // IsInGuide = isInGuide; EventData.IsInGuide = isInGuide; SaveData(); } public int GetFinalReward() { ELog("GetFinalReward"); var finalStage = StageCount - 1; // var isFirstLoop = EventData.Cycle == 0 ; // var stageList = isFirstLoop ? EventCubeMeltMainData?.StageFirstList : EventCubeMeltMainData?.StageLoopList; // var stageId = stageList?.ElementAtOrDefault(finalStage); var stageId = EventStageList?.ElementAtOrDefault(finalStage); var cubeMeltData = _tables.TbEventCubeMeltCollection.DataList.FirstOrDefault(data => data.ID == stageId); return cubeMeltData?.CollectingReward?? 0; } }