Code smells

This commit is contained in:
2021-03-04 20:35:23 -06:00
parent e64f75e3cc
commit 7ed771d467
31 changed files with 310 additions and 339 deletions

View File

@@ -1,18 +0,0 @@
using System.Numerics;
namespace Gameboard.ShogiUI.BoardState
{
public static class Direction
{
public static readonly Vector2 Up = new Vector2(0, 1);
public static readonly Vector2 Down = new Vector2(0, -1);
public static readonly Vector2 Left = new Vector2(-1, 0);
public static readonly Vector2 Right = new Vector2(1, 0);
public static readonly Vector2 UpLeft = new Vector2(-1, 1);
public static readonly Vector2 UpRight = new Vector2(1, 1);
public static readonly Vector2 DownLeft = new Vector2(-1, -1);
public static readonly Vector2 DownRight = new Vector2(1, -1);
public static readonly Vector2 KnightLeft = new Vector2(-1, 2);
public static readonly Vector2 KnightRight = new Vector2(1, 2);
}
}

View File

@@ -1,19 +0,0 @@
using System;
namespace Gameboard.ShogiUI.BoardState
{
public static class Extensions
{
public static void ForEachNotNull(this Piece[,] array, Action<Piece, int, int> action)
{
for (var x = 0; x < array.GetLength(0); x++)
for (var y = 0; y < array.GetLength(1); y++)
{
var piece = array[x, y];
if (piece != null)
{
action(piece, x, y);
}
}
}
}
}

View File

@@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>5</AnalysisLevel>
</PropertyGroup>
<ItemGroup>

View File

@@ -1,34 +1,32 @@
using PathFinding;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Gameboard.ShogiUI.BoardState.Pieces
{
public class Bishop : Piece
{
private static readonly List<Path> MoveSet = new List<Path>(4)
private static readonly List<PathFinding.Move> Moves = new(4)
{
new Path(Direction.UpLeft, Distance.MultiStep),
new Path(Direction.UpRight, Distance.MultiStep),
new Path(Direction.DownLeft, Distance.MultiStep),
new Path(Direction.DownRight, Distance.MultiStep)
new PathFinding.Move(Direction.UpLeft, Distance.MultiStep),
new PathFinding.Move(Direction.UpRight, Distance.MultiStep),
new PathFinding.Move(Direction.DownLeft, Distance.MultiStep),
new PathFinding.Move(Direction.DownRight, Distance.MultiStep)
};
private static readonly List<Path> PromotedMoveSet = new List<Path>(8)
private static readonly List<PathFinding.Move> PromotedMoves = new(8)
{
new Path(Direction.Up),
new Path(Direction.Left),
new Path(Direction.Right),
new Path(Direction.Down),
new Path(Direction.UpLeft, Distance.MultiStep),
new Path(Direction.UpRight, Distance.MultiStep),
new Path(Direction.DownLeft, Distance.MultiStep),
new Path(Direction.DownRight, Distance.MultiStep)
new PathFinding.Move(Direction.Up),
new PathFinding.Move(Direction.Left),
new PathFinding.Move(Direction.Right),
new PathFinding.Move(Direction.Down),
new PathFinding.Move(Direction.UpLeft, Distance.MultiStep),
new PathFinding.Move(Direction.UpRight, Distance.MultiStep),
new PathFinding.Move(Direction.DownLeft, Distance.MultiStep),
new PathFinding.Move(Direction.DownRight, Distance.MultiStep)
};
public Bishop(WhichPlayer owner) : base(WhichPiece.Bishop, owner)
{
// TODO: If this strat works out, we can do away with the Direction class entirely.
PromotedMoveSet.AddRange(MoveSet);
moveSet = new MoveSet(this, Moves);
promotedMoveSet = new MoveSet(this, PromotedMoves);
}
public override Piece DeepClone()
@@ -37,11 +35,5 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
if (IsPromoted) clone.Promote();
return clone;
}
public override ICollection<Path> GetPaths()
{
var moveSet = IsPromoted ? PromotedMoveSet : MoveSet;
return Owner == WhichPlayer.Player1 ? moveSet : moveSet.Select(_ => new Path(Vector2.Negate(_.Direction), _.Distance)).ToList();
}
}
}

View File

@@ -1,23 +1,23 @@
using PathFinding;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Gameboard.ShogiUI.BoardState.Pieces
{
public class GoldenGeneral : Piece
{
public static readonly List<Path> MoveSet = new List<Path>(6)
public static readonly List<PathFinding.Move> Moves = new(6)
{
new Path(Direction.Up),
new Path(Direction.UpLeft),
new Path(Direction.UpRight),
new Path(Direction.Left),
new Path(Direction.Right),
new Path(Direction.Down)
new PathFinding.Move(Direction.Up),
new PathFinding.Move(Direction.UpLeft),
new PathFinding.Move(Direction.UpRight),
new PathFinding.Move(Direction.Left),
new PathFinding.Move(Direction.Right),
new PathFinding.Move(Direction.Down)
};
public GoldenGeneral(WhichPlayer owner) : base(WhichPiece.GoldenGeneral, owner)
{
moveSet = new MoveSet(this, Moves);
promotedMoveSet = new MoveSet(this, Moves);
}
public override Piece DeepClone()
@@ -26,9 +26,5 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
if (IsPromoted) clone.Promote();
return clone;
}
public override ICollection<Path> GetPaths() => Owner == WhichPlayer.Player1
? MoveSet
: MoveSet.Select(_ => new Path(Vector2.Negate(_.Direction), _.Distance)).ToList();
}
}

View File

@@ -1,25 +1,25 @@
using PathFinding;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Gameboard.ShogiUI.BoardState.Pieces
{
public class King : Piece
{
private static readonly List<Path> MoveSet = new List<Path>(8)
private static readonly List<PathFinding.Move> Moves = new(8)
{
new Path(Direction.Up),
new Path(Direction.Left),
new Path(Direction.Right),
new Path(Direction.Down),
new Path(Direction.UpLeft),
new Path(Direction.UpRight),
new Path(Direction.DownLeft),
new Path(Direction.DownRight)
new PathFinding.Move(Direction.Up),
new PathFinding.Move(Direction.Left),
new PathFinding.Move(Direction.Right),
new PathFinding.Move(Direction.Down),
new PathFinding.Move(Direction.UpLeft),
new PathFinding.Move(Direction.UpRight),
new PathFinding.Move(Direction.DownLeft),
new PathFinding.Move(Direction.DownRight)
};
public King(WhichPlayer owner) : base(WhichPiece.King, owner)
{
moveSet = new MoveSet(this, Moves);
promotedMoveSet = new MoveSet(this, Moves);
}
public override Piece DeepClone()
@@ -28,7 +28,5 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
if (IsPromoted) clone.Promote();
return clone;
}
// The move set for a King is the same regardless of orientation.
public override ICollection<Path> GetPaths() => MoveSet;
}
}

View File

@@ -7,14 +7,16 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
{
public class Knight : Piece
{
private static readonly List<Path> MoveSet = new List<Path>(2)
private static readonly List<PathFinding.Move> Moves = new(2)
{
new Path(Direction.KnightLeft),
new Path(Direction.KnightRight)
new PathFinding.Move(Direction.KnightLeft),
new PathFinding.Move(Direction.KnightRight)
};
public Knight(WhichPlayer owner) : base(WhichPiece.Knight, owner)
{
moveSet = new MoveSet(this, Moves);
promotedMoveSet = new MoveSet(this, GoldenGeneral.Moves);
}
public override Piece DeepClone()
@@ -23,11 +25,5 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
if (IsPromoted) clone.Promote();
return clone;
}
public override ICollection<Path> GetPaths()
{
var moveSet = IsPromoted ? GoldenGeneral.MoveSet : MoveSet;
return Owner == WhichPlayer.Player1 ? moveSet : moveSet.Select(_ => new Path(Vector2.Negate(_.Direction), _.Distance)).ToList();
}
}
}

View File

@@ -1,19 +1,19 @@
using PathFinding;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Gameboard.ShogiUI.BoardState.Pieces
{
public class Lance : Piece
{
private static readonly List<Path> MoveSet = new List<Path>(1)
private static readonly List<PathFinding.Move> Moves = new(1)
{
new Path(Direction.Up, Distance.MultiStep),
new PathFinding.Move(Direction.Up, Distance.MultiStep),
};
public Lance(WhichPlayer owner) : base(WhichPiece.Lance, owner)
{
moveSet = new MoveSet(this, Moves);
promotedMoveSet = new MoveSet(this, GoldenGeneral.Moves);
}
public override Piece DeepClone()
@@ -22,11 +22,5 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
if (IsPromoted) clone.Promote();
return clone;
}
public override ICollection<Path> GetPaths()
{
var moveSet = IsPromoted ? GoldenGeneral.MoveSet : MoveSet;
return Owner == WhichPlayer.Player1 ? moveSet : moveSet.Select(_ => new Path(Vector2.Negate(_.Direction), _.Distance)).ToList();
}
}
}

View File

@@ -1,19 +1,19 @@
using PathFinding;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Gameboard.ShogiUI.BoardState.Pieces
{
public class Pawn : Piece
{
private static readonly List<Path> MoveSet = new List<Path>(1)
private static readonly List<PathFinding.Move> Moves = new(1)
{
new Path(Direction.Up)
new PathFinding.Move(Direction.Up)
};
public Pawn(WhichPlayer owner) : base(WhichPiece.Pawn, owner)
{
moveSet = new MoveSet(this, Moves);
promotedMoveSet = new MoveSet(this, GoldenGeneral.Moves);
}
public override Piece DeepClone()
@@ -22,11 +22,5 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
if (IsPromoted) clone.Promote();
return clone;
}
public override ICollection<Path> GetPaths()
{
var moveSet = IsPromoted ? GoldenGeneral.MoveSet : MoveSet;
return Owner == WhichPlayer.Player1 ? moveSet : moveSet.Select(_ => new Path(Vector2.Negate(_.Direction), _.Distance)).ToList();
}
}
}

View File

@@ -1,15 +1,20 @@
using PathFinding;
using System.Collections.Generic;
using System.Diagnostics;
namespace Gameboard.ShogiUI.BoardState
namespace Gameboard.ShogiUI.BoardState.Pieces
{
[DebuggerDisplay("{WhichPiece} {Owner}")]
public abstract class Piece : IPlanarElement
{
protected MoveSet promotedMoveSet;
protected MoveSet moveSet;
public MoveSet MoveSet => IsPromoted ? promotedMoveSet : moveSet;
public abstract Piece DeepClone();
public WhichPiece WhichPiece { get; }
public WhichPlayer Owner { get; private set; }
public bool IsPromoted { get; private set; }
public bool IsUpsideDown => Owner == WhichPlayer.Player2;
public Piece(WhichPiece piece, WhichPlayer owner)
{
@@ -22,27 +27,6 @@ namespace Gameboard.ShogiUI.BoardState
&& WhichPiece != WhichPiece.King
&& WhichPiece != WhichPiece.GoldenGeneral;
public string ShortName => WhichPiece switch
{
WhichPiece.King => " K ",
WhichPiece.GoldenGeneral => " G ",
WhichPiece.SilverGeneral => IsPromoted ? "^S^" : " S ",
WhichPiece.Bishop => IsPromoted ? "^B^" : " B ",
WhichPiece.Rook => IsPromoted ? "^R^" : " R ",
WhichPiece.Knight => IsPromoted ? "^k^" : " k ",
WhichPiece.Lance => IsPromoted ? "^L^" : " L ",
WhichPiece.Pawn => IsPromoted ? "^P^" : " P ",
_ => " ? ",
};
public bool IsRanged => WhichPiece switch
{
WhichPiece.Bishop => true,
WhichPiece.Rook => true,
WhichPiece.Lance => !IsPromoted,
_ => false,
};
public void ToggleOwnership()
{
Owner = Owner == WhichPlayer.Player1
@@ -59,11 +43,5 @@ namespace Gameboard.ShogiUI.BoardState
ToggleOwnership();
Demote();
}
public abstract ICollection<Path> GetPaths();
public abstract Piece DeepClone();
public bool IsUpsideDown => Owner == WhichPlayer.Player2;
}
}

View File

@@ -1,32 +1,32 @@
using PathFinding;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Gameboard.ShogiUI.BoardState.Pieces
{
public class Rook : Piece
{
private static readonly List<Path> MoveSet = new List<Path>(4)
private static readonly List<PathFinding.Move> Moves = new(4)
{
new Path(Direction.Up, Distance.MultiStep),
new Path(Direction.Left, Distance.MultiStep),
new Path(Direction.Right, Distance.MultiStep),
new Path(Direction.Down, Distance.MultiStep)
new PathFinding.Move(Direction.Up, Distance.MultiStep),
new PathFinding.Move(Direction.Left, Distance.MultiStep),
new PathFinding.Move(Direction.Right, Distance.MultiStep),
new PathFinding.Move(Direction.Down, Distance.MultiStep)
};
private static readonly List<Path> PromotedMoveSet = new List<Path>(8)
private static readonly List<PathFinding.Move> PromotedMoves = new(8)
{
new Path(Direction.Up, Distance.MultiStep),
new Path(Direction.Left, Distance.MultiStep),
new Path(Direction.Right, Distance.MultiStep),
new Path(Direction.Down, Distance.MultiStep),
new Path(Direction.UpLeft),
new Path(Direction.UpRight),
new Path(Direction.DownLeft),
new Path(Direction.DownRight)
new PathFinding.Move(Direction.Up, Distance.MultiStep),
new PathFinding.Move(Direction.Left, Distance.MultiStep),
new PathFinding.Move(Direction.Right, Distance.MultiStep),
new PathFinding.Move(Direction.Down, Distance.MultiStep),
new PathFinding.Move(Direction.UpLeft),
new PathFinding.Move(Direction.UpRight),
new PathFinding.Move(Direction.DownLeft),
new PathFinding.Move(Direction.DownRight)
};
public Rook(WhichPlayer owner) : base(WhichPiece.Rook, owner)
{
moveSet = new MoveSet(this, Moves);
promotedMoveSet = new MoveSet(this, PromotedMoves);
}
public override Piece DeepClone()
@@ -35,10 +35,5 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
if (IsPromoted) clone.Promote();
return clone;
}
public override ICollection<Path> GetPaths()
{
var moveSet = IsPromoted ? PromotedMoveSet : MoveSet;
return Owner == WhichPlayer.Player1 ? moveSet : moveSet.Select(_ => new Path(Vector2.Negate(_.Direction), _.Distance)).ToList();
}
}
}

View File

@@ -1,22 +1,22 @@
using PathFinding;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Gameboard.ShogiUI.BoardState.Pieces
{
public class SilverGeneral : Piece
{
private static readonly List<Path> MoveSet = new List<Path>(4)
private static readonly List<PathFinding.Move> Moves = new(4)
{
new Path(Direction.Up),
new Path(Direction.UpLeft),
new Path(Direction.UpRight),
new Path(Direction.DownLeft),
new Path(Direction.DownRight)
new PathFinding.Move(Direction.Up),
new PathFinding.Move(Direction.UpLeft),
new PathFinding.Move(Direction.UpRight),
new PathFinding.Move(Direction.DownLeft),
new PathFinding.Move(Direction.DownRight)
};
public SilverGeneral(WhichPlayer owner) : base(WhichPiece.SilverGeneral, owner)
{
moveSet = new MoveSet(this, Moves);
promotedMoveSet = new MoveSet(this, GoldenGeneral.Moves);
}
public override Piece DeepClone()
@@ -25,11 +25,5 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
if (IsPromoted) clone.Promote();
return clone;
}
public override ICollection<Path> GetPaths()
{
var moveSet = IsPromoted ? GoldenGeneral.MoveSet : MoveSet;
return Owner == WhichPlayer.Player1 ? moveSet : moveSet.Select(_ => new Path(Vector2.Negate(_.Direction), _.Distance)).ToList();
}
}
}

View File

@@ -2,19 +2,17 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
namespace Gameboard.ShogiUI.BoardState
{
public class Array2D<T> : IPlanarCollection<T>, IEnumerable<T>
public class PlanarCollection<T> : IPlanarCollection<T>, IEnumerable<T> where T : IPlanarElement
{
/// <returns>False to stop iterating.</returns>
public delegate void ForEachDelegate(T element, int x, int y);
private readonly T[] array;
private readonly int width;
private readonly int height;
public Array2D(int width, int height)
public PlanarCollection(int width, int height)
{
this.width = width;
this.height = height;
@@ -39,17 +37,6 @@ namespace Gameboard.ShogiUI.BoardState
_ => throw new IndexOutOfRangeException()
};
public void ForEach(ForEachDelegate callback)
{
for (var x = 0; x < width; x++)
{
for (var y = 0; y < height; y++)
{
callback(this[x, y], x, y);
}
}
}
public void ForEachNotNull(ForEachDelegate callback)
{
for (var x = 0; x < width; x++)
@@ -62,19 +49,6 @@ namespace Gameboard.ShogiUI.BoardState
}
}
public Vector2? IndexOf(Predicate<T> predicate)
{
for (var x = 0; x < width; x++)
for (var y = 0; y < height; y++)
{
if (this[x, y] != null && predicate(this[x, y]))
{
return new Vector2(x, y);
}
}
return null;
}
public IEnumerator<T> GetEnumerator()
{
foreach (var item in array) yield return item;

View File

@@ -1,35 +0,0 @@
using System;
namespace Gameboard.ShogiUI.BoardState
{
public class Position
{
private int x;
private int y;
public int X
{
get => x;
set {
if (value > 8 || value < 0) throw new ArgumentOutOfRangeException();
x = value;
}
}
public int Y
{
get => y;
set
{
if (value > 8 || value < 0) throw new ArgumentOutOfRangeException();
y = value;
}
}
public Position(int x, int y)
{
X = x;
Y = y;
}
}
}

View File

@@ -20,7 +20,7 @@ namespace Gameboard.ShogiUI.BoardState
private Vector2 player1King;
private Vector2 player2King;
public IReadOnlyDictionary<WhichPlayer, List<Piece>> Hands { get; }
public Array2D<Piece> Board { get; }
public PlanarCollection<Piece> Board { get; }
public List<Move> MoveHistory { get; }
public WhichPlayer WhoseTurn => MoveHistory.Count % 2 == 0 ? WhichPlayer.Player1 : WhichPlayer.Player2;
public WhichPlayer? InCheck { get; private set; }
@@ -28,7 +28,7 @@ namespace Gameboard.ShogiUI.BoardState
public ShogiBoard()
{
Board = new Array2D<Piece>(9, 9);
Board = new PlanarCollection<Piece>(9, 9);
MoveHistory = new List<Move>(20);
Hands = new Dictionary<WhichPlayer, List<Piece>> {
{ WhichPlayer.Player1, new List<Piece>()},
@@ -54,7 +54,7 @@ namespace Gameboard.ShogiUI.BoardState
private ShogiBoard(ShogiBoard toCopy)
{
Board = new Array2D<Piece>(9, 9);
Board = new PlanarCollection<Piece>(9, 9);
for (var x = 0; x < 9; x++)
for (var y = 0; y < 9; y++)
Board[x, y] = toCopy.Board[x, y]?.DeepClone();
@@ -213,40 +213,6 @@ namespace Gameboard.ShogiUI.BoardState
return !isObstructed && isPathable;
}
public void PrintStateAsAscii()
{
var builder = new StringBuilder();
builder.Append(" Player 2");
builder.AppendLine();
for (var y = 8; y > -1; y--)
{
builder.Append("- ");
for (var x = 0; x < 8; x++) builder.Append("- - ");
builder.Append("- -");
builder.AppendLine();
builder.Append('|');
for (var x = 0; x < 9; x++)
{
var piece = Board[x, y];
if (piece == null)
{
builder.Append(" ");
}
else
{
builder.AppendFormat("{0}", piece.ShortName);
}
builder.Append('|');
}
builder.AppendLine();
}
builder.Append("- ");
for (var x = 0; x < 8; x++) builder.Append("- - ");
builder.Append("- -");
builder.AppendLine();
builder.Append(" Player 1");
Console.WriteLine(builder.ToString());
}
#region Rules Validation
private bool EvaluateCheckAfterMove(Move move, WhichPlayer whichPlayer)
{