using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using MrWu.Debug; using Newtonsoft.Json; using Server.Core; namespace Server { //最近 100 场 最大输值的平均值 //N 倍 // 连输 3 场以上 每加1场 增加 %30 发好牌的几率 // 连赢 4 场以上 每加1场 增加 %20 发差牌的几率 // 输的局数 是平均值的 N 倍 拥有%100 发好牌的几率 // 赢的局数 是平均值的 2N 倍 拥有%100 发差牌的几率 // 计算最大输值的 除以赔率 换算成积分 // 当天输的超过 N 倍 x %30 每超过 %1 就增加 1的发好牌几率 封顶 %50 // 当天赢的超过 N 倍 x %50 每超过 %1 就增加 1的发差牌几率 封顶 %50 /// /// 幸运玩家管理器 /// public partial class LuckPlayerManager : SingleInstance { /// /// 玩法的战绩记录 /// private readonly ConcurrentDictionary _wayGamePlayerScoreRecords = new ConcurrentDictionary(); private readonly ConcurrentQueue _wayGamePlayerWayIds = new ConcurrentQueue(); private const string RecordPath = "LuckRecord/WayRecordDataV2.txt"; /// /// 15天未玩游戏就过期 /// private const long ExpireTime = 15L * 24 * 60 * 60 * 1000; /// /// 统计次数 /// private const int StaticsCnt = 100; /// /// 输的平均值倍数 /// private float _loseMultConfig = 3f; private bool isEnable = false; public event Action PlayerScoreUpdate; public event Action WayRecordUpdate; /// /// 加载数据 /// public void LoadData(float loseMultConfig) { isEnable = loseMultConfig >= 1f; this._loseMultConfig = loseMultConfig; LoadRecordData(); LoadClubBalanceData(); Debug.Info($"是否启用:{isEnable} {loseMultConfig}"); } private void LoadRecordData() { if (!isEnable) { return; } if (_wayGamePlayerScoreRecords.Count > 0) { Debug.Error("已加载过幸运玩家数据!"); return; } _wayGamePlayerScoreRecords.Clear(); try { if (File.Exists(RecordPath)) { string jsonValue = File.ReadAllText(RecordPath); FriendLuckRecord records = JsonConvert.DeserializeObject(jsonValue); foreach (var record in records.ScoreRecords) { //先进行数据初始化 record.PlayerScoreRecords = new ConcurrentDictionary(); foreach (var item in record.Records) { if (!record.PlayerScoreRecords.TryAdd(item.UserId, item)) { Debug.Error($"怎么会有重复数据:{item.UserId}"); } record.PlayerUserIds.Enqueue(item.UserId); } if (_wayGamePlayerScoreRecords.TryAdd(record.WayId, record)) { _wayGamePlayerWayIds.Enqueue(record.WayId); } else { Debug.Error($"玩法怎么会有重复数据:{record.WayId}"); } } } } catch (Exception e) { Debug.Error($"LoadRecordData error:{e.Message}"); } } /// /// 保存数据 /// public void SaveData() { if (!isEnable) { return; } try { FriendLuckRecord records = new FriendLuckRecord(); foreach (var record in _wayGamePlayerScoreRecords.Values) { if (record.PlayerScoreRecords != null) { record.Records.Clear(); foreach (var item in record.PlayerScoreRecords.Values) { record.Records.Add(item); } } records.ScoreRecords.Add(record); } string jsonValue = JsonConvert.SerializeObject(records); FileInfo fileInfo = new FileInfo(RecordPath); if (!fileInfo.Directory.Exists) { fileInfo.Directory.Create(); } File.WriteAllText(RecordPath, jsonValue); SaveClubBalanceData(); } catch (Exception e) { Debug.Error($"SaveData error:{e.Message}"); } } private ConcurrentQueue _randoms = new ConcurrentQueue(); private Random GetRandom() { if (_randoms.TryDequeue(out Random random)) { return random; } return new Random(); } private void ReleaseRandom(Random random) { _randoms.Enqueue(random); } /// /// 获取幸运值索引 /// /// /// /// public (int, int) GetLuckValueIndex(string wayId, List userIds) { int luckIndex = -1; int badLuckIndex = -1; if (!isEnable) { return (luckIndex, badLuckIndex); } List scoreRecords = new List(); if (_wayGamePlayerScoreRecords.TryGetValue(wayId, out GamePlayerScoreRecord record)) { for (int i = 0; i < userIds.Count; i++) { if (userIds[i] <= 0) { Debug.Warning("怎么会有Userid 小于0的情况!"); return (luckIndex, badLuckIndex); } if (record.PlayerScoreRecords.TryGetValue(userIds[i], out PlayerScoreRecord playerRecord)) { scoreRecords.Add(playerRecord); } } } else { return (luckIndex, badLuckIndex); } Random random = GetRandom(); //先计算连输 连赢 for (int i = 0; i < scoreRecords.Count; i++) { //连续赢 增加发差牌概率 if (badLuckIndex < 0 && scoreRecords[i].ContinuousWinCnt > 4) //已经连赢4次以上 { scoreRecords[i].ContinuousWin_BadWeight = 20 * (scoreRecords[i].ContinuousWinCnt - 4); if (random.Next(0, 100) < scoreRecords[i].ContinuousWin_BadWeight) { badLuckIndex = i; } Debug.Info($"连续赢:{i} {badLuckIndex} 触发发差牌概率:{scoreRecords[i].ContinuousWin_BadWeight}"); } //连续输 增加发好牌的概率 if (luckIndex < 0 && scoreRecords[i].ContinuousLoseCnt > 3) //已经连输3次以上 { scoreRecords[i].ContinuosLose_LuckWeight = 30 * (scoreRecords[i].ContinuousLoseCnt - 3); if (random.Next(0, 100) < scoreRecords[i].ContinuosLose_LuckWeight) { luckIndex = i; } Debug.Info($"连续输:{i} {luckIndex} 触发发好牌概率:{scoreRecords[i].ContinuosLose_LuckWeight}"); } } //计算新手保护 if (luckIndex < 0) { for (int i = scoreRecords.Count - 1; i >= 0; i--) { if (i == luckIndex || i == badLuckIndex) { continue; } if (scoreRecords[i].NewPlayerCnt > 0) { scoreRecords[i].NewPlayerCnt--; if (random.NextDouble() < 0.2f) { luckIndex = i; Debug.Info($"新手保护发牌:{scoreRecords[i].UserId}"); break; } } } } //计算俱乐部平衡 if (BalanceAllConfigs.Count > 0) { for (int i = 0; i < scoreRecords.Count; i++) { if (luckIndex >= 0 && badLuckIndex >= 0) { break; } if (i == luckIndex || i == badLuckIndex) { continue; } ClubPlayerBalanceType clubPlayerBalanceType = GetBalancePlayerType(record.BeiLv,record.WayFightCnt, scoreRecords[i].UserId); // 不计算平衡 if (clubPlayerBalanceType < ClubPlayerBalanceType.Luck) { continue; } if (luckIndex < 0 && clubPlayerBalanceType == ClubPlayerBalanceType.Luck) { luckIndex = i; Debug.Info($"俱乐部平衡幸运:{scoreRecords[i].UserId}"); } if (badLuckIndex < 0 && clubPlayerBalanceType == ClubPlayerBalanceType.BadLuck) { badLuckIndex = i; Debug.Info($"俱乐部平衡倒霉:{scoreRecords[i].UserId}"); } } } //计算这段时间的输赢平衡 if (record.AverageLoseMax < -1) { for (int i = scoreRecords.Count - 1; i >= 0; i--) { if (i == luckIndex || i == badLuckIndex) { continue; } if (luckIndex < 0 && scoreRecords[i].NextFightLuck >= 100) { luckIndex = i; scoreRecords[i].NextFightLuck = 0; Debug.Info($"这个位置输的多,发好牌:{i}"); } if (badLuckIndex < 0 && scoreRecords[i].NextFightBadLuck >= 100) { badLuckIndex = i; scoreRecords[i].NextFightBadLuck = 0; Debug.Info($"这个位置赢的多,发差牌:{i}"); } } } //计算一天的输赢平衡 if (record.MaxLoseScore < -1) { for (int i = 0; i < scoreRecords.Count; i++) { if (i == luckIndex || i == badLuckIndex) { continue; } float multValue = Math.Abs(scoreRecords[i].DayScore / record.MaxLoseScore); if (multValue > _loseMultConfig) { //计算幸运 if (luckIndex < 0 && scoreRecords[i].DayScore < 0 && multValue > _loseMultConfig) { float luckProbability = (multValue / _loseMultConfig - 1f) - 0.3f; if (luckProbability > 0 && random.NextDouble() < luckProbability) { luckIndex = i; Debug.Info($"按天计算 幸运玩家概率:{i} {luckProbability} {multValue} {_loseMultConfig}"); } } if (badLuckIndex < 0 && scoreRecords[i].DayScore > 0 && multValue > _loseMultConfig) { float badLuckProbability = (multValue / _loseMultConfig - 1f) - 0.5f; if (badLuckProbability > 0 && random.NextDouble() < badLuckProbability) { badLuckIndex = i; Debug.Info($"按天计算 倒霉玩家概率:{i} {badLuckProbability} {multValue} {_loseMultConfig}"); } } } } } ReleaseRandom(random); if (luckIndex >= 0 || badLuckIndex >= 0) { FriendRoomStatistics.Instance.AddBalance(wayId, luckIndex < 0 ? -1 : scoreRecords[luckIndex].UserId, badLuckIndex < 0 ? -1 : scoreRecords[badLuckIndex].UserId); Debug.Info($"幸运玩家:{(luckIndex >= 0 ? scoreRecords[luckIndex].UserId : -1)} {(badLuckIndex >= 0 ? scoreRecords[badLuckIndex].UserId : -1)}"); } return (luckIndex, badLuckIndex); } public void AddOnceFight(string wayId, int userid, int score) { if (!isEnable) { return; } if (!_wayGamePlayerScoreRecords.TryGetValue(wayId, out GamePlayerScoreRecord record)) { return; } PlayerScoreRecord playerScoreRecord = record.PlayerScoreRecords.GetOrAdd(userid, (key) => { record.PlayerUserIds.Enqueue(key); Debug.Info($"创建玩家x {userid}"); return new PlayerScoreRecord() { UserId = key, LastEditorTime = TimeInfo.Instance.FrameTime }; } ); //连续输赢的统计 小赢小输不算连续 if (score > 5) { playerScoreRecord.ContinuousWinCnt++; playerScoreRecord.ContinuousLoseCnt = 0; } if (score < -5) { playerScoreRecord.ContinuousLoseCnt++; playerScoreRecord.ContinuousWinCnt = 0; } playerScoreRecord.DayScore += score; if (score >= 0) playerScoreRecord.WinFightCnt++; if (score > 0 && playerScoreRecord.NewPlayerCnt > 0) { playerScoreRecord.NewPlayerCnt--; } playerScoreRecord.TotalFightCnt++; //最大输值的平均有值,才开始记录 //记录值 playerScoreRecord.LastEditorTime = TimeInfo.Instance.FrameTime - 5000; Debug.Info($"单局统计:{record.BeiLv} {record.WayFightCnt} {playerScoreRecord.ToString()}"); AddOnceFightScore(record.BeiLv,record.WayFightCnt, userid, score); } /// /// 添加积分到记录 所有局数都打完才记录 临时解散的不算 /// /// 玩法Id /// 倍率 /// fightCnt /// 分值 public void AddScoreRecord(string wayId, float beiLv, int fightCnt, FightScore[] fightScore) { if (!isEnable) { return; } GamePlayerScoreRecord record = _wayGamePlayerScoreRecords.GetOrAdd(wayId, (key) => { _wayGamePlayerWayIds.Enqueue(wayId); Debug.Info($"创建 GamePlayerScoreRecord:{wayId}"); return new GamePlayerScoreRecord() { WayId = wayId, BeiLv = beiLv, WayFightCnt = fightCnt, PlayerScoreRecords = new ConcurrentDictionary(), LastEditorTime = TimeInfo.Instance.FrameTime }; }); Debug.Info($"_wayGamePlayerScoreRecords Count:{_wayGamePlayerScoreRecords.Count}"); int winMaxScore = 0; int loseMaxScore = 0; StringBuilder stringBuilder = new StringBuilder(); foreach (var score in fightScore) { if (score.Value > winMaxScore) { winMaxScore = (int)score.Value; } if (score.Value < loseMaxScore) { loseMaxScore = (int)score.Value; } } //最大输值的平均有值,才开始记录 //记录值 foreach (var score in fightScore) { stringBuilder.Append($"userid:{score.UserId} value:{score.Value} "); PlayerScoreRecord playerScoreRecord = record.PlayerScoreRecords.GetOrAdd(score.UserId, (key) => { record.PlayerUserIds.Enqueue(key); Debug.Info($"创建玩家- {score.UserId}"); return new PlayerScoreRecord() { UserId = key, NewPlayerCnt = 10, //前10局新手保护 概率是%20 所以相当于最多2次机会 LastEditorTime = TimeInfo.Instance.FrameTime }; } ); playerScoreRecord.PingHengFightCnt--; if (playerScoreRecord.PingHengFightCnt <= 0) { Debug.Info($"平衡场次重算:{playerScoreRecord.UserId}"); Random random = GetRandom(); playerScoreRecord.PingHengFightCnt = random.Next(60, 100); if (playerScoreRecord.DayScore < 0) { playerScoreRecord.DayScore /= 2; } else { playerScoreRecord.DayScore = 0; } ReleaseRandom(random); } playerScoreRecord.TotalScore += (int)score.Value; Debug.Info($"添加:{playerScoreRecord.UserId} {score.Value} {playerScoreRecord.TotalScore}"); if (record.AverageLoseMax < -1) { //有了参考值才计算 幸运 或者倒霉玩家 playerScoreRecord.Score += (int)score.Value; playerScoreRecord.NextFightLuck = 0; playerScoreRecord.NextFightBadLuck = 0; float multConfigValue = Math.Abs((float)playerScoreRecord.Score / record.AverageLoseMax); if (playerScoreRecord.Score < 0 && multConfigValue > _loseMultConfig) { //给一个幸运的概率 playerScoreRecord.NextFightLuck = 100; playerScoreRecord.Score = 0; Debug.Info( $"触发幸运玩家:{playerScoreRecord.UserId} {playerScoreRecord.Score} {multConfigValue}:{_loseMultConfig}"); } if (playerScoreRecord.Score > 0 && multConfigValue > 2 * _loseMultConfig) { playerScoreRecord.NextFightBadLuck = 100; playerScoreRecord.Score = 0; Debug.Info( $"触发倒霉玩家:{playerScoreRecord.UserId} {playerScoreRecord.Score} {multConfigValue}:{_loseMultConfig}"); } } PlayerScoreUpdate?.Invoke(record, playerScoreRecord); AddSessionFightScore(beiLv,fightCnt,playerScoreRecord.UserId, (int)score.Value,Math.Abs(score.Value - winMaxScore) < 0.01f); } record.TotalLoseMax += loseMaxScore; record.TotalWinMax += winMaxScore; if (loseMaxScore < record.LoseMax) { record.LoseMax = loseMaxScore; } if (winMaxScore > record.WinMax) { record.WinMax = winMaxScore; } record.FightCnt++; //第一次 只算一半的局数,先把值算出来 if ((record.AverageLoseMax > -1 && record.FightCnt >= StaticsCnt / 2) || record.FightCnt >= StaticsCnt) { record.AverageLoseMax = (int)(record.TotalLoseMax / record.FightCnt); record.MaxLoseScore = (int)(record.AverageLoseMax / beiLv); record.FightCnt = 0; record.TotalLoseMax = 0; record.TotalWinMax = 0; } record.LastEditorTime = TimeInfo.Instance.FrameTime; WayRecordUpdate?.Invoke(record); Debug.Info($"总局统计: {record.ToString()}"); } public string GetPlayerInfo(string wayId, int userid) { if (_wayGamePlayerScoreRecords.TryGetValue(wayId, out GamePlayerScoreRecord record)) { if (record.PlayerScoreRecords.TryGetValue(userid, out PlayerScoreRecord playerScoreRecord)) { return playerScoreRecord.ToString(); } } return "none"; } public void Update() { if (!isEnable) { return; } // 移除不使用的数据 if (_wayGamePlayerWayIds.Count > 0) { try { if (_wayGamePlayerWayIds.TryDequeue(out string wayid)) { bool isRemove = true; if (_wayGamePlayerScoreRecords.TryGetValue(wayid, out GamePlayerScoreRecord record)) { if (TimeInfo.Instance.FrameTime - record.LastEditorTime > ExpireTime) { if (!_wayGamePlayerScoreRecords.TryRemove(wayid, out record)) { isRemove = false; } Debug.Info($"移除 - {wayid}"); } else { // 计算玩家许久未使用的值 if (record.PlayerUserIds.Count > 0) { if (record.PlayerUserIds.TryDequeue(out int userid)) { bool isDel = true; if (record.PlayerScoreRecords.TryGetValue(userid, out PlayerScoreRecord playerScoreRecord)) { if (TimeInfo.Instance.FrameTime - playerScoreRecord.LastEditorTime > ExpireTime) { Debug.Info($"过期移除:{userid}"); if (!record.PlayerScoreRecords.TryRemove(userid, out playerScoreRecord)) { isDel = false; } } else { isDel = false; } } if (!isDel) { record.PlayerUserIds.Enqueue(userid); } } } isRemove = false; } } if (!isRemove) { _wayGamePlayerWayIds.Enqueue(wayid); } } } catch (Exception e) { Debug.Error($"LuckPlayerManager Update Error:{e.Message}"); } } } /// /// 朋友房幸运玩家积分记录 /// public class FriendLuckRecord { public List ScoreRecords = new List(); } /// /// 玩家积分记录 /// public class GamePlayerScoreRecord { /// /// 玩法ID /// public string WayId; /// /// 玩法局数 /// public int WayFightCnt; /// /// 每局赢的最大值总和 /// public long TotalWinMax; /// /// 记录赢的最大值 /// public long WinMax; /// /// 每局输的最大值总和 /// public long TotalLoseMax; /// /// 记录输的最大值 /// public long LoseMax; /// /// 场次 /// public int FightCnt; /// /// 最近一百次的输的平均 /// public int AverageLoseMax; /// /// 倍率 /// public float BeiLv; /// /// AverageLoseMax / BeiLv /// public int MaxLoseScore; /// /// 玩家积分记录 - 用来序列化 /// public List Records = new List(); /// /// 上次修改的时间 /// public long LastEditorTime; /// /// 玩家的Userids /// [JsonIgnore] public ConcurrentQueue PlayerUserIds = new ConcurrentQueue(); /// /// 玩家的积分记录 - 用来计算 /// [JsonIgnore] public ConcurrentDictionary PlayerScoreRecords = null; public override string ToString() { return $"wayid:{WayId}, AverageLoseMax:{AverageLoseMax}, fightcnt:{FightCnt}, winmax:{WinMax}, losemax:{LoseMax}, totalwinmax:{TotalWinMax}, totallosemax:{TotalLoseMax}, lasteditortime:{LastEditorTime}"; } } /// /// 玩家积分记录 /// public class PlayerScoreRecord { /// /// 玩家ID /// public int UserId; /// /// 距上次发牌累计的积分 /// public int Score; /// /// 连续赢的次数 /// public int ContinuousWinCnt; /// /// 连续输的次数 /// public int ContinuousLoseCnt; /// /// 一段时间的分 /// public int DayScore; /// /// 平衡 场次 /// public int PingHengFightCnt; /// /// 获得的总分 /// public int TotalScore; /// /// 赢的局数 /// public int WinFightCnt; /// /// 总战斗次数 /// public int TotalFightCnt; /// /// 上次修改的时间 /// public long LastEditorTime; /// /// 新手保护的局数 /// public int NewPlayerCnt; /// /// 下一个大局 有一个幸运的概率 /// [JsonIgnore] public int NextFightLuck; /// /// 下一个大局 有一个倒霉的概率 /// [JsonIgnore] public int NextFightBadLuck; /// /// 连续赢 输的权重 /// [JsonIgnore] public int ContinuousWin_BadWeight; /// /// 连续输 赢的权重 /// [JsonIgnore] public int ContinuosLose_LuckWeight; public override string ToString() { return $"userid:{UserId}, score:{Score}, TotalScore:{TotalScore}, ContinuousWinCnt:{ContinuousWinCnt},ContinuousLoseCnt:{ContinuousLoseCnt},LastEditorTime:{LastEditorTime},DayScore:{DayScore},WinFightCnt:{WinFightCnt},TotalFightCnt:{TotalFightCnt}"; } } public struct FightScore { /// /// 玩家ID /// public int UserId; /// /// 玩家的分数 /// public double Value; } } }