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;
}
}
}