CB-01: Program.cs 加 --nhn 模式 [NEURAL,ISMCTS,NEURAL] - 3人跑得快 N插花坐排列 - Rotations 数组补全为4种排列 (IIN/NII/INI/NHN) - --nhn 参数: 不生成CSV, 直接评估 CB-02: 胜率解析统一总分法 - TrainingCollector 加 ScoreAccumulator/BotWins/BotGames 静态累加器 - GameOver 无条件调用 AccumulateScore (eval + 训练模式均记录) - stress_summary.txt 输出 #BOT_SCORES: bot=wins/games,wr=XX%,score=YYY - orchestrator eval 解析改用 #BOT_SCORES 格式, 正则回退逻辑移除 管线: orchestrator eval→stress_summary→总分法解析, 闭环可验证
906 lines
36 KiB
C#
906 lines
36 KiB
C#
using GameData;
|
||
using MrWu.Debug;
|
||
using PdkFriendServer.GlobalManager;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using GameMessage.PaoDeKuaiF;
|
||
using GameFix.PaoDeKuaiF;
|
||
using GameMessage;
|
||
using Server.Data;
|
||
|
||
namespace PdkFriendServer.Logic
|
||
{
|
||
/// <summary>
|
||
/// 跑得快朋友房主逻辑
|
||
/// </summary>
|
||
public partial class PdkGameMain
|
||
{
|
||
/// <summary>
|
||
/// 游戏主包
|
||
/// </summary>
|
||
public PdkGamePack GamePack;
|
||
|
||
/// <summary>
|
||
/// 服务器本地内存
|
||
/// </summary>
|
||
public LogicMemory logicMemory;
|
||
|
||
/// <summary>
|
||
/// 规则
|
||
/// </summary>
|
||
public PdkRule Rule;
|
||
|
||
/// <summary>
|
||
/// 桌子对象
|
||
/// </summary>
|
||
public PdkGameDo DeskGameDo = null;
|
||
|
||
/// <summary>
|
||
/// 机器人玩家 [pos0,pos1,pos2],null=人工
|
||
/// </summary>
|
||
private IPdkBot[] _bots = null;
|
||
|
||
/// <summary>
|
||
/// 启用 Bot 自动对局
|
||
/// </summary>
|
||
public bool BotMode { get; set; }
|
||
|
||
public PdkGameMain(PdkGameDo _gamedo)
|
||
{
|
||
this.DeskGameDo = _gamedo;
|
||
this.GamePack = new PdkGamePack();
|
||
}
|
||
|
||
/// <summary>懒初始化3个bot(BotMode==true时首次WaitWanJiaShuRu调用)
|
||
/// BotTypes: "ISMCTS" / "NEURAL" / "NEURAL_V5" / null=默认ISMCTS
|
||
/// 训练模式: ["NEURAL","NEURAL","ISMCTS"] = 2个模型bot + 1个ISMCTS</summary>
|
||
public static string[] BotTypes = null;
|
||
private static int _flushGameCounter = 0;
|
||
|
||
private void InitBots()
|
||
{
|
||
_bots = new IPdkBot[3];
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
string type = BotTypes != null && BotTypes.Length > i ? BotTypes[i] : null;
|
||
if (type == "NEURAL")
|
||
_bots[i] = new NeuralBot("model.onnx", 0.1f);
|
||
else if (type == "NEURAL_V5")
|
||
_bots[i] = new NeuralBotV5("model_v5.onnx", 0.05f);
|
||
else
|
||
_bots[i] = new IsmctsBot();
|
||
}
|
||
}
|
||
|
||
/// <summary>构建当前玩家的可见视图——只看自己手牌+公开信息</summary>
|
||
private PdkBotView BuildBotView(int pos)
|
||
{
|
||
return new PdkBotView
|
||
{
|
||
MyHand = GamePack.CardPack.Cards[pos - 1].CardInfos
|
||
.Where(x => x.GameState == 1).ToArray(),
|
||
MaxPlayCard = GamePack.Info.MaxPlayCard,
|
||
WhoPlay = pos,
|
||
MyPos = pos,
|
||
ShenYuCard = GamePack.Info.ShenYuCard,
|
||
ZhaDans = GamePack.Info.ZhaDans,
|
||
Rule = this.Rule,
|
||
GameZT = GamePack.Info.GameZT
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 游戏开始
|
||
/// </summary>
|
||
public void GameStart()
|
||
{
|
||
byte shangyou = 0; //上游位置,从1开始
|
||
byte overJu = (byte)(this.DeskGameDo == null ? 0 : DeskGameDo.GetDeskInt(8)); //打完了几局,0就是一局都没有打完,也就是首局
|
||
shangyou = GamePack.Info.OverPos;
|
||
var tg = GamePack.Info.PlayTuoGauan;
|
||
GamePack = new PdkGamePack();
|
||
if (this.DeskGameDo != null)
|
||
{
|
||
SetFriendGz();
|
||
GamePack.Init(GamePack.Info.PlayNum);
|
||
}
|
||
else
|
||
{ //测试的规则
|
||
GamePack.Init(3);
|
||
GamePack.Info.PlayNum = 3;
|
||
//设置玩法规则
|
||
Rule = new PdkRule();
|
||
Rule.GuanPai = true; //经典玩法
|
||
Rule.BiYa = true;//不必压
|
||
Rule.ShouJu3 = true;
|
||
Rule.SiDai3 = true;
|
||
Rule.BaoZhuang = true;
|
||
Rule.ZhaDanDeFen = true;
|
||
}
|
||
LoadInit();
|
||
if (overJu == 0)
|
||
{
|
||
logicMemory.BombCount = 0;
|
||
}
|
||
logicMemory.WarCount = overJu;
|
||
logicMemory.ShangYou = shangyou;
|
||
if (overJu != 0)
|
||
{
|
||
GamePack.Info.PlayTuoGauan = tg;
|
||
}
|
||
GamePack.Info.GameZT = 2;
|
||
Log(Rule.ToString());
|
||
//this.GameFaPai(); //屏蔽掉,让dotime驱动流程玩下周
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置规则自动准备时间
|
||
/// </summary>
|
||
void SetAutoReadTime()
|
||
{
|
||
if (this.Rule.TuoGuanNum <= 0) return;
|
||
if (this.DeskGameDo == null) return;
|
||
if (this.DeskGameDo.desk == null) return;
|
||
if (this.DeskGameDo.desk.deskinfo == null) return;
|
||
if (this.DeskGameDo.desk.deskinfo.rule == null) return;
|
||
if (this.DeskGameDo.desk.deskinfo.rule.RuleEx == null) return;
|
||
// if (!this.DeskGameDo.desk.deskinfo.rule.RuleEx.DepositAutoDissolve) return;
|
||
if (this.DeskGameDo.desk.deskinfo.rule.RuleEx.AutoDeposit > 0) return;
|
||
this.DeskGameDo.desk.deskinfo.rule.RuleEx.AutoDeposit = this.Rule.TuoGuanNum * 60;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计时器 1秒钟执行一次
|
||
/// </summary>
|
||
public void DoTime()
|
||
{
|
||
SetAutoReadTime();
|
||
switch (GamePack.Info.GameZT)
|
||
{
|
||
//case 1:
|
||
// GameStart();
|
||
// break;
|
||
case 2:
|
||
GameFaPai();
|
||
break;
|
||
case 3:
|
||
BaoZhuangTuoGuan();
|
||
break;
|
||
case 4:
|
||
//if (Rule.TuoGuanNum > 0)
|
||
//{//需要托管
|
||
DaPaiTuoGuan();
|
||
//}
|
||
//玩家先手,并且是最后一张牌,就自动帮玩家出牌
|
||
if (GamePack.Info.MaxPlayCard.GameNum <= 0)
|
||
{
|
||
var cards = GamePack.CardPack.Cards[GamePack.Info.WhoPlay - 1].CardInfos.Where(x => x.GameState == 1).ToArray();
|
||
var b = PdkCardAlgorithm.GetOutCard(cards, cards, Rule, out var payCard);
|
||
var zhad = PdkCardAlgorithm.IsHaveBomb(cards, Rule.AAAIsZhaDan);
|
||
if (b && payCard.Ids.Length == cards.Length && payCard.Type != CardType1.SiDai2 && payCard.Type != CardType1.SiDai3 && !zhad)
|
||
{
|
||
payCard.Pos = (byte)GamePack.Info.WhoPlay;
|
||
GamePack.Info.ActivePlayCard = payCard;
|
||
Log($"最后一张牌,就自动帮玩家出牌 who:{GamePack.Info.WhoPlay}先出 ,托管出的牌:{payCard}");
|
||
GamePack.Info.ActivePlayCard = payCard;
|
||
Debug.Info("最后一手GameDo");
|
||
GameDo();
|
||
}
|
||
}
|
||
break;
|
||
case 5:
|
||
if (this.TimeNum >= 2)
|
||
{
|
||
GameOver();
|
||
}
|
||
break;
|
||
default:
|
||
// Log("DoTime default !");
|
||
break;
|
||
}
|
||
TimeNum++;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 游戏发牌
|
||
/// </summary>
|
||
public void GameFaPai()
|
||
{
|
||
byte bc = 0; //计算这局的炸弹个数
|
||
for (int i = 0; i < 20; i++)
|
||
{
|
||
if (i > 0)
|
||
{
|
||
Log($"发牌i:{i} bnum:{bc} lc:{logicMemory.BombCount}", DebugLevel.ImportantLog);
|
||
}
|
||
InitCard();
|
||
bc = 0;
|
||
bool b = false;
|
||
for (int j = 0; j < GamePack.Info.PlayNum; j++)
|
||
{
|
||
byte bnum = CalcPlayBombRule(j);
|
||
Log($"玩家:{j},bombCount:{bnum}");
|
||
//if (bnum > 1) //单个人一手牌不能有超过2个炸弹
|
||
//{
|
||
// bc += (byte)bnum;
|
||
// break;
|
||
//}
|
||
if (bnum > 0)
|
||
{
|
||
bc += bnum;
|
||
}
|
||
}
|
||
if (logicMemory.BombCount + bc <= 1)
|
||
{
|
||
logicMemory.BombCount += bc;
|
||
b = true;
|
||
}
|
||
if (b)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
CheckFeiJi();
|
||
if (Rule.HongTao10ZhaNiao)
|
||
{ //红桃十扎鸟
|
||
GamePack.Info.HongTaoShiPos = GetHongTaoShiPos();
|
||
}
|
||
if (Rule.MeiJu3)
|
||
{ //每局黑桃3出
|
||
GamePack.Info.FistPlay = GetHeiTao3Pos();
|
||
Log($"第一个出牌位置:{GamePack.Info.FistPlay}");
|
||
}
|
||
else if (Rule.ShouJu3)
|
||
{ //首局黑桃三先出,下局赢家出
|
||
GamePack.Info.FistPlay = logicMemory.WarCount == 0 ? GetHeiTao3Pos() : logicMemory.ShangYou;
|
||
Log($"第一个出牌位置:{GamePack.Info.FistPlay},开始次数:{logicMemory.WarCount},上游玩家:{logicMemory.ShangYou}");
|
||
}
|
||
GamePack.Info.WhoPlay = GamePack.Info.FistPlay;
|
||
GamePack.Info.GameZT = (byte)(Rule.BaoZhuang ? 3 : 4);
|
||
TimeNum = 0;
|
||
SendPack();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 包庄流程
|
||
/// </summary>
|
||
public void GameBaoZhuang()
|
||
{
|
||
int bcount = 0;//包庄的人数
|
||
byte[] pos = new byte[GamePack.Info.PlayNum]; //包庄人的位置
|
||
bool zhuangB = false;
|
||
if (GamePack.Info.BaoZhuang[GamePack.Info.FistPlay - 1] == 1)
|
||
{ //庄家包庄了,直接进入下一流程
|
||
GamePack.Info.WhoPlay = GamePack.Info.FistPlay;
|
||
GamePack.Info.BaoPos = GamePack.Info.WhoPlay;
|
||
GamePack.Info.GameZT = 4; //进入打牌
|
||
SetFalseOperation();
|
||
SendPack();
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
for (byte i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{
|
||
if (GamePack.Info.BaoZhuang[i] == 0)
|
||
{
|
||
GamePack.Info.WhoPlay = (byte)(i + 1);
|
||
SendPack();
|
||
return; //还有玩家没有操作,不处理流程
|
||
}
|
||
if (GamePack.Info.BaoZhuang[i] == 1)
|
||
{
|
||
pos[bcount] = (byte)(i + 1);
|
||
if (!zhuangB && pos[bcount] == GamePack.Info.FistPlay)
|
||
{
|
||
zhuangB = true;
|
||
}
|
||
bcount++;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (bcount == 1)
|
||
{
|
||
GamePack.Info.WhoPlay = pos[0];
|
||
GamePack.Info.BaoPos = GamePack.Info.WhoPlay;
|
||
}
|
||
else if (bcount > 1)
|
||
{ //多个人包庄
|
||
if (zhuangB)
|
||
{//如果庄家包庄了,那么就是庄家为包庄
|
||
GamePack.Info.WhoPlay = GamePack.Info.FistPlay;
|
||
}
|
||
else
|
||
{ //如果没有包庄的话,那么靠近庄家的下一家为包庄玩家
|
||
byte npos = GetNextPos(GamePack.Info.FistPlay); //庄的下家
|
||
for (int j = 0; j < pos.Length; j++)
|
||
{
|
||
bool b = false;
|
||
for (byte i = 0; i < pos.Length; i++)
|
||
{
|
||
if (pos[i] == npos)
|
||
{
|
||
GamePack.Info.WhoPlay = pos[i];
|
||
b = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!b)
|
||
{
|
||
npos = GetNextPos(npos);
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
}
|
||
GamePack.Info.BaoPos = GamePack.Info.WhoPlay;
|
||
}
|
||
Log($"进入打牌,play:{GamePack.Info.WhoPlay} ,包庄玩家的位置:{GamePack.Info.BaoPos}");
|
||
GamePack.Info.GameZT = 4; //进入打牌
|
||
SetFalseOperation();
|
||
SendPack();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打牌流程
|
||
/// </summary>
|
||
void GameDaPai()
|
||
{
|
||
Log("进入打牌流程,玩家出牌内容是:" + GamePack.Info.ActivePlayCard.ToString());
|
||
//如果玩家有出牌:1修改牌内存,2修改最大出牌信息
|
||
//记录炸弹计分
|
||
//判断一轮打完
|
||
//判断一局游戏打完
|
||
//转换控制权 一个玩家出完牌后
|
||
byte chupaiPos = 0;
|
||
if (GamePack.Info.ActivePlayCard.Type != CardType1.None)
|
||
{
|
||
chupaiPos = GamePack.Info.ActivePlayCard.Pos;
|
||
SetCardToOut(GamePack.Info.ActivePlayCard.Pos, GamePack.Info.ActivePlayCard.Ids);
|
||
if (Rule.ZhaDanDeFen && GamePack.Info.ActivePlayCard.Type == CardType1.ZhaDan)
|
||
{
|
||
//炸弹得分,并且出的是炸弹
|
||
/*炸弹+10:打出炸弹+10分,不勾选0分
|
||
在有大必压得玩法下,一轮中打出的炸弹只算当局最大的炸弹分,
|
||
不必压得玩法下,只要打出炸弹都算分*/
|
||
AddZhaDanCount(GamePack.Info.ActivePlayCard.Pos, 1);
|
||
// GamePack.Info.ZhaDans[GamePack.Info.ActivePlayCard.Pos - 1]++;
|
||
if (Rule.BiYa && GamePack.Info.MaxPlayCard.Type == CardType1.ZhaDan)
|
||
{
|
||
//把之前加的炸弹减回去。
|
||
//GamePack.Info.ZhaDans[GamePack.Info.MaxPlayCard.Pos - 1]--;
|
||
AddZhaDanCount(GamePack.Info.MaxPlayCard.Pos, -1);
|
||
}
|
||
}
|
||
GamePack.Info.MaxPlayCard = GamePack.Info.ActivePlayCard;
|
||
}
|
||
if (IsGameOver(chupaiPos))
|
||
{ //游戏是否结束
|
||
SendPack();
|
||
this.TimeNum = 0;
|
||
GamePack.Info.GameZT = 5;
|
||
// GameOver(); 修改为定时器驱动
|
||
return;
|
||
}
|
||
GamePack.Info.WhoPlay = GetNextPos(GamePack.Info.WhoPlay);
|
||
Debug.Info($"当前玩家:{GamePack.Info.WhoPlay},下一个出牌的人:{GamePack.Info.WhoPlay}");
|
||
if (GamePack.Info.WhoPlay == GamePack.Info.MaxPlayCard.Pos)
|
||
{ //一轮结束
|
||
Log("一轮结束");
|
||
GamePack.Info.MaxPlayCard.Pos = 0;
|
||
GamePack.Info.MaxPlayCard.GameNum = 0;
|
||
GamePack.Info.MaxPlayCard.Type = CardType1.None;
|
||
GamePack.Info.MaxPlayCard.Ids = null;
|
||
}
|
||
SendPack();
|
||
}
|
||
|
||
void GameOver()
|
||
{
|
||
Log($"进入结算 bao:{GamePack.Info.BaoPos},overpos:{GamePack.Info.OverPos}");
|
||
int count = 0;
|
||
if (Rule.JingDianWanFa || Rule.GuanPai)
|
||
{
|
||
count = 16;
|
||
}
|
||
if (Rule.Zhang15)
|
||
{
|
||
count = 15;
|
||
}
|
||
|
||
if (GamePack.Info.BaoPos > 0)
|
||
{ //有人包庄
|
||
var baoWin = GamePack.Info.BaoPos == GamePack.Info.OverPos;
|
||
Log($"baowin:{baoWin}");
|
||
for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{
|
||
if (i + 1 != GamePack.Info.BaoPos)
|
||
{ //先统计闲家
|
||
int bei = GamePack.Info.HongTaoShiPos == i + 1 || GamePack.Info.HongTaoShiPos == GamePack.Info.BaoPos ? 4 : 2; //红桃十扎鸟
|
||
GamePack.Info.Score[i] += (baoWin ? 0 - count * bei : count * bei);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{//先统计闲家
|
||
if (i + 1 != GamePack.Info.OverPos)
|
||
{
|
||
if (GamePack.Info.ShenYuCard[i] == 1) continue;
|
||
var bei = GamePack.Info.HongTaoShiPos == i + 1 || GamePack.Info.HongTaoShiPos == GamePack.Info.OverPos ? 2 : 1;//红桃十扎鸟
|
||
var num = GamePack.Info.ShenYuCard[i] == count ? count * 2 : GamePack.Info.ShenYuCard[i];
|
||
GamePack.Info.Score[i] += 0 - num * bei;
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{
|
||
if (i + 1 == (GamePack.Info.BaoPos > 0 ? GamePack.Info.BaoPos : GamePack.Info.OverPos))
|
||
{ //统计庄家,把闲家的都加起来就是庄家的输赢
|
||
for (int j = 0; j < GamePack.Info.PlayNum; j++)
|
||
{
|
||
if (j == i) continue;
|
||
GamePack.Info.Score[i] -= GamePack.Info.Score[j];
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{
|
||
if (GamePack.Info.ZhaDans[i] != 0)
|
||
{
|
||
GamePack.Info.Score[i] += (GamePack.Info.ZhaDans[i] * 10);
|
||
}
|
||
}
|
||
//处理托管罚分
|
||
var fafen = GetTuoGuanFaFen();
|
||
if (fafen > 0)
|
||
{
|
||
var tgPos = GetTuoGuanPlay();
|
||
if (tgPos >= 0)
|
||
{ //有玩家托管
|
||
//int fen = fafen / (GamePack.Info.PlayNum - 1);
|
||
for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{
|
||
if (i == tgPos)
|
||
{
|
||
GamePack.Info.Score[i] -= (fafen * (GamePack.Info.PlayNum - 1));
|
||
}
|
||
else
|
||
{
|
||
GamePack.Info.Score[i] += fafen;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#if DEBUG
|
||
for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{
|
||
Log($"玩家:{(i + 1)} ,输赢:{GamePack.Info.Score[i]}");
|
||
}
|
||
#endif
|
||
|
||
// 训练数据收集:输出本局所有 ISMCTS 决策
|
||
{
|
||
float[] rewards = new float[GamePack.Info.PlayNum];
|
||
for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{
|
||
// 赢的分 > 0 → reward=1; 输的分 < 0 → reward=-1; 否则 0
|
||
float s = GamePack.Info.Score[i];
|
||
rewards[i] = s > 0 ? 1f : s < 0 ? -1f : 0f;
|
||
}
|
||
var rawScores = GamePack.Info.Score.Select(s => (float)s).ToArray();
|
||
TrainingCollector.AccumulateScore(BotTypes, rawScores);
|
||
TrainingCollector.FlushGame(_flushGameCounter++, rewards, GamePack.Info.OverPos, BotTypes);
|
||
}
|
||
|
||
if (DeskGameDo != null)
|
||
{
|
||
DeskGameDo.GameOver();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否托管罚分
|
||
/// </summary>
|
||
/// <returns>0不罚分 </returns>
|
||
int GetTuoGuanFaFen()
|
||
{
|
||
if (this.DeskGameDo == null || this.DeskGameDo.desk == null || this.DeskGameDo.desk.deskinfo == null || this.DeskGameDo.desk.deskinfo.rule == null ||
|
||
this.DeskGameDo.desk.deskinfo.rule.RuleEx == null) return 0;
|
||
return this.DeskGameDo.desk.deskinfo.rule.RuleEx.DepositAutoDissolve ? this.DeskGameDo.desk.deskinfo.rule.RuleEx.OutLinePenaltyPoint : 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 找打托管的玩家
|
||
/// </summary>
|
||
/// <returns>从0开始 -1未找到</returns>
|
||
int GetTuoGuanPlay()
|
||
{
|
||
return DeskGameDo.GetPlayerFirstDepositSeat();
|
||
//var desk = DeskGameDo.GetDesk();
|
||
//for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
//{
|
||
// if (desk[i] != null && desk[i].isDePosit)
|
||
// {//找到在 托管的玩家
|
||
// return i;
|
||
// }
|
||
//}
|
||
//return -1;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 游戏发包,发全包
|
||
/// </summary>
|
||
void SendPack()
|
||
{
|
||
Log($"whoPlay:{GamePack.Info.WhoPlay}");
|
||
if (DeskGameDo == null)
|
||
{
|
||
var str = JsonPack.GetJson(this.GamePack);
|
||
var dir = "./gamepack";
|
||
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
||
File.WriteAllText(Path.Combine(dir, "gamepack.txt"), str);
|
||
var path = Path.Combine(dir, $"gamepack{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}.txt");
|
||
Log("保存文件:" + path);
|
||
File.WriteAllText(path, str);
|
||
WaitWanJiaShuRu(); //等玩家输入。模拟玩家打牌
|
||
}
|
||
else
|
||
{
|
||
SetOperation();
|
||
DeskGameDo.SendGamePack();
|
||
}
|
||
}
|
||
|
||
void SetFalseOperation()
|
||
{
|
||
if (DeskGameDo == null) return;
|
||
for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{
|
||
this.SetWaitOperation(i, false);
|
||
}
|
||
}
|
||
|
||
public void SetOperation()
|
||
{
|
||
for (int i = 0; i < GamePack.Info.PlayNum; i++)
|
||
{
|
||
|
||
var pl = this.DeskGameDo.desk[i];
|
||
if (pl != null)
|
||
{
|
||
GamePack.Info.PlayTuoGauan[i] = (byte)(pl.isDePosit ? 1 : 0);
|
||
}
|
||
bool b = false; //包庄流程是同时进行的
|
||
if (GamePack.Info.GameZT == 3)
|
||
{
|
||
b = GamePack.Info.BaoZhuang[i] == 0;
|
||
}
|
||
this.SetWaitOperation(i, GamePack.Info.GameZT == 3 ? b : i + 1 == GamePack.Info.WhoPlay);
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置玩家的操作权
|
||
/// </summary>
|
||
/// <param name="seat">下标从0开始</param>
|
||
/// <param name="waitOperation"></param>
|
||
public void SetWaitOperation(int seat, bool waitOperation)
|
||
{
|
||
var desk = this.DeskGameDo.GetDesk();
|
||
if (seat < 0 || seat >= desk.PlayerCnt)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var pl = desk[seat];
|
||
if (pl != null)
|
||
{
|
||
//之前不能操作,现在可以操作,重置托管时间
|
||
if (!pl.WaitOperation && waitOperation)
|
||
{
|
||
pl.AutoDepositTime = 0;
|
||
}
|
||
pl.WaitOperation = waitOperation;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查包内容是否合法
|
||
/// </summary>
|
||
/// <param name="pack"></param>
|
||
/// <returns></returns>
|
||
public bool CheckPack(PdkLogicInfo pack)
|
||
{
|
||
if (pack.WhoPlay != GamePack.Info.WhoPlay && GamePack.Info.GameZT > 3) return false;
|
||
switch (GamePack.Info.GameZT)
|
||
{
|
||
case 3: //包庄 只能输入1和2
|
||
return pack.BaoZhuang[pack.WhoPlay - 1] == 1 || pack.BaoZhuang[pack.WhoPlay - 1] == 2;
|
||
case 4:
|
||
if (pack.WhoPlay != pack.ActivePlayCard.Pos)
|
||
{
|
||
Debug.Error($"who:{pack.WhoPlay},apos:{pack.ActivePlayCard}");
|
||
return false;
|
||
}
|
||
return CheckChuPai(pack.ActivePlayCard);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public void GameDo()
|
||
{
|
||
switch (GamePack.Info.GameZT)
|
||
{
|
||
//case 1:
|
||
// GameStart();
|
||
// break;
|
||
case 2:
|
||
GameFaPai();
|
||
break;
|
||
case 3:
|
||
GameBaoZhuang();
|
||
break;
|
||
case 4:
|
||
GameDaPai();
|
||
break;
|
||
case 5:
|
||
GameOver();
|
||
break;
|
||
default:
|
||
Log("default GameDo game over!");
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 等待玩家输入
|
||
/// </summary>
|
||
void WaitWanJiaShuRu()
|
||
{
|
||
if (DeskGameDo != null) return;
|
||
int pos = GamePack.Info.WhoPlay;
|
||
switch (GamePack.Info.GameZT)
|
||
{
|
||
case 3://包庄流程
|
||
{
|
||
// Bot 接管:懒初始化
|
||
if (BotMode && _bots == null)
|
||
InitBots();
|
||
if (_bots != null && _bots[pos - 1] != null)
|
||
{
|
||
var view = BuildBotView(pos);
|
||
var bao = _bots[pos - 1].DecideBaoZhuang(view);
|
||
GamePack.Info.BaoZhuang[pos - 1] = bao ? (byte)1 : (byte)2;
|
||
Log($"bot-包庄: pos{pos}={(bao ? "包" : "不包")}");
|
||
return;
|
||
}
|
||
// 人工输入
|
||
Log("包庄操作 pos:" + pos);
|
||
while (true)
|
||
{
|
||
string str = Console.ReadLine();
|
||
if (string.IsNullOrWhiteSpace(str)) continue;
|
||
Log(str);
|
||
var ss = str.Split(',');
|
||
if (ss.Length < 2) continue;
|
||
byte.TryParse(ss[0], out var ipos);
|
||
byte.TryParse(ss[1], out byte num);
|
||
GamePack.Info.WhoPlay = ipos;
|
||
GamePack.Info.BaoZhuang[ipos - 1] = num;
|
||
if (CheckPack(GamePack.Info)) break;
|
||
Log("检测不合格,输入非法值");
|
||
}
|
||
break;
|
||
}
|
||
case 4: //打牌流程
|
||
{
|
||
// Bot 接管:懒初始化
|
||
if (BotMode && _bots == null)
|
||
InitBots();
|
||
if (_bots != null && _bots[pos - 1] != null)
|
||
{
|
||
var view = BuildBotView(pos);
|
||
var botPlay = _bots[pos - 1].DecidePlay(view);
|
||
// 非pass(有Ids)则运行 GetOutCard 获取正确牌型,pass(Type=None且Ids为空)直接设
|
||
if (botPlay.Ids != null && botPlay.Ids.Length > 0)
|
||
{
|
||
var hand = view.MyHand;
|
||
var selectCards = hand.Where(c => botPlay.Ids.Contains(c.ID)).ToArray();
|
||
if (selectCards.Length > 0)
|
||
{
|
||
PdkCardAlgorithm.GetOutCard(selectCards, hand, Rule, out var payCard);
|
||
payCard.Pos = (byte)pos;
|
||
GamePack.Info.ActivePlayCard = payCard;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
GamePack.Info.ActivePlayCard = botPlay;
|
||
}
|
||
return;
|
||
}
|
||
// 人工输入
|
||
Log("打牌操作 pos:" + pos);
|
||
while (true)
|
||
{
|
||
string str = Console.ReadLine();
|
||
if (string.IsNullOrWhiteSpace(str)) continue;
|
||
var ss = str.Split(',');
|
||
if (ss.Length <= 0) continue;
|
||
Log(str);
|
||
var selectCards = GamePack.CardPack.Cards[pos - 1].CardInfos
|
||
.Where(x => ss.Any(s => s == x.ID.ToString())).ToArray();
|
||
PlayOutCardPdkF payCard;
|
||
if (selectCards == null || selectCards.Length <= 0)
|
||
{
|
||
payCard = new PlayOutCardPdkF { Pos = (byte)pos, Type = CardType1.None };
|
||
GamePack.Info.ActivePlayCard = payCard;
|
||
if (CheckPack(GamePack.Info)) break;
|
||
Log("检测不合格,输入非法值");
|
||
}
|
||
else if (PdkCardAlgorithm.GetOutCard(selectCards,
|
||
GamePack.CardPack.Cards[pos - 1].CardInfos.Where(x => x.GameState == 1).ToArray(),
|
||
this.Rule, out payCard))
|
||
{
|
||
payCard.Pos = (byte)pos;
|
||
GamePack.Info.ActivePlayCard = payCard;
|
||
if (CheckPack(GamePack.Info)) break;
|
||
Log("检测不合格,输入非法值");
|
||
}
|
||
else
|
||
{
|
||
Log("输入的牌不符合规则,不能出下去");
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void textxxxx()
|
||
{
|
||
|
||
// GameStart();
|
||
// GameFaPai();
|
||
//PlayOutCard output = new PlayOutCard();
|
||
//output.Pos = 1; output.Type = CardType.LianDui;
|
||
//output.GameNum = 9;
|
||
//output.Ids = new byte[6];
|
||
//TCardInfo[] shoupai = new TCardInfo[16];
|
||
//shoupai[0] = new TCardInfo() { ID = 1, GameNum = 14, Flower = 1, Num = 14, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[1] = new TCardInfo() { ID = 27, GameNum = 14, Flower = 3, Num = 14, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[2] = new TCardInfo() { ID = 26, GameNum = 13, Flower = 2, Num = 13, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[3] = new TCardInfo() { ID = 25, GameNum = 12, Flower = 2, Num = 12, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[4] = new TCardInfo() { ID = 38, GameNum = 12, Flower = 3, Num = 12, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[5] = new TCardInfo() { ID = 36, GameNum = 10, Flower = 3, Num = 10, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[6] = new TCardInfo() { ID = 49, GameNum = 10, Flower = 4, Num = 10, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[7] = new TCardInfo() { ID = 35, GameNum = 9, Flower = 3, Num = 9, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[8] = new TCardInfo() { ID = 48, GameNum = 9, Flower = 4, Num = 9, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[9] = new TCardInfo() { ID = 8, GameNum = 8, Flower = 1, Num = 8, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[10] = new TCardInfo() { ID = 34, GameNum = 8, Flower = 3, Num = 8, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[11] = new TCardInfo() { ID = 5, GameNum = 5, Flower = 1, Num = 5, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[12] = new TCardInfo() { ID = 18, GameNum = 5, Flower = 2, Num = 5, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[13] = new TCardInfo() { ID = 44, GameNum = 5, Flower = 4, Num = 5, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[14] = new TCardInfo() { ID = 16, GameNum = 3, Flower =2, Num = 3, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//shoupai[15] = new TCardInfo() { ID = 29, GameNum = 3, Flower = 3, Num = 3, Score = 0, GameOwer = 1, GameState = 1 };
|
||
//var tishi = PdkCardAlgorithm.GetTipCard(output, shoupai, null);
|
||
//Console.WriteLine(tishi);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试函数
|
||
/// </summary>
|
||
public void Test(string path = "")
|
||
{
|
||
//textxxxx();
|
||
//return;
|
||
if (File.Exists("output.txt"))
|
||
{
|
||
File.Delete("output.txt");
|
||
}
|
||
string testDir = !string.IsNullOrWhiteSpace(path)
|
||
? path
|
||
: "./test";
|
||
if (!Directory.Exists(testDir))
|
||
{
|
||
Directory.CreateDirectory(testDir);
|
||
}
|
||
foreach (string file in Directory.GetFiles(testDir))
|
||
{
|
||
File.Delete(file);
|
||
}
|
||
bool b = true;
|
||
GamePack.Info.GameZT = 1;
|
||
if (!string.IsNullOrWhiteSpace(path))
|
||
{
|
||
GamePack = JsonPack.GetData<PdkGamePack>(File.ReadAllText(path));
|
||
}
|
||
while (b)
|
||
{
|
||
switch (GamePack.Info.GameZT)
|
||
{
|
||
case 1:
|
||
GameStart();
|
||
break;
|
||
case 2:
|
||
GameFaPai();
|
||
break;
|
||
case 3:
|
||
GameBaoZhuang();
|
||
break;
|
||
case 4:
|
||
GameDaPai();
|
||
break;
|
||
case 5:
|
||
GameOver();
|
||
GamePack.Info.GameZT = 11; //进入结束就结束
|
||
break;
|
||
default:
|
||
b = false;
|
||
Log("test over!");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void WriteToConsoleAndFile(StreamWriter writer, string message)
|
||
{
|
||
Console.WriteLine(message); // 写入控制台
|
||
writer.WriteLine(message); // 写入文件
|
||
}
|
||
|
||
public void Log(string msg, DebugLevel leve = DebugLevel.Info)
|
||
{
|
||
if (DeskGameDo == null)
|
||
{
|
||
using (StreamWriter writer = new StreamWriter("output.txt", true)) // 第二个参数为true表示追加到文件
|
||
{
|
||
WriteToConsoleAndFile(writer, $"[{DateTime.Now.ToString("G")}]:" + msg);
|
||
}
|
||
Thread.Sleep(1);
|
||
}
|
||
else
|
||
{
|
||
switch (leve)
|
||
{
|
||
case DebugLevel.Info:
|
||
Debug.Info(msg);
|
||
break;
|
||
case DebugLevel.Warning:
|
||
Debug.Warning(msg);
|
||
break;
|
||
case DebugLevel.ImportantLog:
|
||
Debug.ImportantLog(msg);
|
||
break;
|
||
case DebugLevel.Error:
|
||
Debug.Error(msg);
|
||
break;
|
||
case DebugLevel.Fatal:
|
||
Debug.Fatal(msg);
|
||
break;
|
||
default:
|
||
Debug.Info(msg);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|