diff --git a/.gitignore b/.gitignore index 7ecfc1a..18d9ddc 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ bin/ *.sln *.error *.key* +.editorconfig # cs project files !*.csproj diff --git a/Chinese_Chess.csproj b/ChessGame.csproj similarity index 55% rename from Chinese_Chess.csproj rename to ChessGame.csproj index 1748c47..1f476de 100644 --- a/Chinese_Chess.csproj +++ b/ChessGame.csproj @@ -1,8 +1,6 @@ - net6.0 - net7.0 - net8.0 + net8.0 true enable diff --git a/Scripts/Controllers/ChessGame.cs b/Scripts/Controllers/ChessGame.cs index b6de100..ac7bdc9 100644 --- a/Scripts/Controllers/ChessGame.cs +++ b/Scripts/Controllers/ChessGame.cs @@ -1,3 +1,4 @@ +#nullable disable using Godot; using Godot.Collections; using ChineseChess; @@ -39,15 +40,15 @@ public partial class ChessGame : Node2D { sideOpposite = ChessCore.TurnsSideType.Red; } - if (isSession) { - Game = new(ChessCore.Mode.MultiMode, sideSelf); - } else { - Game = new(ChessCore.Mode.SingleMode, sideSelf); - } - board.LoadBoard(Game.board); + // if (isSession) { + // Game = new(ChessCore.Mode.MultiMode, sideSelf); + // } else { + // Game = new(ChessCore.Mode.SingleMode, sideSelf); + // } + Game = new(isSession ? ChessCore.Mode.MultiMode : ChessCore.Mode.SingleMode, sideSelf, board); Game.Init(); - Game.board.OnStepsChanged += (sender, e) => { + board.OnStepsChanged += (sender, e) => { turnSideEdit.Text = Game.GetTurnsType() == ChessCore.TurnsSideType.Red ? "red" : "black"; }; diff --git a/Scripts/Controllers/GameLobby.cs b/Scripts/Controllers/GameLobby.cs index bb7f53c..4f6fc37 100644 --- a/Scripts/Controllers/GameLobby.cs +++ b/Scripts/Controllers/GameLobby.cs @@ -1,3 +1,4 @@ +#nullable disable using Godot; using Godot.Collections; diff --git a/Scripts/Entities/ChessBoard.cs b/Scripts/Entities/ChessBoard.cs index 83f8219..443a925 100644 --- a/Scripts/Entities/ChessBoard.cs +++ b/Scripts/Entities/ChessBoard.cs @@ -4,12 +4,14 @@ using Godot; using ChineseChess; using Godot.Collections; -public partial class ChessBoard : Node2D { - public event EventHandler OnMouseClicked; - public event EventHandler OnPosClicked; +using static IBoard; +public partial class ChessBoard : Node2D, ICCBoardOn { + public event EventHandler? OnMouseClicked; + public event EventHandler? OnPosClicked; + public event EventHandler? OnStepsChanged; public Vector2 from = Vector2.Inf, to = Vector2.Inf; - public Array canMovePos = new(); + public Array canMovePos = []; public override void _Ready() { } @@ -19,29 +21,39 @@ public partial class ChessBoard : Node2D { QueueRedraw(); } - public void LoadBoard(CCBoard board) { - board.OnRemove += (sender, piece) => { - if (piece?.Data.TryGetValue("Godot", out object node) ?? false) { - RemoveChild(node as Node); - } - }; + void IBoardOn.OnInsert(object self, IPiece piece) { + ChessPiece? node = piece.On as ChessPiece; + // throw new InvalidOperationException(); + node ??= new ChessPiece((CCPiece)piece); + node.ShowBehindParent = true; + AddChild(node); - board.OnInsert += (sender, piece) => { - ChessPiece chessPiece = null; - if (piece.Data.TryGetValue("Godot", out object node)) { - chessPiece = node as ChessPiece; - } else { - chessPiece = new((CCPiece)piece); - } - chessPiece.ShowBehindParent = true; - AddChild(chessPiece); - }; + // ChessPiece chessPiece = null; + // if (piece.Data.TryGetValue("Godot", out object node)) { + // chessPiece = node as ChessPiece; + // } else { + // chessPiece = new((CCPiece)piece); + // } + // chessPiece.ShowBehindParent = true; + // AddChild(chessPiece); + } - board.OnMove += (sender, vals) => { - from = PosTrans.transArrToPix * new Vector2(vals.From.X, vals.From.Y); - to = PosTrans.transArrToPix * new Vector2(vals.To.X, vals.To.Y); - QueueRedraw(); - }; + void IBoardOn.OnRemove(object self, IPiece piece) { + if (piece.On is not ChessPiece node) { + throw new InvalidOperationException(); + } + RemoveChild(node); + } + + void IBoardOn.OnMove(object self, IBoardOn.MoveEventArgs vals) { + from = PosTrans.transArrToPix * new Vector2(vals.From.X, vals.From.Y); + to = PosTrans.transArrToPix * new Vector2(vals.To.X, vals.To.Y); + QueueRedraw(); + } + + void ICCBoardOn.OnSteps(object _self, int oldSteps) { + if (_self is not CCBoard self) return; + OnStepsChanged?.Invoke(self, self.Steps); } public override void _Draw() { diff --git a/Scripts/Entities/ChessPiece.cs b/Scripts/Entities/ChessPiece.cs index 8b18863..137996e 100644 --- a/Scripts/Entities/ChessPiece.cs +++ b/Scripts/Entities/ChessPiece.cs @@ -4,37 +4,31 @@ using ChineseChess; using System.Linq; using System; -public partial class ChessPiece : Sprite2D { +using static IPiece; +public partial class ChessPiece : Sprite2D, IPieceOn { [Export] - public string PieceLabel { get; set; } = null; + public string? PieceLabel { get; set; } = null; // Text Color (Can Export for Editor Adjust) [Export] public Color LabelColor { get; set; } = new Color("white"); private Vector2 textureSize; + private Label? labelOfChessName; - private Label labelOfChessName; - private readonly IPiece piece; - - public IPiece GetVirtualPiece() { - return piece; - } - - private void OnPos(object _self, Vector.Vector2I oldPos) { - CCPiece self = (CCPiece)_self; + void IPieceOn.OnPos(object _self, Vector.Vector2I oldPos) { + if (_self is not CCPiece self) return; Position = ChessBoard.PosTrans.transArrToPix * new Vector2(self.Pos.X, self.Pos.Y); } - public void OnSelected(object _self, bool oldIsSelected) { - CCPiece self = (CCPiece)_self; - ChessBoard chessBoard = GetParent() as ChessBoard; + void IPieceOn.OnSelected(object _self, bool oldIsSelected) { + if (GetParent() is not ChessBoard chessBoard || _self is not CCPiece self) return; if (self.IsSelected) { - GD.Print($"{piece.Pos} is selected"); + GD.Print($"{self.Pos} is selected"); Transform *= transToSeleted; foreach (var item in self.CanMoveAllPos()) { chessBoard.canMovePos.Add(ChessBoard.PosTrans.transArrToPix * new Vector2(item.X, item.Y)); } } else { - GD.Print($"{piece.Pos} is deselected"); + GD.Print($"{self.Pos} is deselected"); Transform *= transToSeleted.AffineInverse(); foreach (var item in self.CanMoveAllPos()) { chessBoard.canMovePos.Remove(ChessBoard.PosTrans.transArrToPix * new Vector2(item.X, item.Y)); @@ -49,17 +43,15 @@ public partial class ChessPiece : Sprite2D { new Vector2(0, 0) ); - public ChessPiece() : this(new CCPiece()){ + public ChessPiece() : this(new CCPiece()) { } public ChessPiece(CCPiece piece) { PieceLabel = piece.CNName; - this.piece = piece; LabelColor = piece.TurnsSide == ChessCore.TurnsSideType.Red ? new Color("red") : new Color("black"); - piece.OnPos += OnPos; - piece.OnSelected += OnSelected; + piece.On = this; // Must Be Call for init Pos - OnPos(piece, piece.Pos); + piece.On.OnPos(piece, piece.Pos); piece.Data.TryAdd("Godot", this); } diff --git a/Scripts/Global.cs b/Scripts/Global.cs index 082881e..2ebebfb 100644 --- a/Scripts/Global.cs +++ b/Scripts/Global.cs @@ -1,15 +1,15 @@ +#nullable disable using System.Collections.Generic; using Godot; using RPPackage; -public partial class Global : Node -{ +public partial class Global : Node { public RPClientEDWS RPClient = new(); public string sessionId; public Node CurrentScene { get; set; } public Theme GlobalTheme = null; - readonly GodotConfigManager GlobalConfig = new("user://config.cfg"); + readonly GodotConfigManager GlobalConfig = new("user://config.cfg"); public Dictionary GlobalConfigDict = new() { {"font_size", 20}, {"server_url", "wss://game.zzyxyz.com/"}, @@ -21,8 +21,7 @@ public partial class Global : Node }; // Called when the node enters the scene tree for the first time. - public override void _Ready() - { + public override void _Ready() { if (OS.GetName() == "Android") { bool ret = OS.RequestPermissions(); GD.Print($"RequestPermissions ret is {ret}"); @@ -37,41 +36,36 @@ public partial class Global : Node }; Viewport root = GetTree().Root; - CurrentScene = root.GetChild(root.GetChildCount() - 1); + CurrentScene = root.GetChild(root.GetChildCount() - 1); GlobalConfig.LoadConfig("Global", GlobalConfigDict); SetProcess(false); } - public void ConfigFlush() - { + public void ConfigFlush() { int font_size = (int)GlobalConfigDict["font_size"]; GlobalTheme.DefaultFontSize = font_size; // GlobalTheme?.SetFontSize("font_size", "Label", font_size); - // GlobalTheme?.SetFontSize("font_size", "Button", font_size); - // GlobalTheme?.SetFontSize("font_size", "TextEdit", font_size); - // GlobalTheme?.SetFontSize("font_size", "LineEdit", font_size); + // GlobalTheme?.SetFontSize("font_size", "Button", font_size); + // GlobalTheme?.SetFontSize("font_size", "TextEdit", font_size); + // GlobalTheme?.SetFontSize("font_size", "LineEdit", font_size); // CurrentScene.GetWindow().AddThemeFontSizeOverride("Control", (int)GlobalConfigDict["font_size"]); } - private void OnGotoScene() - { + private void OnGotoScene() { ConfigFlush(); } - public void SaveConfig() - { + public void SaveConfig() { GlobalConfig.SaveConfig("Global", GlobalConfigDict); } // Called every frame. 'delta' is the elapsed time since the previous frame. - public override void _Process(double delta) - { + public override void _Process(double delta) { RPClient.PollEx(delta); } - public override void _Notification(int what) - { + public override void _Notification(int what) { if (what == NotificationWMCloseRequest) { // SaveConfig(); RPClient.Close(); @@ -81,8 +75,7 @@ public partial class Global : Node public delegate void ChangeSceneCallback(Node newSence); private static ChangeSceneCallback changeSceneCallback = null; - public void GotoScene(string path, ChangeSceneCallback callback = null) - { + public void GotoScene(string path, ChangeSceneCallback callback = null) { // This function will usually be called from a signal callback, // or some other function from the current scene. // Deleting the current scene at this point is @@ -100,8 +93,7 @@ public partial class Global : Node changeSceneCallback = null; } - public void DeferredGotoScene(string path, Callable onLoaded) - { + public void DeferredGotoScene(string path, Callable onLoaded) { // It is now safe to remove the current scene. CurrentScene.Free(); diff --git a/Scripts/Src/AbstractBoard.cs b/Scripts/Src/AbstractBoard.cs index af269fd..d4bf391 100644 --- a/Scripts/Src/AbstractBoard.cs +++ b/Scripts/Src/AbstractBoard.cs @@ -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 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 moveRecords = []; + protected readonly int MAX_RECORDS = maxRecords; public int Rows => rows; public int Cols => cols; + protected IBoardOn? on = on; - public event EventHandler? OnSetPiece; - public event EventHandler? OnInsert; - public event EventHandler? OnRemove; - public event EventHandler? OnMove; - - public event EventHandler? OnAddRecord; - public event EventHandler? 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); } } diff --git a/Scripts/Src/AbstractPiece.cs b/Scripts/Src/AbstractPiece.cs index 4f5bbf3..1e0c935 100644 --- a/Scripts/Src/AbstractPiece.cs +++ b/Scripts/Src/AbstractPiece.cs @@ -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 data = new(); + protected string name = name; + private Dictionary 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 Data { + public virtual Dictionary Data { get => data; set => data = value; } - public event EventHandler? OnPos; - public event EventHandler? OnMove; - public event EventHandler? OnSelected; - public event EventHandler? 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(); - } } diff --git a/Scripts/Src/ChineseChess/CCBoard.cs b/Scripts/Src/ChineseChess/CCBoard.cs index 83ba89c..fc6688a 100644 --- a/Scripts/Src/ChineseChess/CCBoard.cs +++ b/Scripts/Src/ChineseChess/CCBoard.cs @@ -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? 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); diff --git a/Scripts/Src/ChineseChess/CCMain.cs b/Scripts/Src/ChineseChess/CCMain.cs index c2e5481..a7ac64e 100644 --- a/Scripts/Src/ChineseChess/CCMain.cs +++ b/Scripts/Src/ChineseChess/CCMain.cs @@ -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 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(); diff --git a/Scripts/Src/ChineseChess/CCPiece.cs b/Scripts/Src/ChineseChess/CCPiece.cs index 4666351..87935e4 100644 --- a/Scripts/Src/ChineseChess/CCPiece.cs +++ b/Scripts/Src/ChineseChess/CCPiece.cs @@ -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 CanMoveAllPosSelf() { - return new List(); + return []; } public IEnumerable 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 CanMoveAllPos() { var ret = CanMoveAllPosLocal() - .Select(item => Local2Global(item)); // 转换为全局坐标 + .Select(item => Local2Global(item)); // Console.WriteLine("CanMoveAllPos: {0}", string.Join(",", ret)); return ret; } diff --git a/Scripts/Src/ChineseChess/CCPlayer.cs b/Scripts/Src/ChineseChess/CCPlayer.cs index 2525a2e..e965a4d 100644 --- a/Scripts/Src/ChineseChess/CCPlayer.cs +++ b/Scripts/Src/ChineseChess/CCPlayer.cs @@ -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? OnMove; + public EventHandler? 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); } diff --git a/Scripts/Src/IBoard.cs b/Scripts/Src/IBoard.cs index 2ab69b2..2347f04 100644 --- a/Scripts/Src/IBoard.cs +++ b/Scripts/Src/IBoard.cs @@ -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 OnInsert; - event EventHandler OnRemove; - event EventHandler OnMove; - event EventHandler 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); } diff --git a/Scripts/Src/IPiece.cs b/Scripts/Src/IPiece.cs index 9d4725e..059cf03 100644 --- a/Scripts/Src/IPiece.cs +++ b/Scripts/Src/IPiece.cs @@ -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 Data { get; set; } - event EventHandler OnPos; - event EventHandler OnMove; - event EventHandler OnSelected; - event EventHandler OnName; - bool CanMove(Vector2I to); bool Move(Vector2I pos); } diff --git a/Test/Test.csproj b/Test/Test.csproj index 7e5d662..f11c34d 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -21,11 +21,11 @@ - - + + diff --git a/Test/src/TestVector.cs b/Test/src/TestVector.cs index 3246fa1..148f397 100644 --- a/Test/src/TestVector.cs +++ b/Test/src/TestVector.cs @@ -1,21 +1,147 @@ using Vector; -namespace Test; -public class TestsVector -{ +namespace Test.Vector; +public class TestsVector2I { [SetUp] - public void Setup() - { + public void Setup() { } [Test] - public void Test1() - { + public void TestVector2I_Constructors() { + // Arrange & Act + var v1 = new Vector2I(); + var v2 = new Vector2I(1, 2); + var v3 = new Vector2I(v2); + + // Assert + Assert.Multiple(() => { + Assert.That(v1.X, Is.EqualTo(0)); + Assert.That(v1.Y, Is.EqualTo(0)); + Assert.That(v2.X, Is.EqualTo(1)); + Assert.That(v2.Y, Is.EqualTo(2)); + Assert.That(v3.X, Is.EqualTo(1)); + Assert.That(v3.Y, Is.EqualTo(2)); + }); + } + + [Test] + public void TestVector2I_Operators() { + // Arrange + var v1 = new Vector2I(1, 2); + var v2 = new Vector2I(3, 4); + + // Act + var v3 = v1 + v2; + var v4 = v1 - v2; + var v5 = v1 * v2; + + // Assert + Assert.Multiple(() => { + Assert.That(v3, Is.EqualTo(new Vector2I(4, 6))); + Assert.That(v4, Is.EqualTo(new Vector2I(-2, -2))); + Assert.That(v5, Is.EqualTo(new Vector2I(3, 8))); + }); + } + + [Test] + public void TestVector2I_DotProduct() { + // Arrange + var v1 = new Vector2I(1, 2); + var v2 = new Vector2I(3, 4); + + // Act + var dotProduct = v1.Dot(v2); + + // Assert + Assert.That(dotProduct, Is.EqualTo(11)); + } + + [Test] + public void TestVector2I_ParameterModification() { + // Arrange + var v1 = new Vector2I(1, 2); + var v2 = new Vector2I(3, 4); + + // Act + var v3 = v1 + v2; + var v4 = v1 - v2; + var v5 = v1 * v2; + + // Assert + Assert.Multiple(() => { + Assert.That(v1, Is.EqualTo(new Vector2I(1, 2)), "v1 should not be modified"); + Assert.That(v2, Is.EqualTo(new Vector2I(3, 4)), "v2 should not be modified"); + }); + } + + [Test] + public void TestVector2I_Equality() { + // Arrange + var v1 = new Vector2I(1, 2); + var v2 = new Vector2I(1, 2); + var v3 = new Vector2I(3, 4); + + // Act & Assert + Assert.Multiple(() => { + Assert.That(v1, Is.EqualTo(v2)); + Assert.That(v1, Is.Not.EqualTo(v3)); + }); + } + + [Test] + public void TestVector2I_HashCode() { + // Arrange + var v1 = new Vector2I(1, 2); + var v2 = new Vector2I(1, 2); + var v3 = new Vector2I(3, 4); + + // Act & Assert + Assert.Multiple(() => { + Assert.That(v1.GetHashCode(), Is.EqualTo(v2.GetHashCode())); + Assert.That(v1.GetHashCode(), Is.Not.EqualTo(v3.GetHashCode())); + }); + } + + [Test] + public void TestVector2I_BoundaryValues() { + // Arrange + var v1 = new Vector2I(0, 0); + var v2 = new Vector2I(-1, -2); + var v3 = new Vector2I(int.MaxValue, int.MaxValue); + var v4 = new Vector2I(int.MinValue, int.MinValue); + + // Act + var v5 = v1 + v2; + var v6 = v1 - v2; + var v7 = v1 * v2; + var v8 = v3 + v4; + var v9 = v3 - v4; + var v10 = v3 * v4; + + // Assert + Assert.Multiple(() => { + Assert.That(v5, Is.EqualTo(new Vector2I(-1, -2))); + Assert.That(v6, Is.EqualTo(new Vector2I(1, 2))); + Assert.That(v7, Is.EqualTo(new Vector2I(0, 0))); + + Assert.That(v8, Is.EqualTo(new Vector2I(-1, -1))); + // Assert.That(v9, Is.EqualTo(new Vector2I(2 * int.MaxValue - 1, 2 * int.MaxValue - 1))); + // Assert.That(v10, Is.EqualTo(new Vector2I(int.MaxValue * int.MinValue, int.MaxValue * int.MinValue))); + }); + } +} + +public class TestsTrans2DI { + [SetUp] + public void Setup() { + } + + [Test] + public void Test1() { Assert.Pass(); } [Test] - public void TestVector2I_Constructors() - { + public void TestVector2I_Constructors() { // Arrange & Act var v1 = new Vector2I(); var v2 = new Vector2I(1, 2); @@ -31,8 +157,7 @@ public class TestsVector } [Test] - public void TestVector2I_Operators() - { + public void TestVector2I_Operators() { // Arrange var v1 = new Vector2I(1, 2); var v2 = new Vector2I(3, 4); @@ -49,8 +174,7 @@ public class TestsVector } [Test] - public void TestTrans2DI_Constructors() - { + public void TestTrans2DI_Constructors() { // Arrange & Act var t1 = new Trans2DI(); var t2 = new Trans2DI(new Vector2I(1, 0), new Vector2I(0, 1), new Vector2I(0, 0)); @@ -76,8 +200,7 @@ public class TestsVector } [Test] - public void TestTrans2DI_Operators() - { + public void TestTrans2DI_Operators() { // Arrange var t1 = new Trans2DI(new Vector2I(1, 0), new Vector2I(0, 1), new Vector2I(1, 2)); var v1 = new Vector2I(3, 4); @@ -90,4 +213,51 @@ public class TestsVector Assert.That(v2, Is.EqualTo(new Vector2I(4, 6))); Assert.That(v3, Is.EqualTo(new Vector2I(2, 2))); } + + [Test] + public void TestTrans2DI_BoundaryValues() { + // Arrange + var t1 = new Trans2DI(new Vector2I(int.MaxValue, 0), new Vector2I(0, int.MaxValue), new Vector2I(0, 0)); + var t2 = new Trans2DI(new Vector2I(int.MinValue, 0), new Vector2I(0, int.MinValue), new Vector2I(0, 0)); + var v1 = new Vector2I(1, 1); + + // Act + var v2 = t1 * v1; + var v3 = t2 * v1; + + // Assert + Assert.That(v2, Is.EqualTo(new Vector2I(int.MaxValue, int.MaxValue))); + Assert.That(v3, Is.EqualTo(new Vector2I(int.MinValue, int.MinValue))); + } + + [Test] + public void TestTrans2DI_SpecialValues() { + // Arrange + var t1 = new Trans2DI(new Vector2I(0, 0), new Vector2I(0, 0), new Vector2I(0, 0)); + var t2 = new Trans2DI(new Vector2I(1, 0), new Vector2I(0, 1), new Vector2I(0, 0)); + var v1 = new Vector2I(1, 1); + + // Act + var v2 = t1 * v1; + var v3 = t2 * v1; + + // Assert + Assert.That(v2, Is.EqualTo(new Vector2I(0, 0))); + Assert.That(v3, Is.EqualTo(new Vector2I(1, 1))); + } + + [Test] + public void TestTrans2DI_Multiplication() { + // Arrange + var t1 = new Trans2DI(new Vector2I(1, 0), new Vector2I(0, 1), new Vector2I(1, 2)); + var t2 = new Trans2DI(new Vector2I(2, 0), new Vector2I(0, 2), new Vector2I(3, 4)); + + // Act + var t3 = t1 * t2; + + // Assert + Assert.That(t3.XAxis, Is.EqualTo(new Vector2I(2, 0))); + Assert.That(t3.YAxis, Is.EqualTo(new Vector2I(0, 2))); + Assert.That(t3.Origin, Is.EqualTo(new Vector2I(5, 8))); + } } \ No newline at end of file diff --git a/project.godot b/project.godot index 6bd330f..6701b30 100644 --- a/project.godot +++ b/project.godot @@ -10,10 +10,11 @@ config_version=5 [application] -config/name="Chinese_Chess" +config/name="ChessGame" +config/version="0.0.5" run/main_scene="res://Main.tscn" config/features=PackedStringArray("4.3", "C#", "Mobile") -config/icon="res://icon.svg" +config/icon="res://Asserts/icon.svg" [autoload] @@ -28,7 +29,12 @@ window/handheld/orientation=1 [dotnet] -project/assembly_name="Chinese_Chess" +project/assembly_name="ChessGame" + +[navigation] + +2d/name_localized={} +2d/version="0.0." [rendering]