47
Shogi.Domain/Pieces/Bishop.cs
Normal file
47
Shogi.Domain/Pieces/Bishop.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Shogi.Domain.Pathing;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Shogi.Domain.Pieces
|
||||
{
|
||||
internal class Bishop : Piece
|
||||
{
|
||||
private static readonly ReadOnlyCollection<Path> BishopPaths = new(new List<Path>(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)
|
||||
});
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> PromotedBishopPaths = new(new List<Path>(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)
|
||||
});
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> Player2Paths =
|
||||
BishopPaths
|
||||
.Select(p => p.Invert())
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> Player2PromotedPaths =
|
||||
PromotedBishopPaths
|
||||
.Select(p => p.Invert())
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
public Bishop(WhichPlayer owner, bool isPromoted = false)
|
||||
: base(WhichPiece.Bishop, owner, isPromoted)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<Path> MoveSet => IsPromoted ? PromotedBishopPaths : BishopPaths;
|
||||
}
|
||||
}
|
||||
31
Shogi.Domain/Pieces/GoldGeneral.cs
Normal file
31
Shogi.Domain/Pieces/GoldGeneral.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Shogi.Domain.Pathing;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Shogi.Domain.Pieces
|
||||
{
|
||||
internal class GoldGeneral : Piece
|
||||
{
|
||||
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(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)
|
||||
});
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> Player2Paths =
|
||||
Player1Paths
|
||||
.Select(p => p.Invert())
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
public GoldGeneral(WhichPlayer owner, bool isPromoted = false)
|
||||
: base(WhichPiece.GoldGeneral, owner, isPromoted)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<Path> MoveSet => Player1Paths;
|
||||
}
|
||||
}
|
||||
27
Shogi.Domain/Pieces/King.cs
Normal file
27
Shogi.Domain/Pieces/King.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Shogi.Domain.Pathing;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Shogi.Domain.Pieces
|
||||
{
|
||||
internal class King : Piece
|
||||
{
|
||||
internal static readonly ReadOnlyCollection<Path> KingPaths = new(new List<Path>(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)
|
||||
});
|
||||
|
||||
public King(WhichPlayer owner, bool isPromoted = false)
|
||||
: base(WhichPiece.King, owner, isPromoted)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<Path> MoveSet => KingPaths;
|
||||
}
|
||||
}
|
||||
32
Shogi.Domain/Pieces/Knight.cs
Normal file
32
Shogi.Domain/Pieces/Knight.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Shogi.Domain.Pathing;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Shogi.Domain.Pieces
|
||||
{
|
||||
internal class Knight : Piece
|
||||
{
|
||||
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(2)
|
||||
{
|
||||
new Path(Direction.KnightLeft),
|
||||
new Path(Direction.KnightRight)
|
||||
});
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> Player2Paths =
|
||||
Player1Paths
|
||||
.Select(p => p.Invert())
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
public Knight(WhichPlayer owner, bool isPromoted = false)
|
||||
: base(WhichPiece.Knight, owner, isPromoted)
|
||||
{
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<Path> MoveSet => Owner switch
|
||||
{
|
||||
WhichPlayer.Player1 => IsPromoted ? GoldGeneral.Player1Paths : Player1Paths,
|
||||
WhichPlayer.Player2 => IsPromoted ? GoldGeneral.Player2Paths : Player2Paths,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
31
Shogi.Domain/Pieces/Lance.cs
Normal file
31
Shogi.Domain/Pieces/Lance.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Shogi.Domain.Pathing;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Shogi.Domain.Pieces
|
||||
{
|
||||
internal class Lance : Piece
|
||||
{
|
||||
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(1)
|
||||
{
|
||||
new Path(Direction.Up, Distance.MultiStep),
|
||||
});
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> Player2Paths =
|
||||
Player1Paths
|
||||
.Select(p => p.Invert())
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
public Lance(WhichPlayer owner, bool isPromoted = false)
|
||||
: base(WhichPiece.Lance, owner, isPromoted)
|
||||
{
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<Path> MoveSet => Owner switch
|
||||
{
|
||||
WhichPlayer.Player1 => IsPromoted ? GoldGeneral.Player1Paths : Player1Paths,
|
||||
WhichPlayer.Player2 => IsPromoted ? GoldGeneral.Player2Paths : Player2Paths,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
31
Shogi.Domain/Pieces/Pawn.cs
Normal file
31
Shogi.Domain/Pieces/Pawn.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Shogi.Domain.Pathing;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Shogi.Domain.Pieces
|
||||
{
|
||||
internal class Pawn : Piece
|
||||
{
|
||||
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(1)
|
||||
{
|
||||
new Path(Direction.Up)
|
||||
});
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> Player2Paths =
|
||||
Player1Paths
|
||||
.Select(p => p.Invert())
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
public Pawn(WhichPlayer owner, bool isPromoted = false)
|
||||
: base(WhichPiece.Pawn, owner, isPromoted)
|
||||
{
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<Path> MoveSet => Owner switch
|
||||
{
|
||||
WhichPlayer.Player1 => IsPromoted ? GoldGeneral.Player1Paths : Player1Paths,
|
||||
WhichPlayer.Player2 => IsPromoted ? GoldGeneral.Player2Paths : Player2Paths,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
78
Shogi.Domain/Pieces/Piece.cs
Normal file
78
Shogi.Domain/Pieces/Piece.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using Shogi.Domain.Pathing;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Shogi.Domain.Pieces
|
||||
{
|
||||
[DebuggerDisplay("{WhichPiece} {Owner}")]
|
||||
public abstract class Piece
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a clone of an existing piece.
|
||||
/// </summary>
|
||||
public static Piece Create(Piece piece) => Create(piece.WhichPiece, piece.Owner, piece.IsPromoted);
|
||||
public static Piece Create(WhichPiece piece, WhichPlayer owner, bool isPromoted = false)
|
||||
{
|
||||
return piece switch
|
||||
{
|
||||
WhichPiece.King => new King(owner, isPromoted),
|
||||
WhichPiece.GoldGeneral => new GoldGeneral(owner, isPromoted),
|
||||
WhichPiece.SilverGeneral => new SilverGeneral(owner, isPromoted),
|
||||
WhichPiece.Bishop => new Bishop(owner, isPromoted),
|
||||
WhichPiece.Rook => new Rook(owner, isPromoted),
|
||||
WhichPiece.Knight => new Knight(owner, isPromoted),
|
||||
WhichPiece.Lance => new Lance(owner, isPromoted),
|
||||
WhichPiece.Pawn => new Pawn(owner, isPromoted),
|
||||
_ => throw new ArgumentException($"Unknown {nameof(WhichPiece)} when cloning a {nameof(Piece)}.")
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: MoveSet doesn't account for Player2's pieces which are upside-down.
|
||||
public abstract IEnumerable<Path> MoveSet { get; }
|
||||
public WhichPiece WhichPiece { get; }
|
||||
public WhichPlayer Owner { get; private set; }
|
||||
public bool IsPromoted { get; private set; }
|
||||
public bool IsUpsideDown => Owner == WhichPlayer.Player2;
|
||||
|
||||
protected Piece(WhichPiece piece, WhichPlayer owner, bool isPromoted = false)
|
||||
{
|
||||
WhichPiece = piece;
|
||||
Owner = owner;
|
||||
IsPromoted = isPromoted;
|
||||
}
|
||||
|
||||
public bool CanPromote => !IsPromoted
|
||||
&& WhichPiece != WhichPiece.King
|
||||
&& WhichPiece != WhichPiece.GoldGeneral;
|
||||
|
||||
public void Promote() => IsPromoted = CanPromote;
|
||||
|
||||
/// <summary>
|
||||
/// Prep the piece for capture by changing ownership and demoting.
|
||||
/// </summary>
|
||||
public void Capture(WhichPlayer newOwner)
|
||||
{
|
||||
Owner = newOwner;
|
||||
IsPromoted = false;
|
||||
}
|
||||
|
||||
public IEnumerable<Vector2> GetPathFromStartToEnd(Vector2 start, Vector2 end)
|
||||
{
|
||||
var steps = new List<Vector2>(10);
|
||||
|
||||
var path = this.MoveSet.GetNearestPath(start, end);
|
||||
var position = start;
|
||||
while (Vector2.Distance(start, position) < Vector2.Distance(start, end))
|
||||
{
|
||||
position += path.Direction;
|
||||
steps.Add(position);
|
||||
}
|
||||
|
||||
if (position == end)
|
||||
{
|
||||
return steps;
|
||||
}
|
||||
|
||||
return Array.Empty<Vector2>();
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Shogi.Domain/Pieces/Rook.cs
Normal file
52
Shogi.Domain/Pieces/Rook.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Shogi.Domain.Pathing;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Shogi.Domain.Pieces
|
||||
{
|
||||
public sealed class Rook : Piece
|
||||
{
|
||||
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(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)
|
||||
});
|
||||
|
||||
private static readonly ReadOnlyCollection<Path> PromotedPlayer1Paths = new(new List<Path>(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)
|
||||
});
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> Player2Paths =
|
||||
Player1Paths
|
||||
.Select(m => m.Invert())
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> Player2PromotedPaths =
|
||||
PromotedPlayer1Paths
|
||||
.Select(m => m.Invert())
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
public Rook(WhichPlayer owner, bool isPromoted = false)
|
||||
: base(WhichPiece.Rook, owner, isPromoted)
|
||||
{
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<Path> MoveSet => Owner switch
|
||||
{
|
||||
WhichPlayer.Player1 => IsPromoted ? PromotedPlayer1Paths : Player1Paths,
|
||||
WhichPlayer.Player2 => IsPromoted ? Player2PromotedPaths : Player2Paths,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
35
Shogi.Domain/Pieces/SilverGeneral.cs
Normal file
35
Shogi.Domain/Pieces/SilverGeneral.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Shogi.Domain.Pathing;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Shogi.Domain.Pieces
|
||||
{
|
||||
internal class SilverGeneral : Piece
|
||||
{
|
||||
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(4)
|
||||
{
|
||||
new Path(Direction.Up),
|
||||
new Path(Direction.UpLeft),
|
||||
new Path(Direction.UpRight),
|
||||
new Path(Direction.DownLeft),
|
||||
new Path(Direction.DownRight)
|
||||
});
|
||||
|
||||
public static readonly ReadOnlyCollection<Path> Player2Paths =
|
||||
Player1Paths
|
||||
.Select(p => p.Invert())
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
public SilverGeneral(WhichPlayer owner, bool isPromoted = false)
|
||||
: base(WhichPiece.SilverGeneral, owner, isPromoted)
|
||||
{
|
||||
}
|
||||
|
||||
public override ReadOnlyCollection<Path> MoveSet => Owner switch
|
||||
{
|
||||
WhichPlayer.Player1 => IsPromoted ? GoldGeneral.Player1Paths : Player1Paths,
|
||||
WhichPlayer.Player2 => IsPromoted ? GoldGeneral.Player2Paths : Player2Paths,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user