please comment on game flow, Solid principle and OOP concepts.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chess
{
class Program
{
static void Main(string[] args)
{
ChessGame game = new ChessGame();
}
}
public class ChessGame
{
public ChessGame()
{
ChessPlayer blackPlayer = new ChessPlayer();
ChessPlayer whitePlayer = new ChessPlayer();
Board board = new Board(blackPlayer, whitePlayer);
while (!board.IsGameOver())
{
Console.WriteLine("white player move");
board.PositionPiece(whitePlayer.GetPieceAtLocation(1, 0), new Position(2, 0));
if (board.IsGameOver())
{
break;
}
Console.WriteLine("black player move");
board.PositionPiece(blackPlayer.GetPieceAtLocation(7, 0), new Position(6, 0));
}
}
}
public class ChessPlayer
{
public List<ChessPiece> PiecesList { get; private set; }
public ChessPlayer()
{
PiecesList = new List<ChessPiece>();
}
public ChessPiece GetPieceAtLocation(int h, int w)
{
var tempPiece = PiecesList.FirstOrDefault(x => x.Position.Height == h && x.Position.Width == w);
if (tempPiece == null)
{
Console.WriteLine("no piece in location h:" + h + "w:" + w);
return tempPiece;
}
return tempPiece;
}
}
/// <summary>
/// the board should position the pieces
/// the board knows where all the piece are and decides if the move is not being block by other pieces
/// </summary>
public class Board
{
private readonly ChessPlayer _blackPlayer;
private readonly ChessPlayer _whitePlayer;
private Rect[,] _rects;
public readonly int BoardSize = 8;
public Board(ChessPlayer blackPlayer, ChessPlayer whitePlayer)
{
_blackPlayer = blackPlayer;
_whitePlayer = whitePlayer;
InitBoardColors();
//init the position of the troops at the top of the board
InitBlackPieces();
InitWhitePieces();
}
private void InitWhitePieces()
{
Bishop whiteBishopLeft = new Bishop(new Position(0, 0));
Knight whiteKnightLeft = new Knight(new Position(0, 1));
Rook whiteRookLeft = new Rook(new Position(0, 2));
King whiteKing = new King(new Position(0, 3));
Queen whiteQueen = new Queen(new Position(0, 4));
Rook whiteRookRight = new Rook(new Position(0, 5));
Knight whiteKnightRight = new Knight(new Position(0, 6));
Bishop whiteBishopRight = new Bishop(new Position(0, 7));
_whitePlayer.PiecesList.Add(whiteBishopLeft);
_whitePlayer.PiecesList.Add(whiteKnightLeft);
_whitePlayer.PiecesList.Add(whiteRookLeft);
_whitePlayer.PiecesList.Add(whiteKing);
_whitePlayer.PiecesList.Add(whiteQueen);
_whitePlayer.PiecesList.Add(whiteKing);
_whitePlayer.PiecesList.Add(whiteRookRight);
_whitePlayer.PiecesList.Add(whiteKnightRight);
_whitePlayer.PiecesList.Add(whiteBishopRight);
for (int i = 0; i < BoardSize; i++)
{
WhitePawn blackPawn = new WhitePawn(new Position(1, i));
_whitePlayer.PiecesList.Add(blackPawn);
}
foreach (var temp in _blackPlayer.PiecesList)
{
_rects[temp.Position.Height, temp.Position.Width].PieceType = temp.PieceType;
}
}
private void InitBlackPieces()
{
Bishop blackBishopLeft = new Bishop(new Position(7,0));
Knight blackKnightLeft = new Knight(new Position(7,1));
Rook blackRookLeft = new Rook(new Position(7,2));
King blackKing = new King(new Position(7,3));
Queen blackQueen = new Queen(new Position(7,4));
Rook blackRookRight = new Rook(new Position(7, 5));
Knight blackKnightRight = new Knight(new Position(7, 6));
Bishop blackBishopRight = new Bishop(new Position(7, 7));
_blackPlayer.PiecesList.Add(blackBishopLeft);
_blackPlayer.PiecesList.Add(blackKnightLeft);
_blackPlayer.PiecesList.Add(blackRookLeft);
_blackPlayer.PiecesList.Add(blackKing);
_blackPlayer.PiecesList.Add(blackQueen);
_blackPlayer.PiecesList.Add(blackKing);
_blackPlayer.PiecesList.Add(blackRookRight);
_blackPlayer.PiecesList.Add(blackKnightRight);
_blackPlayer.PiecesList.Add(blackBishopRight);
for (int i = 0; i < BoardSize; i++)
{
BlackPawn blackPawn = new BlackPawn(new Position(6, i));
_blackPlayer.PiecesList.Add(blackPawn);
}
foreach (var temp in _blackPlayer.PiecesList)
{
_rects[temp.Position.Height, temp.Position.Width].PieceType = temp.PieceType;
}
}
private void InitBoardColors()
{
_rects = new Rect[BoardSize, BoardSize];
//init the colors of the rects on the board
var temp = RectColor.White;
for (int h = 0; h < BoardSize; h++)
{
for (int w = 0; w < BoardSize; w++)
{
_rects[h, w] = new Rect(temp,PieceType.Empty);
if (temp == RectColor.White)
{
temp = RectColor.Black;
}
else
{
temp = RectColor.White;
}
}
}
}
public bool PositionPiece(ChessPiece chessPiece, Position dest)
{
if (!chessPiece.IsValidMove(dest))
{
return false;
}
if (_rects[dest.Height, dest.Width].PieceType == PieceType.Empty)
{
_rects[dest.Height, dest.Width].PieceType = chessPiece.PieceType;
chessPiece.Position.Width = dest.Width;
chessPiece.Position.Height = dest.Height;
return true;
}
else
{
//check friend or enemy
if (_blackPlayer.PiecesList.Contains(chessPiece))
{
if (_blackPlayer.GetPieceAtLocation(dest.Height, dest.Width) != null)
{
//we are trying to put the piece on another black piece
return false;
}
else
{
var tempWhite = _whitePlayer.GetPieceAtLocation(dest.Height, dest.Width);
if (tempWhite != null)
{
//killed white walker!
_whitePlayer.PiecesList.Remove(tempWhite);
_rects[dest.Height, dest.Width].PieceType = chessPiece.PieceType;
chessPiece.Position.Width = dest.Width;
chessPiece.Position.Height = dest.Height;
return true;
}
//error
}
}
//error
}
return false;
}
public bool IsGameOver()
{
// can the king of the black or the white still move?
return false;
}
}
public class Rect
{
public RectColor RectColor { get; set; }
public PieceType PieceType { get; set; }
public Rect(RectColor rectColor, PieceType pieceType)
{
RectColor = rectColor;
PieceType = pieceType;
}
}
public enum RectColor
{
White,
Black
}
public enum PieceType
{
Empty,
King,
Queen,
Rook, //runner
Knight, //horse
Bishop, //castle
BlackPawn,
WhitePawn
}
public abstract class ChessPiece
{
public PieceType PieceType { get; set; }
public Position Position { get; set; }
protected ChessPiece(Position poisition, PieceType pieceType)
{
Position = poisition;
PieceType = pieceType;
}
public abstract bool IsValidMove(Position dest);
protected bool IsOnBoard(Position origin, Position dest)
{
if (origin.Height < 0 || origin.Height > 7 || origin.Width < 0 || origin.Width > 7 || dest.Height < 0 || dest.Height > 7 || dest.Width < 0 || dest.Width > 7)
{
Console.WriteLine("position piece out or range");
return false;
}
return true;
}
}
public class Knight : ChessPiece
{
public Knight(Position poisition)
: base(poisition, PieceType.Knight)
{
}
public override bool IsValidMove(Position dest)
{
if (!IsOnBoard(this.Position,dest))
{
return false;
}
if (dest.Height == Position.Height + 2 && dest.Width == Position.Width + 1 || dest.Width == (Position.Width - 1))
{
return true;
}
if (dest.Height == Position.Height - 2 && dest.Width == Position.Width + 1 || dest.Width == (Position.Width - 1))
{
return true;
}
if (dest.Width == Position.Width - 2 && dest.Height == Position.Height + 1 || dest.Height == Position.Height - 1)
{
return true;
}
if (dest.Width == Position.Width + 2 && dest.Height == Position.Height + 1 || dest.Height == Position.Height - 1)
{
return true;
}
return false;
}
}
public class Bishop : ChessPiece
{
public Bishop(Position poisition)
: base(poisition, PieceType.Bishop)
{
}
public override bool IsValidMove(Position dest)
{
if (!IsOnBoard(this.Position, dest))
{
return false;
}
if (Position.Height == dest.Height) //same row
{
return true;
}
if (Position.Width == dest.Width) //same column
{
return true;
}
return false;
}
}
public class Rook : ChessPiece
{
public Rook(Position poisition)
: base(poisition, PieceType.Rook)
{
}
public override bool IsValidMove(Position dest)
{
if (!IsOnBoard(this.Position, dest))
{
return false;
}
if (dest.Width - Position.Width == dest.Height - Position.Height) // diagonal
{
return true;
}
return false;
}
}
public class Queen :ChessPiece
{
public Queen(Position poisition) : base(poisition, PieceType.Queen)
{
}
public override bool IsValidMove(Position dest)
{
if (!IsOnBoard(this.Position, dest))
{
return false;
}
if (Position.Height == dest.Height) //same row
{
return true;
}
if (Position.Width == dest.Width) //same column
{
return true;
}
if (dest.Width - Position.Width == dest.Height - Position.Height) // diagonal
{
return true;
}
return false;
}
}
public class King : ChessPiece
{
public King(Position poisition): base(poisition, PieceType.King)
{
}
public override bool IsValidMove(Position dest)
{
//can't move to a blocked location - the board should do it
//can't move more than one to each side including diagonal
if (!IsOnBoard(this.Position, dest))
{
return false;
}
//one down
if (Position.Height == dest.Height - 1 && Position.Width == dest.Width)
{
return true;
}
//one up
if (Position.Height + 1 == dest.Height && Position.Width == dest.Width)
{
return true;
}
//one up one right
if (Position.Height + 1 == dest.Height && Position.Width + 1 == dest.Width)
{
return true;
}
//one up one left
if (Position.Height + 1 == dest.Height && Position.Width - 1 == dest.Width)
{
return true;
}
//one down
if (Position.Height - 1 == dest.Height && Position.Width == dest.Width)
{
return true;
}
//one left
if (Position.Height == dest.Height && Position.Width - 1 == dest.Width)
{
return true;
}
//one right
if (Position.Height == dest.Height && Position.Width + 1 == dest.Width)
{
return true;
}
//one down one left
if (Position.Height - 1 == dest.Height && Position.Width - 1 == dest.Width)
{
return true;
}
//one down one right
if (Position.Height - 1 == dest.Height && Position.Width + 1 == dest.Width)
{
return true;
}
return false;
}
}
public class BlackPawn : ChessPiece
{
public BlackPawn(Position poisition) : base(poisition, PieceType.BlackPawn)
{
}
public override bool IsValidMove(Position dest)
{
if (!IsOnBoard(this.Position, dest))
{
return false;
}
if (Position.Height == dest.Height - 1 && Position.Width == dest.Width)
{
return true;
}
if (Position.Height == dest.Height && Position.Width == dest.Width - 1)
{
return true;
}
return false;
}
}
public class WhitePawn : ChessPiece
{
public WhitePawn(Position poisition)
: base(poisition, PieceType.WhitePawn)
{
}
public override bool IsValidMove(Position dest)
{
if (!IsOnBoard(this.Position, dest))
{
return false;
}
if (Position.Height == dest.Height + 1 && Position.Width == dest.Width)
{
return true;
}
if (Position.Height == dest.Height && Position.Width == dest.Width + 1)
{
return true;
}
return false;
}
}
public class Position
{
public int Height { get; set; }
public int Width { get; set; }
public Position(int h, int w)
{
Height = h;
Width = w;
}
}
}
PiecesList.Contains(thisPiece);
So construction is "color agnostic" until a set of instantiated pieces is passed to a player at which time the player & pieces color can be set in a nice, tight loop - in theGame.BlackPlayer
&Game.WhitePlayer
property setters; which very nicely encapsulates all that color that's currently spilled all over the code. \$\endgroup\$isOpponent
, to help test capturing for example. Thus all the functional code, like the construction code (above), can be color agnostic. And so the code as written and read conveys that chess game objects behave the same regardless of color. And the reader is not distracted with trying to find out how and why white pieces move different from black - they must or there wouldn't be so much color-differentiated code everywhere in what otherwise would be duplicate/redundant code. \$\endgroup\$