refactor(重构): 重构了事件驱动的代码体系,使用全新命名和版本,以及测试套件的初试

- 移除了.csproj文件
- 更新了.gitignore,添加了.editorconfig
- 重构了IBoard和IPiece接口,引入了新的事件处理机制
- 优化了CCBoard、CCPiece等类的实现,使用新的事件驱动模型
- 删除了冗余代码,提高了代码的可读性和可维护性
This commit is contained in:
ZZY
2024-11-24 15:42:30 +08:00
parent 327c2df94d
commit e16f76e11f
18 changed files with 416 additions and 274 deletions

View File

@ -1,32 +1,20 @@
using System;
using System.Collections.Generic;
using Vector2I = Vector.Vector2I;
using static IBoard;
public abstract class AbstractBoard : IBoard {
private readonly int rows;
private readonly int cols;
protected readonly IPiece?[,] pieces;
protected readonly List<MoveRecord> moveRecords = new();
protected readonly int MAX_RECORDS;
public abstract class AbstractBoard(int rows, int cols, int maxRecords = int.MaxValue,
IBoardOn? on = null) : IBoard {
private readonly int rows = rows;
private readonly int cols = cols;
protected readonly IPiece?[,] pieces = new IPiece[rows, cols];
protected readonly List<MoveRecord> moveRecords = [];
protected readonly int MAX_RECORDS = maxRecords;
public int Rows => rows;
public int Cols => cols;
protected IBoardOn? on = on;
public event EventHandler<SetPieceEventArgs>? OnSetPiece;
public event EventHandler<IPiece>? OnInsert;
public event EventHandler<IPiece>? OnRemove;
public event EventHandler<MoveEventArgs>? OnMove;
public event EventHandler<MoveRecord>? OnAddRecord;
public event EventHandler<MoveRecord>? OnUndoRecord;
public AbstractBoard(int rows, int cols, int maxRecords = int.MaxValue) {
this.rows = rows;
this.cols = cols;
pieces = new IPiece[rows, cols];
MAX_RECORDS = maxRecords;
}
public virtual IBoardOn? On { get => on; set => on = value; }
public virtual bool IsPosOutOfRange(Vector2I arrayPos) {
return arrayPos.X < 0 || arrayPos.X >= Rows || arrayPos.Y < 0 || arrayPos.Y >= Cols;
@ -42,19 +30,20 @@ public abstract class AbstractBoard : IBoard {
IPiece? oldPiece = pieces[pos.X, pos.Y];
pieces[pos.X, pos.Y] = piece;
if (piece != null) piece.Pos = pos;
if (piece is not null) piece.Pos = pos;
// if (oldPiece != null) oldPiece.Pos = Vector2I.Zero;
OnSetPiece?.Invoke(this, new SetPieceEventArgs { OldPiece = oldPiece, NewPiece = piece, Pos = pos });
on?.OnSetPieceInteral(this, new IBoardOn.SetPieceEventArgs
(oldPiece, piece, pos));
return oldPiece;
}
public virtual bool InsertPiece(IPiece piece, Vector2I pos) {
if (GetPiece(pos) != null && piece == null) {
if (GetPiece(pos) is not null && piece == null) {
return false;
}
OnInsert?.Invoke(this, piece);
on?.OnInsert(this, piece);
SetPiece(piece, pos);
return true;
}
@ -65,11 +54,11 @@ public abstract class AbstractBoard : IBoard {
}
IPiece? piece = GetPiece(from);
if (GetPiece(to) != null || piece == null) {
if (GetPiece(to) is not null || piece is null) {
return false;
}
OnMove?.Invoke(this, new MoveEventArgs { From = from, To = to, Piece = piece });
on?.OnMove(this, new IBoardOn.MoveEventArgs(piece, from, to));
SetPiece(null, from);
SetPiece(piece, to);
return true;
@ -77,8 +66,9 @@ public abstract class AbstractBoard : IBoard {
public virtual IPiece? RemovePiece(Vector2I pos) {
IPiece? piece = GetPiece(pos);
if (piece == null) return null;
OnRemove?.Invoke(this, piece);
if (piece is null) return null;
on?.OnRemove(this, piece);
return SetPiece(null, pos);
}
@ -103,7 +93,7 @@ public abstract class AbstractBoard : IBoard {
}
MoveRecord record = new(From, To, FromPos, ToPos);
OnAddRecord?.Invoke(this, record);
on?.OnAddRecord(this, record);
moveRecords.Add(record);
}
@ -113,29 +103,15 @@ public abstract class AbstractBoard : IBoard {
moveRecords.RemoveAt(moveRecords.Count - 1);
// 恢复新位置的棋子order is very inmportant
if (record.From != null) {
if (record.From is not null) {
MovePiece(record.ToPos, record.FromPos);
}
// 恢复旧位置的棋子
if (record.To != null) {
if (record.To is not null) {
InsertPiece(record.To, record.ToPos);
}
OnUndoRecord?.Invoke(this, record);
}
public class MoveRecord {
public IPiece? From { get; }
public IPiece? To { get; }
public Vector2I FromPos { get; }
public Vector2I ToPos { get; }
public MoveRecord(IPiece? From, IPiece? To, Vector2I FromPos, Vector2I ToPos) {
this.From = From;
this.To = To;
this.ToPos = ToPos;
this.FromPos = FromPos;
}
on?.OnUndoRecord(this, record);
}
}

View File

@ -1,62 +1,61 @@
using System;
using System.Collections.Generic;
using Vector2I = Vector.Vector2I;
public abstract class AbstractPiece : IPiece {
private Vector2I pos; // 注意这个坐标的非像素坐标而是棋盘坐标
using static IPiece;
public abstract class AbstractPiece(string name = "", Vector2I? pos = null,
IPieceOn? on = null) : IPiece {
private Vector2I pos = pos ?? new(); // 注意这个坐标的非像素坐标而是棋盘坐标
private bool isSelected = false;
protected string name;
private Dictionary<string, object> data = new();
protected string name = name;
private Dictionary<string, object> data = [];
protected IPieceOn? on = on;
public Vector2I Pos {
public virtual IPieceOn? On { get => on; set => on = value; }
public virtual Vector2I Pos {
get => pos;
set {
if (pos != value) {
var oldPos = pos;
pos = value;
OnPos?.Invoke(this, oldPos);
on?.OnPos(this, oldPos);
}
}
}
public bool IsSelected {
public virtual bool IsSelected {
get => isSelected;
set {
if (isSelected != value) {
var oldIsSelected = isSelected;
isSelected = value;
OnSelected?.Invoke(this, oldIsSelected);
on?.OnSelected(this, oldIsSelected);
}
}
}
public string Name {
public virtual string Name {
get => name;
set {
if (name != value) {
var oldName = name;
name = value;
OnName?.Invoke(this, oldName);
on?.OnName(this, oldName);
}
}
}
public Dictionary<string, object> Data {
public virtual Dictionary<string, object> Data {
get => data;
set => data = value;
}
public event EventHandler<Vector2I>? OnPos;
public event EventHandler<Vector2I>? OnMove;
public event EventHandler<bool>? OnSelected;
public event EventHandler<string>? OnName;
public virtual bool Move(Vector2I pos) {
if (!CanMove(pos)) {
return false;
}
Pos = pos;
OnMove?.Invoke(this, pos);
on?.OnMove(this, pos);
return true;
}
@ -65,9 +64,4 @@ public abstract class AbstractPiece : IPiece {
public override string ToString() {
return $"{Name} at {pos}";
}
public AbstractPiece(string name = "", Vector2I? pos = null) {
this.name = name;
this.pos = pos ?? new();
}
}

View File

@ -1,24 +1,29 @@
using System;
using System.Collections;
using static ChineseChess.ChessCore;
using Vector2I = Vector.Vector2I;
namespace ChineseChess;
public class CCBoard : AbstractBoard
{
public event EventHandler<int>? OnStepsChanged;
public interface ICCBoardOn : IBoard.IBoardOn {
void OnSteps(object self, int oldSteps) {}
}
public class CCBoard(ICCBoardOn? on = null) : AbstractBoard(9, 10, on: on) {
private int steps = 0;
protected new ICCBoardOn? on = on;
// public virtual IPieceOn? On { get => on; set => on = value; }
public new ICCBoardOn? On { get => on; set {
on = value; base.on = value;} }
public int Steps {
get => steps;
protected set {
if (steps != value) {
var oldSteps = steps;
steps = value;
OnStepsChanged?.Invoke(this, steps);
on?.OnSteps(this, oldSteps);
}
}
}
public CCBoard() : base(9, 10) {
}
public override void AddRecord(IPiece? From, IPiece? To, Vector2I FromPos, Vector2I ToPos) {
base.AddRecord(From, To, FromPos, ToPos);
@ -32,65 +37,49 @@ public class CCBoard : AbstractBoard
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 blackPart = InitOnePartPieces(TurnsSideType.Black, [
new CCChariot (this, TurnsSideType.Black, new Vector2I( 4, 0)),
new CCHorse (this, TurnsSideType.Black, new Vector2I( 3, 0)),
new CCElephant(this, TurnsSideType.Black, new Vector2I( 2, 0)),
new CCAdvisor (this, TurnsSideType.Black, new Vector2I( 1, 0)),
new CCGeneral (this, TurnsSideType.Black, new Vector2I( 0, 0)),
new CCAdvisor (this, TurnsSideType.Black, new Vector2I(-1, 0)),
new CCElephant(this, TurnsSideType.Black, new Vector2I(-2, 0)),
new CCHorse (this, TurnsSideType.Black, new Vector2I(-3, 0)),
new CCChariot (this, TurnsSideType.Black, new Vector2I(-4, 0)),
new CCCannon (this, TurnsSideType.Black, new Vector2I( 3, 2)),
new CCCannon (this, TurnsSideType.Black, new Vector2I(-3, 2)),
new CCPawn (this, TurnsSideType.Black, new Vector2I(-4, 3)),
new CCPawn (this, TurnsSideType.Black, new Vector2I(-2, 3)),
new CCPawn (this, TurnsSideType.Black, new Vector2I( 0, 3)),
new CCPawn (this, TurnsSideType.Black, new Vector2I( 2, 3)),
new CCPawn (this, TurnsSideType.Black, new Vector2I( 4, 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
});
ArrayList redPart = InitOnePartPieces(TurnsSideType.Red, [
new CCChariot (this, TurnsSideType.Red, new Vector2I( 4, 0)),
new CCHorse (this, TurnsSideType.Red, new Vector2I( 3, 0)),
new CCElephant(this, TurnsSideType.Red, new Vector2I( 2, 0)),
new CCAdvisor (this, TurnsSideType.Red, new Vector2I( 1, 0)),
new CCGeneral (this, TurnsSideType.Red, new Vector2I( 0, 0)),
new CCAdvisor (this, TurnsSideType.Red, new Vector2I(-1, 0)),
new CCElephant(this, TurnsSideType.Red, new Vector2I(-2, 0)),
new CCHorse (this, TurnsSideType.Red, new Vector2I(-3, 0)),
new CCChariot (this, TurnsSideType.Red, new Vector2I(-4, 0)),
new CCCannon (this, TurnsSideType.Red, new Vector2I( 3, 2)),
new CCCannon (this, TurnsSideType.Red, new Vector2I(-3, 2)),
new CCPawn (this, TurnsSideType.Red, new Vector2I(-4, 3)),
new CCPawn (this, TurnsSideType.Red, new Vector2I(-2, 3)),
new CCPawn (this, TurnsSideType.Red, new Vector2I( 0, 3)),
new CCPawn (this, TurnsSideType.Red, new Vector2I( 2, 3)),
new CCPawn (this, TurnsSideType.Red, new Vector2I( 4, 3)),
]);
return (blackPart, redPart);
}
private ArrayList InitOnePartPieces(TurnsSideType side, CCPiece[] pieces) {
ArrayList list = new();
ArrayList list = [];
foreach (var piece in pieces) {
list.Add(piece);
InsertPiece(piece, piece.Pos);

View File

@ -1,6 +1,8 @@
using Vector2 = Vector.Vector2I;
using System;
using System.Collections;
using static IBoard.IBoardOn;
namespace ChineseChess;
public class ChessCore {
@ -23,17 +25,18 @@ public class ChessCore {
private TurnsSideType sideType = TurnsSideType.Red;
private readonly TurnsSideType selfSide;
public readonly CCBoard board = new();
public readonly CCBoard board;
private readonly Player playerSelf;
private readonly Player playerOpponent;
// public EventHandler<IBoard.MoveEventArgs> OnMove;
public ChessCore(Mode mode, TurnsSideType selfSide) {
public ChessCore(Mode mode, TurnsSideType selfSide, ICCBoardOn? boardOn = null) {
this.selfSide = selfSide;
board = new(boardOn);
playerSelf = new(board, Player.PlayerType.Human);
playerOpponent = new(board, Player.PlayerType.Human);
void func(object? _self, IBoard.MoveEventArgs record) {
void func(object? _self, MoveEventArgs record) {
// 防止 Undo 时 Selected Clear 出现 Null Pointer Exception
playerSelf.SelectedClear();
playerOpponent.SelectedClear();

View File

@ -1,6 +1,5 @@
namespace ChineseChess;
using System;
using System.Collections.Generic;
using System.Linq;
using Vector;
@ -20,11 +19,14 @@ public class CCPiece : AbstractPiece {
string name = "", Vector2I? pos = null) : base(name) {
this.board = board ?? new();
TurnsSide = turnsSide;
OnPos += (sender, args) => {
localPos = Global2Local(Pos);
};
Pos = pos is not null ? Local2Global(pos) : Vector2I.Zero;
localPos = Global2Local(Pos);
}
public override Vector2I Pos {
get => base.Pos; set {
base.Pos = value;
localPos = Global2Local(Pos);
}
}
public override bool CanMove(Vector2I to) {
@ -44,18 +46,18 @@ public class CCPiece : AbstractPiece {
}
public virtual List<Vector2I> CanMoveAllPosSelf() {
return new List<Vector2I>();
return [];
}
public IEnumerable<Vector2I> CanMoveAllPosLocal() {
var self = CanMoveAllPosSelf().Select(item => item + localPos);// 转换局部坐标
var self = CanMoveAllPosSelf().Select(item => item + localPos);
var ret = self.Where(item => {
CCPiece? piece = GetCCPieceLocal(item);
bool ret = piece is null || piece.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));
@ -64,7 +66,7 @@ public class CCPiece : AbstractPiece {
public IEnumerable<Vector2I> CanMoveAllPos() {
var ret = CanMoveAllPosLocal()
.Select(item => Local2Global(item)); // 转换为全局坐标
.Select(item => Local2Global(item));
// Console.WriteLine("CanMoveAllPos: {0}", string.Join(",", ret));
return ret;
}

View File

@ -3,11 +3,12 @@ using System.Collections;
using Vector2I = Vector.Vector2I;
namespace ChineseChess;
using static IBoard.IBoardOn;
public class Player {
private readonly CCBoard board;
private readonly SelectedPiece selectedNode;
public EventHandler<IBoard.MoveEventArgs>? OnMove;
public EventHandler<MoveEventArgs>? OnMove;
public bool CanMove { get; set; } = true;
public enum PlayerType {
@ -15,8 +16,7 @@ public class Player {
AI
}
public Player(CCBoard board, PlayerType type = PlayerType.Human)
{
public Player(CCBoard board, PlayerType type = PlayerType.Human) {
this.board = board;
this.selectedNode = new SelectedPiece(board);
}
@ -56,9 +56,9 @@ public class Player {
}
// MUST BE THERE !!! 防止删除节点后在启动回调导致错误
OnMove?.Invoke(this, new IBoard.MoveEventArgs
{ From = fromPos, To = toPos, Piece = fromChess });;
OnMove?.Invoke(this, new MoveEventArgs
(fromChess, fromPos, toPos));
if (toChess != null) {
board.RemovePiece(toPos);
}

View File

@ -1,32 +1,33 @@
using System;
using Godot;
using Vector2I = Vector.Vector2I;
public interface IBoard {
public interface IBoardOn {
void OnInsert(object self, IPiece piece) {}
void OnRemove(object self, IPiece piece) {}
void OnMove(object self, MoveEventArgs args) {}
void OnAddRecord(object self, MoveRecord record) {}
void OnUndoRecord(object self, MoveRecord record) {}
void OnSetPieceInteral(object self, SetPieceEventArgs args) {}
record SetPieceEventArgs(IPiece? OldPiece, IPiece? NewPiece, Vector2I Pos);
record MoveEventArgs(IPiece? Piece, Vector2I From, Vector2I To);
}
public IBoardOn? On { get; protected set; }
public int Rows { get; }
public int Cols { get; }
public class SetPieceEventArgs : EventArgs {
public IPiece? OldPiece { get; set; }
public IPiece? NewPiece { get; set; }
public Vector2I Pos { get; set; } = new();
}
public class MoveEventArgs : EventArgs {
public IPiece? Piece { get; set; }
public Vector2I From { get; set; } = new();
public Vector2I To { get; set; } = new();
}
event EventHandler<IPiece> OnInsert;
event EventHandler<IPiece> OnRemove;
event EventHandler<MoveEventArgs> OnMove;
event EventHandler<SetPieceEventArgs> OnSetPiece;
bool IsPosOutOfRange(Vector2I pos);
IPiece? GetPiece(Vector2I pos);
IPiece? SetPiece(IPiece piece, Vector2I pos);
IPiece? SetPiece(IPiece? piece, Vector2I pos);
bool InsertPiece(IPiece piece, Vector2I pos);
bool MovePiece(Vector2I from, Vector2I to);
IPiece? RemovePiece(Vector2I pos);
void Clear();
void Clear(bool clearRecords);
void AddRecord(IPiece? From, IPiece? To, Vector2I FromPos, Vector2I ToPos);
void UndoRecord();
record MoveRecord(IPiece? From, IPiece? To, Vector2I FromPos, Vector2I ToPos);
}

View File

@ -2,17 +2,21 @@ using System;
using System.Collections.Generic;
using Vector2I = Vector.Vector2I;
public interface IPiece {
public interface IPieceOn {
void OnPos(object self, Vector2I oldPos) {}
void OnMove(object self, Vector2I oldPos) {}
void OnSelected(object self, bool oldIsSelected) {}
void OnName(object self, string oldName) {}
}
IPieceOn? On { get; protected set; }
Vector2I Pos { get; set; }
string Name { get; set; }
bool IsSelected { get; set; }
Dictionary<string, object> Data { get; set; }
event EventHandler<Vector2I> OnPos;
event EventHandler<Vector2I> OnMove;
event EventHandler<bool> OnSelected;
event EventHandler<string> OnName;
bool CanMove(Vector2I to);
bool Move(Vector2I pos);
}