From 190a44aa887ff8976c3ab9aef6cac3f14232170a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A3=AE2003?= <2384017988@qq.com> Date: Sun, 13 Sep 2026 01:47:54 +0800 Subject: [PATCH] 1 --- .../ChessGame/ChessGameManager.cs | 74 +++++++++++++++++++ .../ChineseChess/ChineseChessMainPanel.cs | 17 ++--- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/Assets/MyGame/aa/UI/Game/ChineseChess/ChessGame/ChessGameManager.cs b/Assets/MyGame/aa/UI/Game/ChineseChess/ChessGame/ChessGameManager.cs index b6b681a90..db0c9ca36 100644 --- a/Assets/MyGame/aa/UI/Game/ChineseChess/ChessGame/ChessGameManager.cs +++ b/Assets/MyGame/aa/UI/Game/ChineseChess/ChessGame/ChessGameManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using UnityEngine; /// @@ -161,6 +162,79 @@ public class ChessGameManager 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; diff --git a/Assets/MyGame/aa/UI/Game/ChineseChess/ChineseChessMainPanel.cs b/Assets/MyGame/aa/UI/Game/ChineseChess/ChineseChessMainPanel.cs index 4562ec522..70aa8e4a2 100644 --- a/Assets/MyGame/aa/UI/Game/ChineseChess/ChineseChessMainPanel.cs +++ b/Assets/MyGame/aa/UI/Game/ChineseChess/ChineseChessMainPanel.cs @@ -547,8 +547,8 @@ public class ChineseChessMainPanel : GameMainPanel_Base } /// - /// 走棋倒计时:轮到我 → 显示在下方(超时自动判负,走 EndGame 通道); - /// 轮到对方 → 显示在上方(对方客户端超时会自行上报,本地只显示) + /// 走棋倒计时:轮到我 → 显示在下方(超时 AI 自动走一步,不判负不结算); + /// 轮到对方 → 显示在上方(对方客户端超时同样由它的 AI 自动走) /// private IEnumerator TurnCountdownRoutine(int totalSeconds, bool myTurn) { @@ -572,17 +572,14 @@ public class ChineseChessMainPanel : GameMainPanel_Base if (myTurn) { - // 我方超时 → 自动判负(与认输同款 EndGame 通道,第二个参数为胜者 userId) - if (chessGameManager != null && !chessGameManager.isGameOver && roomData != null) + // 我方超时 → AI 自动走一步(走完会触发回合切换,倒计时自动重开) + if (chessGameManager != null && !chessGameManager.isGameOver) { - var opponent = roomData.users.Find(u => u.userId != GameManager.Instance.user.BaseInfo.number); - if (opponent != null) - { - WebSocketSendList.EndGameChineseChess(roomData.roomId, opponent.userId); - } + bool moved = chessGameManager.AutoMove(); + if (moved && chessBoard != null) chessBoard.ShowToast("超时,已自动走子"); } } - // 对方回合超时:由对方客户端上报(服务端也有 timeoutSeconds 兜底),本地只停表 + // 对方回合超时:对方客户端同样 AI 自动走一步,我方等它的走子帧 } #endregion