refactor(chess): 重构象棋程序基础结构,使用Nullable重构核心代码
- 更新了多个文件的代码结构和类型定义,提高了代码的健壮性和可维护性 - 优化了事件处理、棋子管理和移动逻辑,为后续功能扩展打下坚实基础 - 修复了一些潜在的空指针异常问题,提高了程序的稳定性 - 修复部分bug
This commit is contained in:
@ -6,7 +6,7 @@ using Vector2I = Vector.Vector2I;
|
||||
namespace ChineseChess;
|
||||
public class CCBoard : AbstractBoard
|
||||
{
|
||||
public event EventHandler<int> OnStepsChanged;
|
||||
public event EventHandler<int>? OnStepsChanged;
|
||||
private int steps = 0;
|
||||
public int Steps {
|
||||
get => steps;
|
||||
@ -20,7 +20,7 @@ public class CCBoard : AbstractBoard
|
||||
public CCBoard() : base(9, 10) {
|
||||
}
|
||||
|
||||
public override void AddRecord(IPiece From, IPiece To, Vector2I FromPos, Vector2I ToPos) {
|
||||
public override void AddRecord(IPiece? From, IPiece? To, Vector2I FromPos, Vector2I ToPos) {
|
||||
base.AddRecord(From, To, FromPos, ToPos);
|
||||
Steps += 1;
|
||||
}
|
||||
|
@ -33,12 +33,19 @@ public class ChessCore {
|
||||
playerSelf = new(board, Player.PlayerType.Human);
|
||||
playerOpponent = new(board, Player.PlayerType.Human);
|
||||
|
||||
void func(object _self, IBoard.MoveEventArgs record) {
|
||||
void func(object? _self, IBoard.MoveEventArgs record) {
|
||||
// 防止 Undo 时 Selected Clear 出现 Null Pointer Exception
|
||||
playerSelf.SelectedClear();
|
||||
playerOpponent.SelectedClear();
|
||||
board.AddRecord(board.GetPiece(record.From), board.GetPiece(record.To),
|
||||
record.From, record.To);
|
||||
|
||||
// TODO FIXME it can be simple
|
||||
if (record.From is null || record.To is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
IPiece? from = record.From is not null ? board.GetPiece(record.From) : null;
|
||||
IPiece? to = record.To is not null ? board.GetPiece(record.To) : null;
|
||||
board.AddRecord(from, to, record.From ?? new(), record.To ?? new());
|
||||
}
|
||||
playerSelf.OnMove += func;
|
||||
playerOpponent.OnMove += func;
|
||||
|
@ -7,7 +7,7 @@ using Vector;
|
||||
using static ChineseChess.ChessCore;
|
||||
|
||||
public class CCPiece : AbstractPiece {
|
||||
public string CNName { get; protected set; }
|
||||
public string CNName { get; protected set; } = "unknown";
|
||||
public TurnsSideType TurnsSide { get; }
|
||||
protected CCBoard board;
|
||||
protected Vector2I localPos = new();
|
||||
@ -16,14 +16,15 @@ public class CCPiece : AbstractPiece {
|
||||
new(new Vector2I(1, 0), new Vector2I(0, -1), new Vector2I(-4, 9));
|
||||
static readonly Trans2DI transBlackGlobal2Local =
|
||||
new(new Vector2I(-1, 0), new Vector2I(0, 1), new Vector2I(4, 0));
|
||||
public CCPiece(CCBoard board = null, TurnsSideType turnsSide = TurnsSideType.Red,
|
||||
string name = "", Vector2I pos = null) : base(name) {
|
||||
this.board = board;
|
||||
public CCPiece(CCBoard? board = null, TurnsSideType turnsSide = TurnsSideType.Red,
|
||||
string name = "", Vector2I? pos = null) : base(name) {
|
||||
this.board = board ?? new();
|
||||
TurnsSide = turnsSide;
|
||||
OnPos += (sender, args) => {
|
||||
localPos = Global2Local(Pos);
|
||||
};
|
||||
Pos = pos != null ? Local2Global(pos) : Vector2I.Zero;
|
||||
Pos = pos is not null ? Local2Global(pos) : Vector2I.Zero;
|
||||
localPos = Global2Local(Pos);
|
||||
}
|
||||
|
||||
public override bool CanMove(Vector2I to) {
|
||||
@ -38,8 +39,8 @@ public class CCPiece : AbstractPiece {
|
||||
return TurnsSide == TurnsSideType.Red ? (transRedGlobal2Local * pos) : (transBlackGlobal2Local * pos);
|
||||
}
|
||||
|
||||
protected CCPiece GetCCPieceLocal(in Vector2I pos) {
|
||||
return (CCPiece)board.GetPiece(Local2Global(pos));
|
||||
protected CCPiece? GetCCPieceLocal(in Vector2I pos) {
|
||||
return (CCPiece?)board.GetPiece(Local2Global(pos));
|
||||
}
|
||||
|
||||
public virtual List<Vector2I> CanMoveAllPosSelf() {
|
||||
@ -49,7 +50,8 @@ public class CCPiece : AbstractPiece {
|
||||
public IEnumerable<Vector2I> CanMoveAllPosLocal() {
|
||||
var self = CanMoveAllPosSelf().Select(item => item + localPos);// 转换局部坐标
|
||||
var ret = self.Where(item => {
|
||||
bool ret = GetCCPieceLocal(item) == null || GetCCPieceLocal(item).TurnsSide != TurnsSide;
|
||||
CCPiece? piece = GetCCPieceLocal(item);
|
||||
bool ret = piece is null || piece.TurnsSide != TurnsSide;
|
||||
// Console.WriteLine($"{item} can move: {ret}");
|
||||
return ret;
|
||||
}); // 过滤无效位置
|
||||
@ -71,7 +73,7 @@ public class CCPiece : AbstractPiece {
|
||||
return board.IsPosOutOfRange(Local2Global(pos));
|
||||
}
|
||||
|
||||
protected CCPiece GetRecursivePieceLocal(Vector2I origin, Vector2I pos) {
|
||||
protected CCPiece? GetRecursivePieceLocal(Vector2I origin, Vector2I pos) {
|
||||
Vector2I with = origin + pos;
|
||||
while (!IsPosOutOfRangeLocal(with) && GetCCPieceLocal(with) == null) {
|
||||
with += pos;
|
||||
|
@ -7,7 +7,7 @@ public class Player {
|
||||
private readonly CCBoard board;
|
||||
private readonly SelectedPiece selectedNode;
|
||||
|
||||
public EventHandler<IBoard.MoveEventArgs> OnMove;
|
||||
public EventHandler<IBoard.MoveEventArgs>? OnMove;
|
||||
public bool CanMove { get; set; } = true;
|
||||
|
||||
public enum PlayerType {
|
||||
@ -24,7 +24,7 @@ public class Player {
|
||||
public void HandleBoardPosClick(Vector2I clickPos) {
|
||||
if (board.IsPosOutOfRange(clickPos)) return;
|
||||
// Console.WriteLine($"VirtualBoard {clickPos} clicked");
|
||||
IPiece clickChess = board.GetPiece(clickPos);
|
||||
IPiece? clickChess = board.GetPiece(clickPos);
|
||||
|
||||
if (!selectedNode.HasSelected()) {
|
||||
// Select piece
|
||||
@ -45,9 +45,10 @@ public class Player {
|
||||
|
||||
public void MoveAndRecord(Vector2I toPos, Vector2I fromPos) {
|
||||
// GD.Print($"{fromPos} move to {toPos}");
|
||||
IPiece toChess = board.GetPiece(toPos);
|
||||
IPiece fromChess = board.GetPiece(fromPos);
|
||||
IPiece? toChess = board.GetPiece(toPos);
|
||||
IPiece? fromChess = board.GetPiece(fromPos);
|
||||
if (fromChess != null) fromChess.IsSelected = false;
|
||||
else return;
|
||||
|
||||
selectedNode.Clear();
|
||||
if (!fromChess.CanMove(toPos)) {
|
||||
@ -55,7 +56,8 @@ public class Player {
|
||||
}
|
||||
|
||||
// MUST BE THERE !!! 防止删除节点后在启动回调导致错误
|
||||
OnMove?.Invoke(this, new IBoard.MoveEventArgs { From = fromPos, To = toPos });
|
||||
OnMove?.Invoke(this, new IBoard.MoveEventArgs
|
||||
{ From = fromPos, To = toPos, Piece = fromChess });;
|
||||
|
||||
if (toChess != null) {
|
||||
board.RemovePiece(toPos);
|
||||
@ -76,9 +78,9 @@ public class Player {
|
||||
private class SelectedPiece {
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
private Vector2I selectedNodePos = Vector2I.MaxValue;
|
||||
private IPiece piece;
|
||||
private IPiece? piece = null;
|
||||
private readonly CCBoard board;
|
||||
public ArrayList allowedPieces = null;
|
||||
public ArrayList? allowedPieces = null;
|
||||
|
||||
public SelectedPiece(CCBoard board) {
|
||||
this.board = board;
|
||||
@ -87,7 +89,8 @@ public class Player {
|
||||
public void Clear() {
|
||||
if (selectedNodePos != Vector2I.MaxValue) {
|
||||
selectedNodePos = Vector2I.MaxValue;
|
||||
piece.IsSelected = false;
|
||||
if (piece != null)
|
||||
piece.IsSelected = false;
|
||||
}
|
||||
// Console.WriteLine("SelectedPiece.Clear {0}", piece);
|
||||
}
|
||||
@ -98,11 +101,13 @@ public class Player {
|
||||
return;
|
||||
}
|
||||
selectedNodePos = pos;
|
||||
if (piece == null)
|
||||
return;
|
||||
piece.IsSelected = true;
|
||||
Console.WriteLine("SelectedPiece.SetPos {0}", piece);
|
||||
}
|
||||
|
||||
public IPiece GetPiece() {
|
||||
public IPiece? GetPiece() {
|
||||
return piece;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ using Vector2I = Vector.Vector2I;
|
||||
|
||||
namespace ChineseChess;
|
||||
public class CCGeneral : CCPiece {
|
||||
public CCGeneral(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
|
||||
public CCGeneral(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I? pos = null)
|
||||
: base(board, turnsSide, name : "General", pos) {
|
||||
if (turnsSide == TurnsSideType.Red) {
|
||||
CNName = "帅";
|
||||
@ -31,7 +31,7 @@ public class CCGeneral : CCPiece {
|
||||
}
|
||||
|
||||
public class CCAdvisor : CCPiece {
|
||||
public CCAdvisor(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
|
||||
public CCAdvisor(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I? pos = null)
|
||||
: base(board, turnsSide, name : "Advisor", pos) {
|
||||
if (turnsSide == TurnsSideType.Red) {
|
||||
CNName = "仕";
|
||||
@ -58,7 +58,7 @@ public class CCAdvisor : CCPiece {
|
||||
}
|
||||
|
||||
public class CCElephant : CCPiece {
|
||||
public CCElephant(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
|
||||
public CCElephant(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I? pos = null)
|
||||
: base(board, turnsSide, name : "Bishop", pos) {
|
||||
if (turnsSide == TurnsSideType.Red) {
|
||||
CNName = "相";
|
||||
@ -85,7 +85,7 @@ public class CCElephant : CCPiece {
|
||||
}
|
||||
|
||||
public class CCHorse : CCPiece {
|
||||
public CCHorse(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
|
||||
public CCHorse(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I? pos = null)
|
||||
: base(board, turnsSide, name : "Horse", pos) {
|
||||
if (turnsSide == TurnsSideType.Red) {
|
||||
CNName = "馬";
|
||||
@ -125,7 +125,7 @@ public class CCHorse : CCPiece {
|
||||
}
|
||||
|
||||
public class CCChariot : CCPiece {
|
||||
public CCChariot(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
|
||||
public CCChariot(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I? pos = null)
|
||||
: base(board, turnsSide, name : "Chariot", pos) {
|
||||
if (turnsSide == TurnsSideType.Red) {
|
||||
CNName = "車";
|
||||
@ -145,7 +145,7 @@ public class CCChariot : CCPiece {
|
||||
break;
|
||||
}
|
||||
if (GetCCPieceLocal(ptr) != null) {
|
||||
list.Add(ptr);
|
||||
list.Add(ptr - localPos);
|
||||
break;
|
||||
}
|
||||
list.Add(ptr - localPos);
|
||||
@ -161,7 +161,7 @@ public class CCChariot : CCPiece {
|
||||
}
|
||||
|
||||
public class CCCannon : CCPiece {
|
||||
public CCCannon(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
|
||||
public CCCannon(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I? pos = null)
|
||||
: base(board, turnsSide, name : "Cannon", pos) {
|
||||
if (turnsSide == TurnsSideType.Red) {
|
||||
CNName = "砲";
|
||||
@ -202,7 +202,7 @@ public class CCCannon : CCPiece {
|
||||
}
|
||||
|
||||
public class CCPawn : CCPiece {
|
||||
public CCPawn(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
|
||||
public CCPawn(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I? pos = null)
|
||||
: base(board, turnsSide, name : "Pawn", pos) {
|
||||
if (turnsSide == TurnsSideType.Red) {
|
||||
CNName = "兵";
|
||||
|
Reference in New Issue
Block a user