feat(重构): 重构棋盘和棋子的逻辑

- 重构了 ChessBoard 和 ChessPiece 类的逻辑
- 添加了新版本的 ChineseChess 库
- 删除了旧版本的 VirtualBoard 和 VirtualPiece 类
- 更新了相关的场景和脚本
This commit is contained in:
ZZY
2024-11-22 20:01:40 +08:00
parent 8ee9732a6f
commit 9c619784af
28 changed files with 1068 additions and 455 deletions

View File

@ -0,0 +1,100 @@
using System;
using System.Collections;
using static ChineseChess.ChessCore;
using Vector2I = Vector.Vector2I;
namespace ChineseChess;
public class CCBoard : AbstractBoard
{
public event EventHandler<int> OnStepsChanged;
private int steps = 0;
public int Steps {
get => steps;
protected set {
if (steps != value) {
steps = value;
OnStepsChanged?.Invoke(this, steps);
}
}
}
public CCBoard() : base(9, 10) {
}
public override void AddRecord(IPiece From, IPiece To, Vector2I FromPos, Vector2I ToPos) {
base.AddRecord(From, To, FromPos, ToPos);
Steps += 1;
}
public override void UndoRecord() {
base.UndoRecord();
if (Steps > 0) Steps -= 1;
}
public (ArrayList, ArrayList) InitGame() {
Steps = 0;
// ArrayList blackPart = InitOnePartPieces(TurnsSideType.Black, new[] {
// new CCChariot(TurnsSideType.Black, "black chariot 0", 0, 0),
// ("车", 0, 0), ("马", 1, 0), ("象", 2, 0),
// ("士", 3, 0), ("将", 4, 0), ("士", 5, 0),
// ("象", 6, 0), ("马", 7, 0), ("车", 8, 0),
// ("炮", 1, 2), ("炮", 7, 2),
// ("卒", 0, 3), ("卒", 2, 3), ("卒", 4, 3), ("卒", 6, 3), ("卒", 8, 3)
// });
// ArrayList redPart = InitOnePartPieces(TurnsSideType.Red, new[] {
// ("车", 0, -0), ("马", 1, -0), ("象", 2, -0),
// ("士", 3, -0), ("将", 4, -0), ("士", 5, -0),
// ("象", 6, -0), ("马", 7, -0), ("车", 8, -0),
// ("炮", 1, -2), ("炮", 7, -2),
// ("卒", 0, -3), ("卒", 2, -3), ("卒", 4, -3), ("卒", 6, -3), ("卒", 8, -3)
// });
ArrayList blackPart = InitOnePartPieces(TurnsSideType.Black, new CCPiece[] {
new CCChariot (this, TurnsSideType.Black, new Vector2I( 4, 0)), // 4,0
new CCHorse (this, TurnsSideType.Black, new Vector2I( 3, 0)), // 3,0
new CCElephant(this, TurnsSideType.Black, new Vector2I( 2, 0)), // 2,0
new CCAdvisor (this, TurnsSideType.Black, new Vector2I( 1, 0)), // 1,0
new CCGeneral (this, TurnsSideType.Black, new Vector2I( 0, 0)), // 0,0
new CCAdvisor (this, TurnsSideType.Black, new Vector2I(-1, 0)), // -1,0
new CCElephant(this, TurnsSideType.Black, new Vector2I(-2, 0)), // -2,0
new CCHorse (this, TurnsSideType.Black, new Vector2I(-3, 0)), // -3,0
new CCChariot (this, TurnsSideType.Black, new Vector2I(-4, 0)), // -4,0
new CCCannon (this, TurnsSideType.Black, new Vector2I( 3, 2)), // 3,2
new CCCannon (this, TurnsSideType.Black, new Vector2I(-3, 2)), // -3,2
new CCPawn (this, TurnsSideType.Black, new Vector2I(-4, 3)), // -4,3
new CCPawn (this, TurnsSideType.Black, new Vector2I(-2, 3)), // -2,3
new CCPawn (this, TurnsSideType.Black, new Vector2I( 0, 3)), // 0,3
new CCPawn (this, TurnsSideType.Black, new Vector2I( 2, 3)), // -4,3
new CCPawn (this, TurnsSideType.Black, new Vector2I( 4, 3)) // 4,3
});
ArrayList redPart = InitOnePartPieces(TurnsSideType.Red, new CCPiece[] {
new CCChariot (this, TurnsSideType.Red, new Vector2I( 4, 0)), // 4,0
new CCHorse (this, TurnsSideType.Red, new Vector2I( 3, 0)), // 3,0
new CCElephant(this, TurnsSideType.Red, new Vector2I( 2, 0)), // 2,0
new CCAdvisor (this, TurnsSideType.Red, new Vector2I( 1, 0)), // 1,0
new CCGeneral (this, TurnsSideType.Red, new Vector2I( 0, 0)), // 0,0
new CCAdvisor (this, TurnsSideType.Red, new Vector2I(-1, 0)), // -1,0
new CCElephant(this, TurnsSideType.Red, new Vector2I(-2, 0)), // -2,0
new CCHorse (this, TurnsSideType.Red, new Vector2I(-3, 0)), // -3,0
new CCChariot (this, TurnsSideType.Red, new Vector2I(-4, 0)), // -4,0
new CCCannon (this, TurnsSideType.Red, new Vector2I( 3, 2)), // 3,2
new CCCannon (this, TurnsSideType.Red, new Vector2I(-3, 2)), // -3,2
new CCPawn (this, TurnsSideType.Red, new Vector2I(-4, 3)), // -4,3
new CCPawn (this, TurnsSideType.Red, new Vector2I(-2, 3)), // -2,3
new CCPawn (this, TurnsSideType.Red, new Vector2I( 0, 3)), // 0,3
new CCPawn (this, TurnsSideType.Red, new Vector2I( 2, 3)), // 2,3
new CCPawn (this, TurnsSideType.Red, new Vector2I( 4, 3)) // 4,3
});
return (blackPart, redPart);
}
private ArrayList InitOnePartPieces(TurnsSideType side, CCPiece[] pieces) {
ArrayList list = new();
foreach (var piece in pieces) {
list.Add(piece);
InsertPiece(piece, piece.Pos);
}
return list;
}
}

View File

@ -0,0 +1,107 @@
using Vector2 = Vector.Vector2I;
using System;
using System.Collections;
namespace ChineseChess;
public class ChessCore {
public enum Mode {
SingleMode,
MultiMode,
DebugMode,
};
public enum TurnsSideType {
Red,
Black,
};
public enum PlayerSideType {
Self,
Opponent,
Any,
};
private TurnsSideType sideType = TurnsSideType.Red;
private readonly TurnsSideType selfSide;
public readonly CCBoard board = new();
private readonly Player playerSelf;
private readonly Player playerOpponent;
// public EventHandler<IBoard.MoveEventArgs> OnMove;
public ChessCore(Mode mode, TurnsSideType selfSide) {
this.selfSide = selfSide;
playerSelf = new(board, Player.PlayerType.Human);
playerOpponent = new(board, Player.PlayerType.Human);
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);
}
playerSelf.OnMove += func;
playerOpponent.OnMove += func;
switch (mode) {
case Mode.SingleMode:
break;
case Mode.MultiMode:
break;
default:
case Mode.DebugMode:
throw new NotImplementedException();
}
}
public void Init() {
(ArrayList blackPart, ArrayList redPart) = board.InitGame();
if (selfSide == TurnsSideType.Red) {
playerSelf.SetAllowedPieces(redPart);
playerOpponent.SetAllowedPieces(blackPart);
} else {
playerSelf.SetAllowedPieces(blackPart);
playerOpponent.SetAllowedPieces(redPart);
}
}
public void OnPosClicked(Vector2 pos, PlayerSideType clickedSide = PlayerSideType.Any) {
if (GetTurnsType() == selfSide) {
playerSelf.CanMove = true;
playerOpponent.CanMove = false;
} else {
playerSelf.CanMove = false;
playerOpponent.CanMove = true;
}
switch (clickedSide) {
case PlayerSideType.Any:
playerSelf.HandleBoardPosClick(pos);
playerOpponent.HandleBoardPosClick(pos);
break;
case PlayerSideType.Self:
playerSelf.HandleBoardPosClick(pos);
break;
case PlayerSideType.Opponent:
playerOpponent.HandleBoardPosClick(pos);
break;
}
}
public TurnsSideType GetTurnsType() {
sideType = board.Steps % 2 == 0 ? TurnsSideType.Red : TurnsSideType.Black;
return sideType;
}
public void Undo() {
playerSelf.SelectedClear();
playerOpponent.SelectedClear();
board.UndoRecord();
}
public void ReInit() {
playerSelf.SelectedClear();
playerOpponent.SelectedClear();
board.Clear(true);
Init();
}
}

View File

@ -0,0 +1,81 @@
namespace ChineseChess;
using System;
using System.Collections.Generic;
using System.Linq;
using Vector;
using static ChineseChess.ChessCore;
public class CCPiece : AbstractPiece {
public string CNName { get; protected set; }
public TurnsSideType TurnsSide { get; }
protected CCBoard board;
protected Vector2I localPos = new();
static readonly Trans2DI transRedGlobal2Local =
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;
TurnsSide = turnsSide;
OnPos += (sender, args) => {
localPos = Global2Local(Pos);
};
Pos = pos != null ? Local2Global(pos) : Vector2I.Zero;
}
public override bool CanMove(Vector2I to) {
return CanMoveAllPos().Any(item => item == to);
}
protected Vector2I Local2Global(in Vector2I pos) {
return TurnsSide == TurnsSideType.Red ? (pos * transRedGlobal2Local) : (pos * transBlackGlobal2Local);
}
protected Vector2I Global2Local(in Vector2I pos) {
return TurnsSide == TurnsSideType.Red ? (transRedGlobal2Local * pos) : (transBlackGlobal2Local * pos);
}
protected CCPiece GetCCPieceLocal(in Vector2I pos) {
return (CCPiece)board.GetPiece(Local2Global(pos));
}
public virtual List<Vector2I> CanMoveAllPosSelf() {
return new List<Vector2I>();
}
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;
// Console.WriteLine($"{item} can move: {ret}");
return ret;
}); // 过滤无效位置
// Console.WriteLine("localPos: {0}", localPos);
// Console.WriteLine("CanMoveAllPosSelf: {0}", string.Join(",", self));
// Console.WriteLine("CanMoveAllPosLocal: {0}", string.Join(",", ret));
return ret;
}
public IEnumerable<Vector2I> CanMoveAllPos() {
var ret = CanMoveAllPosLocal()
.Select(item => Local2Global(item)); // 转换为全局坐标
// Console.WriteLine("CanMoveAllPos: {0}", string.Join(",", ret));
return ret;
}
protected bool IsPosOutOfRangeLocal(Vector2I pos) {
return board.IsPosOutOfRange(Local2Global(pos));
}
protected CCPiece GetRecursivePieceLocal(Vector2I origin, Vector2I pos) {
Vector2I with = origin + pos;
while (!IsPosOutOfRangeLocal(with) && GetCCPieceLocal(with) == null) {
with += pos;
}
return GetCCPieceLocal(with);
}
}

View File

@ -1,12 +1,13 @@
using Vector2 = Godot.Vector2;
using System;
using System.Collections;
using Vector2I = Vector.Vector2I;
namespace ChineseChess;
public class Player {
private readonly VirtualBoard board;
private readonly CCBoard board;
private readonly SelectedPiece selectedNode;
public EventHandler<VirtualBoard.MoveEventArgs> OnMove;
public EventHandler<IBoard.MoveEventArgs> OnMove;
public bool CanMove { get; set; } = true;
public enum PlayerType {
@ -14,16 +15,16 @@ public class Player {
AI
}
public Player(VirtualBoard board, PlayerType type = PlayerType.Human)
public Player(CCBoard board, PlayerType type = PlayerType.Human)
{
this.board = board;
this.selectedNode = new SelectedPiece(board);
}
public void HandleBoardPosClick(Vector2 clickPos) {
if (board.ArrPosOutOfRange(clickPos)) return;
// GD.Print($"VirtualBoard {clickPos} clicked");
VirtualPiece clickChess = board.GetPiece(clickPos);
public void HandleBoardPosClick(Vector2I clickPos) {
if (board.IsPosOutOfRange(clickPos)) return;
// Console.WriteLine($"VirtualBoard {clickPos} clicked");
IPiece clickChess = board.GetPiece(clickPos);
if (!selectedNode.HasSelected()) {
// Select piece
@ -42,22 +43,23 @@ public class Player {
}
}
public void MoveAndRecord(Vector2 toPos, Vector2 fromPos) {
public void MoveAndRecord(Vector2I toPos, Vector2I fromPos) {
// GD.Print($"{fromPos} move to {toPos}");
VirtualPiece toChess = board.GetPiece(toPos);
VirtualPiece fromChess = board.GetPiece(fromPos);
fromChess?.Selected(false);
IPiece toChess = board.GetPiece(toPos);
IPiece fromChess = board.GetPiece(fromPos);
if (fromChess != null) fromChess.IsSelected = false;
// MUST BE THERE !!! 防止删除节点后在启动回调导致错误
OnMove?.Invoke(this, new VirtualBoard.MoveEventArgs { From = fromPos, To = toPos });
VirtualPiece NowNode;
if (toChess != null) {
NowNode = toChess;
board.RemovePiece(toPos);
} else {
NowNode = toChess;
selectedNode.Clear();
if (!fromChess.CanMove(toPos)) {
return;
}
// MUST BE THERE !!! 防止删除节点后在启动回调导致错误
OnMove?.Invoke(this, new IBoard.MoveEventArgs { From = fromPos, To = toPos });
if (toChess != null) {
board.RemovePiece(toPos);
}
board.MovePiece(fromPos, toPos);
selectedNode.Clear();
@ -73,42 +75,43 @@ public class Player {
private class SelectedPiece {
// Called when the node enters the scene tree for the first time.
private Vector2 selectedNodePos = Vector2.Inf;
private VirtualPiece piece;
private readonly VirtualBoard board;
private Vector2I selectedNodePos = Vector2I.MaxValue;
private IPiece piece;
private readonly CCBoard board;
public ArrayList allowedPieces = null;
public SelectedPiece(VirtualBoard board) {
public SelectedPiece(CCBoard board) {
this.board = board;
}
public void Clear() {
if (selectedNodePos != Vector2.Inf) {
selectedNodePos = Vector2.Inf;
piece.Selected(false);
if (selectedNodePos != Vector2I.MaxValue) {
selectedNodePos = Vector2I.MaxValue;
piece.IsSelected = false;
}
// Console.WriteLine("SelectedPiece.Clear {0}", piece);
}
public void SetPos(Vector2 pos) {
// piece = board.GetNodeFromBoard(pos) as VirtualPiece;
public void SetPos(Vector2I pos) {
piece = board.GetPiece(pos);
if (allowedPieces != null && allowedPieces.Contains(piece) == false) {
return;
}
selectedNodePos = pos;
piece.Selected(true);
piece.IsSelected = true;
Console.WriteLine("SelectedPiece.SetPos {0}", piece);
}
public VirtualPiece GetPiece() {
public IPiece GetPiece() {
return piece;
}
public Vector2 GetPos() {
public Vector2I GetPos() {
return selectedNodePos;
}
public bool HasSelected() {
return selectedNodePos != Vector2.Inf;
return selectedNodePos != Vector2I.MaxValue;
}
}
}

View File

@ -0,0 +1,246 @@
using System.Collections.Generic;
using static ChineseChess.ChessCore;
using Vector2I = Vector.Vector2I;
namespace ChineseChess;
public class CCGeneral : CCPiece {
public CCGeneral(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
: base(board, turnsSide, name : "General", pos) {
if (turnsSide == TurnsSideType.Red) {
CNName = "帅";
} else {
CNName = "将";
}
}
public override List<Vector2I> CanMoveAllPosSelf() {
List<Vector2I> list = new() {
new(1, 0),
new(-1, 0),
new(0, 1),
new(0, -1),
};
// 移除不符合条件的元素
list.RemoveAll(item =>
localPos.X + item.X > 1 || localPos.X + item.X < -1 ||
localPos.Y + item.Y > 2 || localPos.Y + item.Y < 0
|| GetRecursivePieceLocal(localPos + item, new(0, 1)) is CCGeneral);
return list;
}
}
public class CCAdvisor : CCPiece {
public CCAdvisor(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
: base(board, turnsSide, name : "Advisor", pos) {
if (turnsSide == TurnsSideType.Red) {
CNName = "仕";
} else {
CNName = "士";
}
}
public override List<Vector2I> CanMoveAllPosSelf() {
List<Vector2I> list = new() {
new(1, 1),
new(-1, 1),
new(1, -1),
new(-1, -1),
};
// 移除不符合条件的元素
list.RemoveAll(item =>
localPos.X + item.X > 1 || localPos.X + item.X < -1 ||
localPos.Y + item.Y > 2 || localPos.Y + item.Y < 0);
return list;
}
}
public class CCElephant : CCPiece {
public CCElephant(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
: base(board, turnsSide, name : "Bishop", pos) {
if (turnsSide == TurnsSideType.Red) {
CNName = "相";
} else {
CNName = "象";
}
}
public override List<Vector2I> CanMoveAllPosSelf() {
List<Vector2I> list = new() {
new(2, 2),
new(-2, 2),
new(2, -2),
new(-2, -2),
};
list.RemoveAll(item => IsPosOutOfRangeLocal(localPos + item));
// 移除不符合条件的元素
list.RemoveAll(item => localPos.Y + item.Y < 0 || localPos.Y + item.Y > 4 ||
GetCCPieceLocal(new(localPos.X + item.X / 2, localPos.Y + item.Y / 2)) is not null);
return list;
}
}
public class CCHorse : CCPiece {
public CCHorse(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
: base(board, turnsSide, name : "Horse", pos) {
if (turnsSide == TurnsSideType.Red) {
CNName = "馬";
} else {
CNName = "马";
}
}
public override List<Vector2I> CanMoveAllPosSelf() {
List<Vector2I> list = new () {
new Vector2I(1, 2),
new Vector2I(1, -2),
new Vector2I(2, 1),
new Vector2I(2, -1),
new Vector2I(-1, -2),
new Vector2I(-1, 2),
new Vector2I(-2, -1),
new Vector2I(-2, 1),
};
list.RemoveAll(item => IsPosOutOfRangeLocal(localPos + item));
list.RemoveAll(item => {
Vector2I pos = new(localPos);
if (item.X == 2) {
pos.X += 1;
} else if (item.X == -2) {
pos.X -= 1;
} else if (item.Y == 2) {
pos.Y += 1;
} else if (item.Y == -2) {
pos.Y -= 1;
}
return GetCCPieceLocal(pos) is not null;
});
return list;
}
}
public class CCChariot : CCPiece {
public CCChariot(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
: base(board, turnsSide, name : "Chariot", pos) {
if (turnsSide == TurnsSideType.Red) {
CNName = "車";
} else {
CNName = "车";
}
}
public override List<Vector2I> CanMoveAllPosSelf() {
List<Vector2I> list = new ();
void func(Vector2I added) {
Vector2I ptr = new(localPos);
while (true) {
ptr += added;
if (IsPosOutOfRangeLocal(ptr)) {
break;
}
if (GetCCPieceLocal(ptr) != null) {
list.Add(ptr);
break;
}
list.Add(ptr - localPos);
}
}
func(Vector2I.Up);
func(Vector2I.Down);
func(Vector2I.Left);
func(Vector2I.Right);
return list;
}
}
public class CCCannon : CCPiece {
public CCCannon(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
: base(board, turnsSide, name : "Cannon", pos) {
if (turnsSide == TurnsSideType.Red) {
CNName = "砲";
} else {
CNName = "炮";
}
}
public override List<Vector2I> CanMoveAllPosSelf() {
List<Vector2I> list = new ();
void func(Vector2I added) {
Vector2I ptr = new(localPos);
bool flag = true;
while (true) {
ptr += added;
if (IsPosOutOfRangeLocal(ptr)) {
break;
}
if (GetCCPieceLocal(ptr) != null) {
if (flag) {
flag = false;
} else {
list.Add(ptr - localPos);
break;
}
}
if (flag) list.Add(ptr - localPos);
}
}
func(Vector2I.Up);
func(Vector2I.Down);
func(Vector2I.Left);
func(Vector2I.Right);
return list;
}
}
public class CCPawn : CCPiece {
public CCPawn(CCBoard board, TurnsSideType turnsSide = TurnsSideType.Red, Vector2I pos = null)
: base(board, turnsSide, name : "Pawn", pos) {
if (turnsSide == TurnsSideType.Red) {
CNName = "兵";
} else {
CNName = "卒";
}
}
public override List<Vector2I> CanMoveAllPosSelf() {
List<Vector2I> list = new () {
new(0, 1),
new(1, 0),
new(-1, 0),
};
list.RemoveAll(item => IsPosOutOfRangeLocal(localPos + item));
list.RemoveAll(item => localPos.Y <= 4 && item != new Vector2I(0, 1));
return list;
}
}
// 帅/将 (General) - 代表双方的最高统帅。
// 子类名ChessGeneral
// 仕/士 (Advisor) - 保护帅/将的近身侍卫。
// 子类名ChessAdvisor
// 相/象 (Elephant) - 行动受限,走田字格,不能过河。
// 子类名ChessElephant
// 車/车 (Chariot) - 横竖移动,威力巨大。
// 子类名ChessChariot
// 馬/马 (Horse) - 走日字形,跳跃式移动。
// 子类名ChessHorse
// 砲/炮 (Cannon) - 需要隔子才能吃子,直线移动。
// 子类名ChessCannon
// 兵/卒 (Pawn) - 最基础的棋子,过河后可横移。
// 子类名ChessPawn

View File

@ -1,20 +0,0 @@
// 帅/将 (General) - 代表双方的最高统帅。
// 子类名ChessGeneral
// 仕/士 (Advisor) - 保护帅/将的近身侍卫。
// 子类名ChessAdvisor
// 相/象 (Elephant) - 行动受限,走田字格,不能过河。
// 子类名ChessElephant
// 車/车 (Chariot) - 横竖移动,威力巨大。
// 子类名ChessChariot
// 馬/马 (Horse) - 走日字形,跳跃式移动。
// 子类名ChessHorse
// 砲/炮 (Cannon) - 需要隔子才能吃子,直线移动。
// 子类名ChessCannon
// 兵/卒 (Pawn) - 最基础的棋子,过河后可横移。
// 子类名ChessPawn

View File

@ -1,154 +0,0 @@
using Vector2 = Godot.Vector2;
using System;
using System.Collections;
namespace ChineseChess;
class ChessCore {
public enum Mode {
SingleMode,
MultiMode,
DebugMode,
};
public enum TurnsSideType {
Red,
Black,
};
public enum PlayerSideType {
Self,
Opponent,
Any,
};
private TurnsSideType sideType = TurnsSideType.Red;
private readonly TurnsSideType selfSide;
public readonly VirtualBoard board = new(9, 10);
private readonly Player playerSelf;
private readonly Player playerOpponent;
private readonly MoveRecords<VirtualPiece> moveRecords;
public EventHandler<VirtualBoard.MoveEventArgs> OnMove;
public ChessCore(Mode mode, TurnsSideType selfSide) {
this.selfSide = selfSide;
playerSelf = new(board, Player.PlayerType.Human);
playerOpponent = new(board, Player.PlayerType.Human);
playerSelf.OnMove += (sender, args) => {
moveRecords.AddRecord(board.GetPiece(args.To), board.GetPiece(args.From),
args.To, args.From);
};
playerOpponent.OnMove += (sender, args) => {
moveRecords.AddRecord(board.GetPiece(args.To), board.GetPiece(args.From),
args.To, args.From);
};
moveRecords = new MoveRecords<VirtualPiece>(
onAddRecordCallback: (newNode, oldNode, newPos, oldPos) => {
playerSelf.SelectedClear();
playerOpponent.SelectedClear();
},
onUndoRecordCallback: (newNode, oldNode, newPos, oldPos) => {
// GD.Print("Undo: ", newNode, "->", oldNode, ":", newPos, "->", oldPos);
VirtualPiece newPiece = newNode;
VirtualPiece oldPiece = oldNode;
board.MovePiece(newPos, oldPos);
if (newPiece != null) {
board.InsertPiece(newPiece, newPos);
}
});
switch (mode) {
case Mode.SingleMode:
break;
case Mode.MultiMode:
break;
default:
case Mode.DebugMode:
throw new NotImplementedException();
}
}
public void InitGame() {
ArrayList blackPart = InitOnePartPieces(TurnsSideType.Black, new[] {
("车", 0, 0), ("马", 1, 0), ("象", 2, 0),
("士", 3, 0), ("将", 4, 0), ("士", 5, 0),
("象", 6, 0), ("马", 7, 0), ("车", 8, 0),
("炮", 1, 2), ("炮", 7, 2),
("卒", 0, 3), ("卒", 2, 3), ("卒", 4, 3), ("卒", 6, 3), ("卒", 8, 3)
});
ArrayList redPart = InitOnePartPieces(TurnsSideType.Red, new[] {
("车", 0, -0), ("马", 1, -0), ("象", 2, -0),
("士", 3, -0), ("将", 4, -0), ("士", 5, -0),
("象", 6, -0), ("马", 7, -0), ("车", 8, -0),
("炮", 1, -2), ("炮", 7, -2),
("卒", 0, -3), ("卒", 2, -3), ("卒", 4, -3), ("卒", 6, -3), ("卒", 8, -3)
});
if (selfSide == TurnsSideType.Red) {
playerSelf.SetAllowedPieces(redPart);
playerOpponent.SetAllowedPieces(blackPart);
} else {
playerSelf.SetAllowedPieces(blackPart);
playerOpponent.SetAllowedPieces(redPart);
}
}
private ArrayList InitOnePartPieces(TurnsSideType side, (string label, int x, int y)[] positions) {
ArrayList list = new();
foreach (var (label, x, y) in positions) {
// FIXME: use a better way to initialize pieces
Vector2 pos = new(x, y + (TurnsSideType.Red == side ? 9 : 0));
VirtualPiece piece = new(label, pos);
list.Add(piece);
board.InsertPiece(piece, pos);
}
return list;
}
public void OnPosClicked(Vector2 pos, PlayerSideType clickedSide = PlayerSideType.Any) {
if (sideType == selfSide) {
playerSelf.CanMove = true;
playerOpponent.CanMove = false;
} else {
playerSelf.CanMove = false;
playerOpponent.CanMove = true;
}
switch (clickedSide) {
case PlayerSideType.Any:
playerSelf.HandleBoardPosClick(pos);
playerOpponent.HandleBoardPosClick(pos);
break;
case PlayerSideType.Self:
playerSelf.HandleBoardPosClick(pos);
break;
case PlayerSideType.Opponent:
playerOpponent.HandleBoardPosClick(pos);
break;
}
sideType = moveRecords.Count() % 2 == 0 ? TurnsSideType.Red : TurnsSideType.Black;
}
public TurnsSideType GetTurnsType() {
sideType = moveRecords.Count() % 2 == 0 ? TurnsSideType.Red : TurnsSideType.Black;
return sideType;
}
public void Undo() {
playerSelf.SelectedClear();
playerOpponent.SelectedClear();
moveRecords.Undo();
}
public void ReInit() {
playerSelf.SelectedClear();
playerOpponent.SelectedClear();
moveRecords.Clear();
board.Clear();
InitGame();
}
}