using System;
using System.Collections.Generic;
using UnityEngine;
///
/// 象棋对局总管理(普通类,由 ChineseChessMainPanel 持有并统一控制)
/// 负责:开局、回合、走棋校验与应用、将军/胜负判断、悔棋、网络同步数据封装
///
/// 联网方案:帧同步——走法与规则判断全在前端本地跑,
/// 走子数据通过 SyncGameData 透传给对方(后端不解析),后端只负责胜负结算
///
public class ChessGameManager
{
/// 棋盘数据管理
public ChessBoardManager board = new ChessBoardManager();
/// 规则管理
public ChessRulesManager rules = new ChessRulesManager();
/// 悔棋记录管理
public ChessMoveRecorder recorder = new ChessMoveRecorder();
/// 本地玩家阵营
public ChessCamp myCamp;
/// 当前轮到哪方走(红先黑后)
public ChessCamp currentTurn;
/// 已走的半回合数(同时作为帧序号)
public int moveCount;
/// 对局是否已结束
public bool isGameOver;
/// 胜方
public ChessCamp winnerCamp;
/// 走棋成功(本地或网络同步),参数含完整走子数据
public event Action OnMoveApplied;
/// 悔棋完成,参数为回退的步数
public event Action OnUndoApplied;
/// 对局结束(本地规则判定),参数为胜方
public event Action OnGameOver;
/// 回合切换
public event Action OnTurnChanged;
/// 现在是否轮到本地玩家
public bool IsMyTurn => !isGameOver && currentTurn == myCamp;
/// 开一局新对局
public void StartGame(ChessCamp myCamp)
{
this.myCamp = myCamp;
board.InitDefaultBoard();
recorder.Clear();
moveCount = 0;
isGameOver = false;
winnerCamp = ChessCamp.None;
currentTurn = ChessCamp.Red; // 红先
OnTurnChanged?.Invoke(currentTurn);
}
///
/// 本地玩家尝试走棋(完整校验:回合、归属、规则、送将),失败返回 false
///
public bool TryLocalMove(Vector2Int from, Vector2Int to)
{
if (isGameOver || currentTurn != myCamp) return false;
var piece = board.GetPiece(from);
if (piece == null || piece.camp != myCamp) return false;
if (!rules.GetLegalMoves(board, piece).Contains(to)) return false;
ApplyMoveInternal(piece, to, true);
return true;
}
///
/// 应用网络同步过来的走棋(对方走的)。带帧序号校验,重复/乱序帧直接丢弃
///
public bool ApplyRemoteMove(ChessMoveData move)
{
if (isGameOver || move == null) return false;
// 帧序号校验
if (move.seq < moveCount) return false; // 重复帧
if (move.seq > moveCount)
{
Debug.LogWarning($"[Chess] 丢帧:收到 seq={move.seq},本地 moveCount={moveCount}");
return false; // 乱序帧
}
var from = new Vector2Int(move.fromX, move.fromY);
var to = new Vector2Int(move.toX, move.toY);
var piece = board.GetPiece(from);
if (piece == null || piece.camp == myCamp || piece.camp != currentTurn) return false;
if (!rules.GetLegalMoves(board, piece).Contains(to)) return false;
ApplyMoveInternal(piece, to, false);
return true;
}
/// 应用一步棋(数据层),并触发相关事件
private void ApplyMoveInternal(ChessPieceData piece, Vector2Int to, bool isLocal)
{
Vector2Int from = piece.pos;
ChessPieceData captured = board.ApplyMove(piece, to);
moveCount++;
recorder.Push(new ChessMoveRecord { piece = piece, from = from, to = to, captured = captured });
ChessCamp moverCamp = piece.camp;
currentTurn = moverCamp == ChessCamp.Red ? ChessCamp.Black : ChessCamp.Red;
// 胜负与将军判断(纯前端本地判定)
bool check = false;
bool gameOver = false;
if (captured != null && captured.pieceType == ChessPieceType.Jiang)
{
gameOver = true; // 将/帅被吃
}
else
{
check = rules.IsInCheck(board, currentTurn);
if (!rules.HasAnyLegalMove(board, currentTurn))
{
gameOver = true; // 对方无任何合法走法 → 将死或困毙
}
}
var args = new ChessMoveEventArgs
{
move = new ChessMoveData { seq = moveCount - 1, fromX = from.x, fromY = from.y, toX = to.x, toY = to.y },
movePieceId = piece.id,
capturedPieceId = captured != null ? captured.id : -1,
isLocal = isLocal,
isCheck = check,
moverCamp = moverCamp
};
OnMoveApplied?.Invoke(args);
if (gameOver) SetGameOver(moverCamp);
else OnTurnChanged?.Invoke(currentTurn);
}
///
/// 悔棋:回退最近一步(一个半回合,双方确认后各调一次,两端回退同一手)
///
public bool UndoOneStep()
{
var r = recorder.PopLast();
if (r == null) return false;
board.UndoMove(r.piece, r.from, r.captured);
currentTurn = r.piece.camp;
moveCount--;
isGameOver = false;
winnerCamp = ChessCamp.None;
OnUndoApplied?.Invoke(1);
OnTurnChanged?.Invoke(currentTurn);
return true;
}
///
/// 超时代走:AI 自动为本地玩家走一步合法棋(不判负、不结算)
/// 策略:优先吃价值最高的子;没有吃子就避开会被攻击的位置随机走
/// 返回是否成功走棋
///
public bool AutoMove()
{
if (isGameOver || currentTurn != myCamp) return false;
var candidates = new List();
int bestScore = int.MinValue;
ChessCamp enemy = myCamp == ChessCamp.Red ? ChessCamp.Black : ChessCamp.Red;
foreach (var p in board.allPieces)
{
if (!p.isAlive || p.camp != myCamp) continue;
var legal = rules.GetLegalMoves(board, p);
foreach (var to in legal)
{
int score;
var target = board.GetPiece(to);
if (target != null)
{
score = GetPieceValue(target.pieceType); // 吃子按价值
}
else if (rules.IsSquareAttacked(board, to, enemy))
{
score = -GetPieceValue(p.pieceType); // 走过去会被吃的位置降权
}
else
{
score = 0;
}
if (score > bestScore) bestScore = score;
candidates.Add(new AutoCandidate { from = p.pos, to = to, score = score });
}
}
if (candidates.Count == 0) return false;
// 同分随机挑一个(避免每次 AI 走得一模一样)
var pool = new List();
for (int i = 0; i < candidates.Count; i++)
if (candidates[i].score == bestScore) pool.Add(candidates[i]);
var pick = pool[UnityEngine.Random.Range(0, pool.Count)];
return TryLocalMove(pick.from, pick.to);
}
/// 棋子价值(AI 吃子/避损评估用)
private static int GetPieceValue(ChessPieceType type)
{
switch (type)
{
case ChessPieceType.Jiang: return 1000;
case ChessPieceType.Ju: return 90; // 车
case ChessPieceType.Pao: return 45; // 炮
case ChessPieceType.Ma: return 40; // 马
case ChessPieceType.Xiang: return 20; // 象
case ChessPieceType.Shi: return 20; // 士
case ChessPieceType.Bing: return 20; // 兵
default: return 0;
}
}
/// AI 候选走法
private struct AutoCandidate
{
public Vector2Int from;
public Vector2Int to;
public int score;
}
private void SetGameOver(ChessCamp winner)
{
isGameOver = true;
winnerCamp = winner;
OnGameOver?.Invoke(winner);
}
///
/// 外部强制结束对局(挂起判负 / 服务端结算同步)。
/// silent=true 只置结束状态、不触发 OnGameOver(调用方自行上报和弹面板,避免重复)
///
public void ForceGameOver(ChessCamp winner, bool silent = false)
{
if (isGameOver) return;
isGameOver = true;
winnerCamp = winner;
if (!silent) OnGameOver?.Invoke(winner);
}
}