7
\$\begingroup\$

I've recently created my own 2-player chess game, no AI... at least for now.

I do plan to do that in the future, so one of my concerns is if the code is flexible enough to just use the same classes in all the different modes so I can avoid rewriting the same code over and over again. I've tried to explain most of the logic with comments in the main Form but I've skipped most of the explanation of what's going in the other classes, so if anything is unclear I will be happy to answer in the comments.

User Interface

The game itself starts in the MainMenuForm:

GLS Chess Main Menu

public partial class MainMenuForm : Form
{
    public MainMenuForm()
    {
        InitializeComponent();
    }

    private void bExit_Click(object sender, EventArgs e)
    {
        Application.Exit();
    }

    private void bOptions_Click(object sender, EventArgs e)
    {
        OptionsForm optf = new OptionsForm();
        optf.ShowDialog();
    }

    private void bOnline_Click(object sender, EventArgs e)
    {

    }

    private void b2Players_Click(object sender, EventArgs e)
    {
        CooperativeForm cpf = new CooperativeForm();
        cpf.ShowDialog();
    }

    private void b1Player_Click(object sender, EventArgs e)
    {

    }
}

As I've mentioned earlier currently the only working mode is the 2-Players mode so the other buttons are disabled and have nothing inside they're Click event handler.

From here you can go to the OptionsForm:

game options

It mostly works with the Properties.Settings

app settings

The form itself is implemented like this :

public partial class OptionsForm : Form
{
    private bool settingsSaved = false;
    private bool closeOptions = false;
    private bool enableTurnHelper = Properties.Settings.Default.EnabledTurnHelper;
    private bool enableTurnTracking = Properties.Settings.Default.EnabledTurnTracking;
    private bool enableTurnTimers = Properties.Settings.Default.EnabledTurnTimers;
    private uint timerTime = Properties.Settings.Default.TurnTimer;
    public OptionsForm()
    {
        InitializeComponent();
        if (enableTurnTimers)
        {
            cbEnableTurnTimers.Checked = true;
            lbTimerTime.Visible = true;
            tbTimerTime.Visible = true;
            tbTimerTime.Text = (timerTime/60).ToString();
        }
        if (enableTurnTracking)
        {
            cbEnableTurnTracking.Checked = true;
        }
        if (enableTurnHelper)
        {
            cbEnableTurnHelper.Checked = true;
        }
    }

    private void bEnableTurnTimers_Click(object sender, EventArgs e)
    {
        cbEnableTurnTimers.Checked = !cbEnableTurnTimers.Checked;
        lbTimerTime.Visible = !lbTimerTime.Visible;
        tbTimerTime.Visible = !tbTimerTime.Visible;
        enableTurnTimers = GetValue(cbEnableTurnTimers);
    }
    private void bEnableTurnTracking_Click(object sender, EventArgs e)
    {
        cbEnableTurnTracking.Checked = !cbEnableTurnTracking.Checked;
        enableTurnTracking = GetValue(cbEnableTurnTracking);
    }
    private void bEnableTurnHelper_Click(object sender, EventArgs e)
    {
        cbEnableTurnHelper.Checked = !cbEnableTurnHelper.Checked;
        enableTurnHelper = GetValue(cbEnableTurnHelper);
    }
    private void bSave_Click(object sender, EventArgs e)
    {
        if (!CheckFormat(tbTimerTime.Text, out timerTime))
        {
            return;
        }
        settingsSaved = true;
        Properties.Settings.Default.EnabledTurnHelper = enableTurnHelper;
        Properties.Settings.Default.EnabledTurnTimers = enableTurnTimers;
        Properties.Settings.Default.EnabledTurnTracking = enableTurnTracking;
        Properties.Settings.Default.TurnTimer = timerTime*60;
        Properties.Settings.Default.Save();
        MessageBox.Show(@"Your settings have been saved succesfully !");
    }
    private static bool GetValue(CheckBox inputCheckBox)
    {
        return inputCheckBox.Checked;
    }

    private void Options_Closing(object sender, FormClosingEventArgs e)
    {
        if (!settingsSaved && !closeOptions)
        {
            uint tempParse;
            bool tempTryParse = uint.TryParse(tbTimerTime.Text, out tempParse);
            if (Properties.Settings.Default.EnabledTurnHelper != enableTurnHelper ||
                Properties.Settings.Default.EnabledTurnTimers != enableTurnTimers ||
                Properties.Settings.Default.EnabledTurnTracking != enableTurnTracking ||
                Properties.Settings.Default.TurnTimer != tempParse * 60)
            {
                DialogResult replayDialog =
                    MessageBox.Show(
                        @"Your settings haven't been saved yet !" + Environment.NewLine +
                        @"Would you like to leave ?",
                        @"Settings not saved", MessageBoxButtons.YesNo);
                if (replayDialog == DialogResult.Yes)
                {
                    closeOptions = true;
                    Close();
                }
                else e.Cancel = true;
            }
        }
    }

    private void tbTimerTime_KeyPress(object sender, KeyPressEventArgs e)
    {
        TextBox tb = (TextBox) sender;
        string text = tb.Text;
        if (e.KeyChar == (char)Keys.Enter)
        {
            e.Handled = true;
            CheckFormat(text, out timerTime);
        }
    }
    private static bool CheckFormat(string input, out uint output)
    {
        uint tempParse;
        if (!uint.TryParse(input, out tempParse))
        {
            MessageBox.Show(@"Timer time input can be only digits ");
            output = Properties.Settings.Default.TurnTimer;
            return false;
        }
        if (tempParse > 60 || tempParse <= 0)
        {
            MessageBox.Show(@"Timer time must be in the range 1 - 60 minutes !");
            output = Properties.Settings.Default.TurnTimer;
            return false;
        }
        output = tempParse;
        return true;
    }
}

It does check if you have saved your settings upon closing the form. If you are done with editing your setting you can go close this form and go to play in the CooperativeModeForm which is where all my code is.

game screen

/// <summary>
/// Currently the only available Game Mode is being implemented in here.
/// </summary>
public sealed partial class CooperativeForm : Form
{
    private readonly Timer timer = new Timer();
    //variable used by the Turn Tracking Feature if checked in the Options menu.
    private static Tuple<Point, Figure, PictureBox> passedTurn;

    // tile size.
    private const int tileWidth = 85;
    private const int tileHeight = 75;

    //figure size.
    private const int figureWidth = 75;
    private const int figureHeight = 65;

    // since there's difference in the tile size and the figure size this helps place it in the middle of the tile it's left on.
    private const int marginsLeftToCenterTile = 5;

    // size of the board 8x8.
    public const int size = 8;

    // all the tiles are pictureboxs
    public static readonly PictureBox[][] DrawnBoard = new PictureBox[size][];

    //boolean board which helps with the checking of where a figure is placed ??
    public static bool[][] Board = new bool[8][];

    // all the figures are stored here.
    public static List<Figure> Figures = new List<Figure>();

    // Figure class doesn't have a PictureBox property so this is basically chained to a specific figure.
    private static readonly PictureBox[] figuresImages = new PictureBox[32];

    // boolean variable to determine wheter the player is dragging a figure.
    private bool isMoved;

    // variable to keep track of the current figure it's updated whenever a figure is being pressed not neccessary to be moved.
    private Figure currentFigure;

    // if the player's movement was unavailable those variables will help return it to it's previous place
    private int previousX;
    private int previousY;

    // this variable keeps track of the current player
    private static FigureColor currentColor = FigureColor.White;

    // keeping track of the current turn
    private static int currentTurn;

    // white and black figures are stored here
    public static readonly List<Figure> WhiteFigures = new List<Figure>();
    public static readonly List<Figure> BlackFigures = new List<Figure>();

    // variables converting the const figure and tile size's into Size variables.
    private static readonly Size figureSize = new Size(figureWidth, figureHeight);
    private static readonly Size tileSize = new Size(tileWidth, tileHeight);

    // taking the user's input for the time length from the resources.
    private readonly uint timerMaxValue = Properties.Settings.Default.TurnTimer;

    // pictureBox which acts as a Board it's currently not useful but will help with future updates
    // because currently figures back color isn't perfect they are not completely transparent they take
    // either black or white depending on what they are standing on. Not supporting if in between.
    private PictureBox parentBoardPictureBox = new PictureBox();

    // variable to keep track of the Turn Helper's highlighted tiles which must be set back to normal color
    // whenever the figure is released.
    private readonly List<Tuple<int, int, Color>> highlightedTiles = new List<Tuple<int, int, Color>>();

    // variables to keep track of the white and black turns which are used by the Turn Tracking Option
    // if checked in the Options Menu.
    private readonly List<List<object>> whiteTurns = new List<List<object>>();
    private readonly List<List<object>> blackTurns = new List<List<object>>();
    private ListView TurnTrackingList = new ListView();
    private static readonly char[] letters = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'};

    /// <summary>
    /// Reduces the flickering caused by dragging a figure/PictureBox.
    /// </summary>
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x02000000;
            return cp;
        }
    }

    public CooperativeForm()
    {
        InitializeComponent();
        BackColor = Color.SaddleBrown;
        FillBoard();
        DrawBoard();
        DrawLines();

        LoadAllFigures();

        if (Properties.Settings.Default.EnabledTurnTimers)
        {
            pbTimer.Visible = true;
            pbTimer.Maximum = (int) timerMaxValue;
            pbTimer.Value = pbTimer.Maximum;
            timer.Interval = 1000;
            timer.Tick += timer_Tick;
            timer.Start();
        }
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        pbTimer.Value--;
        if (pbTimer.Value <= 0)
        {
            pbTimer.Value = (int) timerMaxValue;
            currentColor = currentColor == FigureColor.Black
                ? FigureColor.White
                : FigureColor.Black;
        }
    }

    /// <summary> Method to load and draw all the figures using several other methods </summary>
    private void LoadAllFigures()
    {
        if (Properties.Settings.Default.EnabledTurnTracking)
        {
            flpBlackPlayerWinnings.Height /= 2;
            flpWhitePlayerWinnings.Height /= 2;
            flpWhitePlayerWinnings.Location = new Point(flpWhitePlayerWinnings.Location.X, flpBlackPlayerWinnings.Location.Y + flpBlackPlayerWinnings.Height + 10);
            EnableTurnTracking();
        }
        if (Figures.Count > 0)
        {
            Restart();
        }
        else
        {
            AddKings();
            AddQueens();
            AddRooks();
            AddBishops();
            AddKnights();
            AddPawns();

            DrawFigures();
        }
    }

    // Methods to add the figures to each team </summary>
    private static void AddPawns()
    {
        IEnumerable<FigureDefinition> blackDefinitions = new PawnDefinitions().BlackPawns;
        IEnumerable<FigureDefinition> whiteDefinitions = new PawnDefinitions().WhitePawns;

        AddPawnsByColor(whiteDefinitions);
        AddPawnsByColor(blackDefinitions);
    }
    private static void AddPawnsByColor(IEnumerable<FigureDefinition> definitions)
    {
        foreach (PawnPiece piece in definitions.Select(definition => new PawnPiece(definition)))
        {
            AddFigure(piece);
        }
    }

    private static void AddKnights()
    {
        IEnumerable<FigureDefinition> blackDefinitions = new KnightDefinitions().BlackKnights;
        IEnumerable<FigureDefinition> whiteDefinitions = new KnightDefinitions().WhiteKnights;

        AddKnightsByColor(whiteDefinitions);
        AddKnightsByColor(blackDefinitions);
    }
    private static void AddKnightsByColor(IEnumerable<FigureDefinition> definitions)
    {
        foreach (KnightPiece piece in definitions.Select(definition => new KnightPiece(definition)))
        {
            AddFigure(piece);
        }
    }

    private static void AddBishops()
    {
        IEnumerable<FigureDefinition> blackDefinitions = new BishopDefinitions().BlackBishops;
        IEnumerable<FigureDefinition> whiteDefinitions = new BishopDefinitions().WhiteBishops;

        AddBishopsByColor(whiteDefinitions);
        AddBishopsByColor(blackDefinitions);
    }
    private static void AddBishopsByColor(IEnumerable<FigureDefinition> definitions)
    {
        foreach (BishopPiece piece in definitions.Select(definition => new BishopPiece(definition)))
        {
            AddFigure(piece);
        }
    }

    private static void AddRooks()
    {
        IEnumerable<FigureDefinition> blackDefinitions = new TopDefinitions().BlackRooks;
        IEnumerable<FigureDefinition> whiteDefinitions = new TopDefinitions().WhiteRooks;

        AddRooksByColor(whiteDefinitions);
        AddRooksByColor(blackDefinitions);
    }
    private static void AddRooksByColor(IEnumerable<FigureDefinition> definitions)
    {
        foreach (RookPiece piece in definitions.Select(definition => new RookPiece(definition)))
        {
            AddFigure(piece);
        }
    }

    private static void AddQueens()
    {
        IEnumerable<FigureDefinition> blackDefinitions = new QueenDefinitions().BlackQueens;
        IEnumerable<FigureDefinition> whiteDefinitions = new QueenDefinitions().WhiteQueens;

        AddQueensByColor(whiteDefinitions);
        AddQueensByColor(blackDefinitions);
    }
    private static void AddQueensByColor(IEnumerable<FigureDefinition> definitions)
    {
        foreach (QueenPiece piece in definitions.Select(definition => new QueenPiece(definition)))
        {
            AddFigure(piece);
        }
    }

    private static void AddKings()
    {
        IEnumerable<FigureDefinition> blackDefinitions = new KingDefinitions().BlackKings;
        IEnumerable<FigureDefinition> whiteDefinitions = new KingDefinitions().WhiteKings;

        AddKingsByColor(whiteDefinitions);
        AddKingsByColor(blackDefinitions);
    }
    private static void AddKingsByColor(IEnumerable<FigureDefinition> definitions)
    {
        foreach (KingPiece piece in definitions.Select(definition => new KingPiece(definition)))
        {
            AddFigure(piece);
        }
    }

    /// <summary> Method to add figure to the  
    /// <seealso cref="Figures"/>
    /// List </summary>
    private static void AddFigure(Figure newFigure)
    {
        Figures.Add(newFigure);
        AddToTeam(newFigure);
    }

    // Methods to add/remove figure/figures to teams

    private static void AddToTeam(Figure newFigure)
    {
        if (newFigure.PieceColor == FigureColor.Black)
        {
            BlackFigures.Add(newFigure);
        }
        else
        {
            WhiteFigures.Add(newFigure);
        }
    }
    private static void AddMultipleFiguresToTeam(params Figure[] inputFigures)
    {
        foreach (var inputFigure in inputFigures)
        {
            AddToTeam(inputFigure);
        }
    }
    private static void RemoveFromTeam(Figure inputFigure)
    {
        if (inputFigure.PieceColor == FigureColor.Black)
        {
            BlackFigures.Remove(inputFigure);
        }
        else
        {
            WhiteFigures.Remove(inputFigure);
        }
    }
    private static void RemoveMultipleFiguresFromTeam(params Figure[] inputFigures)
    {
        foreach (var inputFigure in inputFigures)
        {
            RemoveFromTeam(inputFigure);
        }
    }
    private static void RemoveMultipleFiguresFromFiguresList(params Figure[] inputFigures)
    {
        foreach (var inputFigure in inputFigures)
        {
            Figures.Remove(inputFigure);
        }   
    }
    private static void AddMultipleFiguresToFiguresList(params Figure[] inputFigures)
    {
        foreach (var inputFigure in inputFigures)
        {
            Figures.Add(inputFigure);
        }
    }

    private void DrawFigures()
    {
        for (int i = 0; i < Figures.Count; i++)
        {
            int x = Figures[i].StartingPosition.Item1;
            int y = Figures[i].StartingPosition.Item2;
            LoadFigure(i, x, y);
        }
    }
    private void LoadFigure(int i, int x, int y)
    {
        figuresImages[i] = new PictureBox
        {
            Size = figureSize,
            BackgroundImage = Figures[i].PieceImage,
            BackgroundImageLayout = ImageLayout.Stretch,
            AllowDrop = true,
            Location =
                new Point(DrawnBoard[x][y].Location.X + marginsLeftToCenterTile,
                    DrawnBoard[x][y].Location.Y + marginsLeftToCenterTile),
            BackColor = DrawnBoard[x][y].BackColor
        };
        figuresImages[i].MouseDown += Figure_MouseDown;
        figuresImages[i].MouseMove += Figure_MouseMove;
        figuresImages[i].MouseUp += Figure_MouseUp;
        parentBoardPictureBox.Controls.Add(figuresImages[i]);
        figuresImages[i].BringToFront();
    }

    // Creating the Board's lines, rows and columns 

    private void DrawLines()
    {
        DrawLineNumbers();
        DrawLineAlphabet();
    }
    private void DrawLineNumbers()
    {
        const int horizontal = 5;
        int vertical = tileHeight;
        for (int i = 0; i < size; i++)
        {
            Label nextLabel = new Label
            {
                AutoSize = true,
                Location = new Point(horizontal, vertical),
                Text = (i + 1).ToString(),
                Font = new Font("Microsoft Sans Serif", 16),
            };
            vertical += tileHeight;
            Controls.Add(nextLabel);
        }
    }
    private void DrawLineAlphabet()
    {
        const int vertical = tileHeight*9 - 10;
        int horizontal = tileWidth - 15;
        for (int i = 0; i < size; i++)
        {
            Label nextLabel = new Label
            {
                AutoSize = true,
                Location = new Point(horizontal, vertical),
                Text = letters[i].ToString(),
                Font = new Font("Microsoft Sans Serif", 16),
            };
            horizontal += tileWidth;
            Controls.Add(nextLabel);
        }
    }
    private static void FillBoard()
    {
        for (int i = 0; i < size; i++)
        {
            bool[] row = new bool[size];
            bool value = i == 0 || i == 1 || i == 6 || i == 7;
            for (int j = 0; j < size; j++)
            {
                row[j] = value;
            }
            Board[i] = row;
        }
    }
    private void DrawBoard()
    {
        const int startHorizontal = 40;
        const int startVertical = 580;
        parentBoardPictureBox = new PictureBox
        {
            Location = new Point(startHorizontal, startVertical - 7*tileHeight),
            Size = new Size(8*tileWidth,8*tileHeight),
        };
        const int startHorizontalTile = 0;
        const int startVerticalTile = 7*tileHeight;
        Controls.Add(parentBoardPictureBox);
        int horizontal = startHorizontalTile;
        int vertical = startVerticalTile;
        bool white = false;
        for (int i = 0; i < DrawnBoard.Length; i++)
        {
            DrawnBoard[i] = CreateBoardRow(ref horizontal, ref vertical, ref white);
            white = !white;
            vertical -= tileHeight;
            horizontal = startHorizontalTile;
        }
    }
    private PictureBox[] CreateBoardRow(ref int horizontal,ref int vertical, ref bool white)
    {
        PictureBox[] Row = new PictureBox[size];
        for (int i = 0; i < Row.Length; i++)
        {
            Row[i] = new PictureBox
            {
                Size = tileSize,
                Location = new Point(horizontal, vertical),
                BorderStyle = BorderStyle.Fixed3D,
            };
            if (white)
            {
                white = false;
                Row[i].BackColor = Color.White;
            }
            else
            {
                white = true;
                Row[i].BackColor = Color.Black;
            }
            parentBoardPictureBox.Controls.Add(Row[i]);
            horizontal += tileWidth;
        }
        return Row;
    } 

    //Mouse events handling the movement of the figures and validates the figure's movement

    private void Figure_MouseDown(object sender, MouseEventArgs e)
    {
        PictureBox thisPB = (PictureBox)sender;
        isMoved = true;
        previousX = thisPB.Location.X;
        previousY = thisPB.Location.Y;
        int newColumn;
        int newRow = GetNewTile(thisPB, out newColumn);
        if (Properties.Settings.Default.EnabledTurnHelper)
        {
            Figure tempFigure = GetFigure(thisPB);
            if (tempFigure.PieceColor == currentColor)
            {
                tempFigure.UpdateMoves(tempFigure);
                foreach (Tuple<int, int> move in tempFigure.Moves)
                {
                    highlightedTiles.Add(new Tuple<int, int, Color>(move.Item1, move.Item2,
                        DrawnBoard[move.Item1][move.Item2].BackColor));
                    DrawnBoard[move.Item1][move.Item2].BackColor = Color.LightGreen;
                }
            }
        }
        passedTurn = new Tuple<Point, Figure, PictureBox>(new Point(previousX, previousY), GetFigure(thisPB), thisPB);
    }
    private void Figure_MouseMove(object sender, MouseEventArgs e)
    {
        if (isMoved)
        {
            PictureBox thisPB = (PictureBox)sender;
            thisPB.BringToFront();
            var temp = GetFigure(thisPB);
            if (temp.PieceColor == currentColor)
            {
                currentFigure = GetFigure(thisPB);
                thisPB.Location = new Point(thisPB.Left + e.X, thisPB.Top + e.Y);
                thisPB.Refresh();
            }
        }
    }
    private void Figure_MouseUp(object sender, MouseEventArgs e)
    {
        foreach (var highlightedTile in highlightedTiles)
        {
            DrawnBoard[highlightedTile.Item1][highlightedTile.Item2].BackColor = highlightedTile.Item3;
        }
        highlightedTiles.Clear();
        isMoved = false;
        KingIsDefeated();
        if (currentFigure == null) return;
        PictureBox thisPB = (PictureBox)sender;
        int newColumn;
        int newRow = GetNewTile(thisPB, out newColumn);
        if (MustReplacePawn(currentFigure, new Tuple<int, int>(newRow, newColumn)))
        {
            DoPawnReplaceMove(newRow, newColumn, thisPB);
            return;
        }
        Rochade rochade = new Rochade();
        Rochade.newKingMove = new Tuple<int, int>(newRow, newColumn);
        rochade.DoRochade(currentFigure);
        if (rochade.RochadeKing != null && rochade.RochadeKing.PieceColor == currentColor) // rochade succed
        {
            DoRochadeMove(rochade, thisPB);
            return;
        }

        currentFigure.UpdateMoves(currentFigure);
        if (currentFigure.IsValidMove(new Tuple<int, int>(newRow, newColumn)) &&
            !currentFigure.WillExposeKing(currentFigure, new Tuple<int, int>(newRow, newColumn)))
        {
            DoStandartMove(newRow, newColumn, thisPB);
        }
        else
        {
            thisPB.Location = new Point(previousX, previousY);
        }
        Color tempTileColor = GetTile(currentFigure).BackColor;
        thisPB.BackColor = tempTileColor;
        currentFigure = null;
        KingIsDefeated();
    }

    // All the different possible moves are being checked here


    private void KingIsDefeated()
    {
        Figure blackKing = BlackFigures.Single(blackFigure => blackFigure.PieceType == FigureType.King);
        Figure whiteKing = WhiteFigures.Single(whiteFigure => whiteFigure.PieceType == FigureType.King);
        int count = blackKing.Moves.Count(blackKingMove => blackKing.WillExposeKing(blackKing, blackKingMove));
        if (count == blackKing.Moves.Count && blackKing.Moves.Count > 0)
        {
            WinningCondition(blackKing);
        }
        count = whiteKing.Moves.Count(whiteKingMove => whiteKing.WillExposeKing(whiteKing, whiteKingMove));
        if (count == whiteKing.Moves.Count && whiteKing.Moves.Count > 0)
        {
            WinningCondition(whiteKing);
        }
    }

    private void DoRochadeMove(Rochade rochade, Control thisPB)
    {
        PassedTurns passedTurns = new PassedTurns();
        UpdateTimerValue();
        List<Figure> figureList = currentFigure.PieceColor == FigureColor.Black
            ? BlackFigures
            : WhiteFigures;
        Figure currentKing = figureList.Single(x => x.PieceType == FigureType.King);
        Figure currentRook = figureList.
            Single(x => x.PieceType == FigureType.Rook &&
                        Equals(x.StartingPosition, rochade.RochadeRook.StartingPosition));
        PictureBox rookPb = GetFigureTile(currentRook);
        RemoveMultipleFiguresFromTeam(currentKing, currentRook);
        RemoveMultipleFiguresFromFiguresList(currentKing, currentRook);
        currentKing = rochade.RochadeKing;
        currentRook = rochade.RochadeRook;
        AddMultipleFiguresToFiguresList(currentKing, currentRook);
        AddMultipleFiguresToTeam(currentKing, currentRook);
        currentKing.UpdateMoves(currentKing);
        currentRook.UpdateMoves(currentRook);
        rookPb.Location =
            new Point(
                DrawnBoard[currentRook.CurrentPosition.Item1][currentRook.CurrentPosition.Item2].Location.X +
                marginsLeftToCenterTile,
                DrawnBoard[currentRook.CurrentPosition.Item1][currentRook.CurrentPosition.Item2].Location.Y +
                marginsLeftToCenterTile);
        UpdateBoard();
        passedTurn = null;
        currentColor = currentColor == FigureColor.Black
            ? FigureColor.White
            : FigureColor.Black;
        currentFigure = currentKing;
        thisPB.Location =
            new Point(
                DrawnBoard[currentKing.CurrentPosition.Item1][currentKing.CurrentPosition.Item2].Location.X +
                marginsLeftToCenterTile,
                DrawnBoard[currentKing.CurrentPosition.Item1][currentKing.CurrentPosition.Item2].Location.Y +
                marginsLeftToCenterTile);
        currentTurn++;
        UpdateTurnTracking(passedTurns, currentFigure, "Rochade");
    }
    private void DoPawnReplaceMove(int newRow, int newColumn, Control thisPB)
    {
        PassedTurns passedTurns = new PassedTurns();
        UpdateTimerValue();
        currentFigure.CurrentPosition = new Tuple<int, int>(newRow, newColumn);
        thisPB.Location = new Point(DrawnBoard[newRow][newColumn].Location.X + marginsLeftToCenterTile,
            DrawnBoard[newRow][newColumn].Location.Y + marginsLeftToCenterTile);
        RemoveFromTeam(currentFigure);
        Figures.Remove(currentFigure);
        currentFigure = ReplacePawn(currentFigure, new Tuple<int, int>(newRow, newColumn), thisPB);
        AddToTeam(currentFigure);
        Figures.Add(currentFigure);
        currentFigure.UpdateMoves(currentFigure);
        passedTurn = null;
        UpdateBoard();
        currentTurn++;
        UpdateAllFiguresMoves();
        UpdateTurnTracking(passedTurns, currentFigure, "Replaced Pawn");
    }
    private void DoStandartMove(int newRow, int newColumn, Control thisPB)
    {
        PassedTurns passedTurns = new PassedTurns();
        UpdateTimerValue();
        currentFigure.WasMoved = true;
        currentFigure.CurrentPosition = new Tuple<int, int>(newRow, newColumn);
        thisPB.Location = new Point(DrawnBoard[newRow][newColumn].Location.X + marginsLeftToCenterTile,
            DrawnBoard[newRow][newColumn].Location.Y + marginsLeftToCenterTile);
        currentColor = currentColor == FigureColor.Black
            ? FigureColor.White
            : FigureColor.Black;
        string tempAction;
        if (currentFigure.WillCollideWithEnemy(currentFigure.CurrentPosition, currentFigure.PieceColor).Item1)
        {
            RemoveFigure(currentFigure);
            tempAction = "Took a Figure";
        }
        else
        {
            tempAction = "Normal";
        }
        UpdateBoard();
        currentTurn++;
        UpdateAllFiguresMoves();
        UpdateTurnTracking(passedTurns, currentFigure, tempAction);
    }

    // Checks and enables the timer if checked in the options

    private void UpdateTimerValue()
    {
        if (Properties.Settings.Default.EnabledTurnTimers)
        {
            pbTimer.Value = (int) timerMaxValue;
        }
    }

    /// <summary>
    /// Updates the Board's boolean array
    /// <seealso cref="Board"/>
    /// </summary>
    private static void UpdateBoard()
    {
        foreach (bool[] row in Board)
        {
            for (int j = 0; j < Board.Length; j++)
            {
                row[j] = false;
            }
        }
        for (int i = 0; i < Board.Length; i++)
        {
            for (int j = 0; j < Board.Length; j++)
            {
                if (figuresImages.TakeWhile(figureImage => figureImage != null)
                        .Any(figureImage => DrawnBoard[i][j].Location == figureImage.Location))
                {
                    Board[i][j] = true;
                }
            }
        }
    }

    /// <summary>
    /// Method to determine where the player has dropped his figure located in the
    /// <seealso cref="DrawnBoard"/>
    /// </summary>
    private static int GetNewTile(Control thisPB, out int newColumn)
    {
        int newRow = 0;
        newColumn = 0;
        int closestX = int.MaxValue;
        int closestY = int.MaxValue;
        for (int i = 0; i < DrawnBoard.Length; i++)
        {
            for (int j = 0; j < DrawnBoard.Length; j++)
            {
                int horizontalDifference = thisPB.Location.X - DrawnBoard[i][j].Location.X;
                int verticalDifference = thisPB.Location.Y - DrawnBoard[i][j].Location.Y;
                if (horizontalDifference <= closestX && verticalDifference <= closestY && horizontalDifference > -1 &&
                    verticalDifference > -1)
                {
                    closestX = thisPB.Location.X - DrawnBoard[i][j].Location.X;
                    closestY = thisPB.Location.Y - DrawnBoard[i][j].Location.Y;
                    newRow = i;
                    newColumn = j;
                }
            }
        }
        return newRow;
    }

    /// <summary>
    /// Method to determine which figure is being moved by comparing it's image to the ones in 
    /// <seealso cref="figuresImages"/>
    /// </summary>
    private static Figure GetFigure(Control sender)
    {
        return (from figuresImage in figuresImages.TakeWhile(figuresImage => figuresImage != null)
            where figuresImage.Location.X == sender.Location.X && figuresImage.Location.Y == sender.Location.Y
            from figure1 in Figures
            where Equals(figure1.PieceImage, figuresImage.BackgroundImage)
            select figure1).FirstOrDefault();
    }

    /// <summary>
    /// Method returning the current tile that the figure was placed on
    /// </summary>
    private static PictureBox GetTile(Figure inputCurrentFigure)
    {
        return DrawnBoard[inputCurrentFigure.CurrentPosition.Item1][inputCurrentFigure.CurrentPosition.Item2];
    }

    /// <summary>
    /// Method returning the current tile that the figure is standing on
    /// </summary>
    private static PictureBox GetFigureTile(Figure inputCurrentFigure)
    {
        return
            figuresImages.TakeWhile(figuresImage => figuresImage != null)
                .FirstOrDefault(
                    figuresImage =>
                        figuresImage.Location.X == GetTile(inputCurrentFigure).Location.X + marginsLeftToCenterTile &&
                        figuresImage.Location.Y == GetTile(inputCurrentFigure).Location.Y + marginsLeftToCenterTile &&
                        Equals(figuresImage.BackgroundImage, inputCurrentFigure.PieceImage));
    }

    //this button is currently disabled usually it rerolls the last turn played 
    //if a figure is removed the turn cant be undone
    private void bUndo_Click(object sender, EventArgs e)
    {
        if (passedTurn == null || currentTurn == 0)
        {
            return;
        }
        TurnTrackingList.Items[TurnTrackingList.Items.Count - 1].Remove();
        Tuple<Point, Figure, PictureBox> previous = passedTurn;
        previous.Item3.Location = previous.Item1;
        int newColumn;
        int newRow = GetNewTile(previous.Item3, out newColumn);
        previous.Item2.CurrentPosition = new Tuple<int, int>(newRow, newColumn);
        currentColor = currentColor == FigureColor.Black
            ? FigureColor.White
            : FigureColor.Black;
        if (Equals(previous.Item2.CurrentPosition, previous.Item2.StartingPosition))
        {
            previous.Item2.WasMoved = false;
        }
        UpdateBoard();
        UpdateAllFiguresMoves();
        currentTurn--;
    }

    /// <summary>
    /// Method removing figures also checking if the removed figure is king if so the game ends.
    /// </summary>
    private void RemoveFigure(Figure inputCurrentFigure)
    {
        Tuple<int, int> turnRoute = inputCurrentFigure.CurrentPosition;

        Figure enemyFigure = inputCurrentFigure.WillCollideWithEnemy(turnRoute, inputCurrentFigure.PieceColor).Item2;
        PictureBox enemyFigurePictureBox = GetFigureTile(enemyFigure);
        enemyFigurePictureBox.MouseMove -= Figure_MouseMove;
        enemyFigurePictureBox.MouseDown -= Figure_MouseDown;
        enemyFigurePictureBox.MouseUp -= Figure_MouseUp;
        enemyFigurePictureBox.BackColor = Color.White;
        if (Properties.Settings.Default.EnabledTurnTracking)
        {
            enemyFigurePictureBox.Width -= enemyFigurePictureBox.Width / 3;
            enemyFigurePictureBox.Height -= enemyFigurePictureBox.Height / 3;
        }
        if (inputCurrentFigure.PieceColor == FigureColor.Black)
        {
            flpBlackPlayerWinnings.Controls.Add(enemyFigurePictureBox);
            WhiteFigures.Remove(enemyFigure);
        }
        else
        {
            flpWhitePlayerWinnings.Controls.Add(enemyFigurePictureBox);
            BlackFigures.Remove(enemyFigure);
        }
        if (enemyFigure.PieceType == FigureType.King)
        {
            WinningCondition(enemyFigure);
            return;
        }
        inputCurrentFigure.WillCollideWithEnemy(turnRoute, inputCurrentFigure.PieceColor).Item2.CurrentPosition =
            new Tuple<int, int>(-1, -1);
        passedTurn = null;

    }

    private void WinningCondition(Figure enemyFigure)
    {
        const string replayText = @"Would You like to play again ?";
        string winningText = @"White Player Wins !";
        if (enemyFigure.PieceColor == FigureColor.White)
        {
            winningText = @"Black Player Wins !";
        }
        DialogResult replayDialog = MessageBox.Show(winningText + Environment.NewLine + replayText,
            @"Congratulations", MessageBoxButtons.YesNo);
        if (replayDialog == DialogResult.Yes)
        {
            Restart();
        }
        else
        {
            DialogResult exitApplicationDialog =
                MessageBox.Show(@"You are about to exit the application are you sure ?", @"Quit",
                    MessageBoxButtons.YesNo);
            if (exitApplicationDialog == DialogResult.Yes)
            {
                Application.Exit();
            }
            else
            {
                Close();
            }
        }
    }
    private void Restart()
    {
        passedTurn = null;
        Figures.Clear();
        for (int i = 0; i < figuresImages.Length; i++)
        {
            if (figuresImages[i] == null)
            {
                break;
            }
            figuresImages[i].Dispose();
            figuresImages[i] = null;
        }
        isMoved = false;
        currentFigure = null;
        previousX = 0;
        previousY = 0;
        currentColor = currentColor == FigureColor.Black
            ? FigureColor.White
            : FigureColor.Black;
        currentTurn = 0;
        WhiteFigures.Clear();
        BlackFigures.Clear();

        AddKings();
        AddQueens();
        AddRooks();
        AddBishops();
        AddKnights();
        AddPawns();

        DrawFigures();
    }

    /// <summary>
    /// Whenever one figure is moved all the other figures moves also change so this is called whenever a figure is being moved
    /// </summary>
    private static void UpdateAllFiguresMoves()
    {
        foreach (var figure in Figures)
        {
            figure.UpdateMoves(figure);
        }
    }

    /// <summary>
    /// Method to replace the pawn with selected figure from 
    /// <seealso cref="ReplacePawnForm"/>
    /// </summary>
    private static Figure ReplacePawn(Figure currentFigure, Tuple<int, int> newMove,Control pb)
    {
        ReplacePawnForm replacedPawnForm = new ReplacePawnForm(currentFigure.PieceColor, newMove);
        replacedPawnForm.ShowDialog();
        currentFigure = replacedPawnForm.ReplacedFigure;
        pb.BackgroundImage = currentFigure.PieceImage;
        return currentFigure;
    }

    /// <summary>
    /// Method that determines if a pawn must be replaced i.e it has reached the other end of the board
    /// </summary>
    private static bool MustReplacePawn(Figure currentFigure, Tuple<int,int> newMove)
    {
        if (currentFigure.PieceType != FigureType.Pawn || !currentFigure.Moves.Contains(newMove)) return false;
        if (currentFigure.PieceColor == FigureColor.Black)
        {
            if (newMove.Item1 == 0)
            {
                return true;
            }
        }
        else
        {
            if (newMove.Item1 == 7)
            {
                return true;
            }
        }
        return false;
    }

    //Methods to enable and update turn tracking if it was checked in Options
    private void EnableTurnTracking()
    {
        TurnTrackingList = new ListView
        {
            View = View.Details,
            GridLines = true,
            Location = new Point(flpWhitePlayerWinnings.Location.X, flpWhitePlayerWinnings.Location.Y + flpWhitePlayerWinnings.Height + 40),
            Size = new Size(flpWhitePlayerWinnings.Width, flpWhitePlayerWinnings.Height * 2 - 30),
            BorderStyle = BorderStyle.FixedSingle,
            BackColor = Color.White
        };
        TurnTrackingList.Columns.Add("Turn", -2, HorizontalAlignment.Left);
        TurnTrackingList.Columns.Add("White Player", -2, HorizontalAlignment.Left);
        TurnTrackingList.Columns.Add("Black Player", -2, HorizontalAlignment.Left);
        TurnTrackingList.Columns.Add("Piece Type", -2, HorizontalAlignment.Left);
        TurnTrackingList.Columns.Add("Action", -2, HorizontalAlignment.Left);
        TurnTrackingList.AutoResizeColumn(0, ColumnHeaderAutoResizeStyle.HeaderSize);
        Controls.Add(TurnTrackingList);
    }
    private void UpdateTurnTrackingList(FigureColor figureColor)
    {
        int actualTurn = currentTurn%2 == 0 ? currentTurn/2 : currentTurn/2 + 1;
        ListViewItem turnTrackingRow = new ListViewItem(actualTurn.ToString(), 0); // first item only here
        object[] turnTrackingRowItems = new object[4];
        if (figureColor == FigureColor.Black)
        {
            Tuple<int, int> tempPosition = (Tuple<int, int>)blackTurns[actualTurn - 1][(int)PassedTurns.ItemsOrder.Position];
            int x = tempPosition.Item2;
            int y = size - tempPosition.Item1;
            string formatedPosition = letters[x] + " , " + y;
            turnTrackingRowItems[(int) PassedTurns.ListsOrder.WhitePlayer] = string.Empty;
            turnTrackingRowItems[(int) PassedTurns.ListsOrder.BlackPlayer] = formatedPosition;
            turnTrackingRowItems[(int) PassedTurns.ListsOrder.PieceType] =
                blackTurns[actualTurn - 1][(int) PassedTurns.ItemsOrder.PieceType];
            turnTrackingRowItems[(int) PassedTurns.ListsOrder.Action] =
                blackTurns[actualTurn - 1][(int) PassedTurns.ItemsOrder.Action];
        }
        else
        {
            Tuple<int, int> tempPosition = (Tuple<int, int>)whiteTurns[actualTurn - 1][(int)PassedTurns.ItemsOrder.Position];
            int x = tempPosition.Item2;
            int y = size - tempPosition.Item1;
            string formatedPosition = letters[x] + " , " + y;
            turnTrackingRowItems[(int) PassedTurns.ListsOrder.BlackPlayer] = string.Empty;
            turnTrackingRowItems[(int) PassedTurns.ListsOrder.WhitePlayer] = formatedPosition;
            turnTrackingRowItems[(int) PassedTurns.ListsOrder.PieceType] =
                whiteTurns[actualTurn - 1][(int) PassedTurns.ItemsOrder.PieceType];
            turnTrackingRowItems[(int) PassedTurns.ListsOrder.Action] =
                whiteTurns[actualTurn - 1][(int) PassedTurns.ItemsOrder.Action];
        }
        foreach (object item in turnTrackingRowItems)
        {
            turnTrackingRow.SubItems.Add(item.ToString());
        }
        TurnTrackingList.Items.AddRange(new[] {turnTrackingRow});
    }
    private void UpdateTurnTracking(PassedTurns passedTurns, Figure inputFigure,string inputAction)
    {
        passedTurns.AddNewMove(inputFigure.CurrentPosition, inputFigure.PieceType, inputAction);
        if (inputFigure.PieceColor == FigureColor.Black)
        {
            blackTurns.Add(passedTurns.GetPassedTurns());
        }
        else
        {
            whiteTurns.Add(passedTurns.GetPassedTurns());
        }
        UpdateTurnTrackingList(inputFigure.PieceColor);
    }
}

Most of my comments are here so I hope that it will be a little bit easier to understand what's going on.


Figures

The Figure class is the base class for all figures:

public abstract class Figure
{
    public enum FigureType
    {
        Pawn,
        Knight,
        Bishop,
        Rook,
        Queen,
        King
    }

    public enum FigureColor
    {
        White,
        Black
    }

    private readonly FigureDefinition definition;

    public List<Tuple<int, int>> Moves { get; set; }

    public bool WasMoved
    {
        get { return definition.WasMoved; }
        set { definition.WasMoved = value; }
    }
    public FigureColor PieceColor
    {
        get { return definition.PieceColor; }
        set { definition.PieceColor = value; }
    }

    public FigureType PieceType
    {
        get { return definition.PieceType; }
        set { definition.PieceType = value; }
    }

    public Image PieceImage
    {
        get { return definition.PieceImage; }
        set { definition.PieceImage = value; }
    }

    public Tuple<int, int> StartingPosition
    {
        get { return definition.StartingPosition; }
        set { definition.StartingPosition = value; }
    }

    public Tuple<int, int> CurrentPosition
    {
        get { return definition.CurrentPosition; }
        set { definition.CurrentPosition = value; }
    }

    protected Figure(FigureDefinition definition)
    {
        this.definition = definition;
    }

    protected abstract List<Tuple<int, int>> GetValidTurns();

    public bool IsValidMove(Tuple<int, int> newPosition) => Moves.Contains(newPosition);

    protected bool IsOutOfBounds(Tuple<int, int> newPosition) =>
        newPosition.Item1 > CooperativeForm.size - 1 ||
        newPosition.Item2 > CooperativeForm.size - 1 ||
        newPosition.Item1 < 0 ||
        newPosition.Item2 < 0;

    public bool WillCollideWithAlly(Tuple<int, int> turnRoute, FigureColor currentColor)
    {
        bool willCollide = false;
        foreach (Figure figure in CooperativeForm.Figures)
        {
            if (figure == null)
            {
                break;
            }
            if (figure.CurrentPosition.Item1 == turnRoute.Item1 &&
                figure.CurrentPosition.Item2 == turnRoute.Item2 &&
                figure.PieceColor == currentColor)
            {
                willCollide = true;
            }
        }
        return willCollide;
    }

    public Tuple<bool, Figure> WillCollideWithEnemy(Tuple<int, int> turnRoute, FigureColor currentColor)
    {
        bool willCollide = false;
        Figure enemyFigure = null;
        foreach (Figure figure in CooperativeForm.Figures)
        {
            if (figure == null)
            {
                break;
            }
            if (figure.CurrentPosition.Item1 == turnRoute.Item1 &&
                figure.CurrentPosition.Item2 == turnRoute.Item2 &&
                figure.PieceColor != currentColor)
            {
                willCollide = true;
                enemyFigure = figure;
                break;
            }
        }
        return new Tuple<bool, Figure>(willCollide, enemyFigure);
    }

    public bool WillExposeKing(Figure currentFigure, Tuple<int, int> newPosition)
    {
        List<Figure> enemyPieces = currentFigure.PieceColor == FigureColor.Black
            ? CooperativeForm.WhiteFigures
            : CooperativeForm.BlackFigures;
        List<Figure> allyPieces = currentFigure.PieceColor == FigureColor.Black
            ? CooperativeForm.BlackFigures
            : CooperativeForm.WhiteFigures;

        Figure allyKing = allyPieces.SingleOrDefault(x => x.PieceType == FigureType.King);
        if (allyKing == null) return false;
        Tuple<int, int> previousPosition =
            allyPieces.SingleOrDefault(x => Equals(x.CurrentPosition, currentFigure.CurrentPosition))?
                .CurrentPosition;
        if (previousPosition == null) return false;
        currentFigure.CurrentPosition = newPosition;
        foreach (var enemyPiece in enemyPieces)
        {
            enemyPiece.Moves = enemyPiece.GetValidTurns();
            if (enemyPiece.Moves.Contains(allyKing.CurrentPosition))
            {
                if (!Equals(currentFigure.CurrentPosition, enemyPiece.CurrentPosition))
                {
                    currentFigure.CurrentPosition = previousPosition;
                    return true;
                }
            }
        }
        currentFigure.CurrentPosition = previousPosition;
        return false;
    }

    protected List<Tuple<int, int>> RemoveFailedTurns(Figure currentFigure, List<Tuple<int, int>> inputValidMoves)
    {
        List<Tuple<int,int>> itemsToRemove = inputValidMoves.Where(inputValidMove => WillCollideWithAlly(inputValidMove, currentFigure.PieceColor)).ToList();
        foreach (Tuple<int, int> item in itemsToRemove)
        {
            inputValidMoves.Remove(item);
        }
        return inputValidMoves;
    }

    public void UpdateMoves(Figure inputFigure)
    {
        inputFigure.Moves = RemoveFailedTurns(this, GetValidTurns()).Distinct().ToList();
    }
}

Combined with the FigureDefinition Class :

public class FigureDefinition
{
    public Figure.FigureColor PieceColor { get; set; }
    public Figure.FigureType PieceType { get; set; }
    public Image PieceImage { get; set; }
    public Tuple<int, int> StartingPosition { get; set; }
    public Tuple<int, int> CurrentPosition { get; set; }
    public Tuple<int, int>[] ValidTurns { get; set; }
    public bool WasMoved { get; set; }
}

Now every single Piece is inheriting this Figure class

PawnPiece Class

public sealed class PawnPiece : Figure
{
    public PawnPiece(FigureDefinition definition) : base(definition)
    {
        Moves = RemoveFailedTurns(this, GetValidTurns());
        Moves = Moves.Distinct().ToList();
    }

    protected override List<Tuple<int, int>> GetValidTurns()
    {
        List<Tuple<int, int>> validMoves = new List<Tuple<int, int>>();
        int newRow = PieceColor == FigureColor.White ? 1 : -1;

        if (Equals(StartingPosition, CurrentPosition))
        {
            Tuple<int, int> moveForwardStarting = new Tuple<int, int>(CurrentPosition.Item1 + 2 * newRow,
                CurrentPosition.Item2);
            if (!WillCollideWithAlly(moveForwardStarting, PieceColor) &&
                !WillCollideWithEnemy(moveForwardStarting, PieceColor).Item1)
            {
                validMoves.Add(moveForwardStarting);
            }
        }
        Tuple<int, int> moveForward = new Tuple<int, int>(CurrentPosition.Item1 + newRow, CurrentPosition.Item2);
        Tuple<int, int> moveLeftDiagonal = new Tuple<int, int>(CurrentPosition.Item1 + newRow,
            CurrentPosition.Item2 - 1);
        Tuple<int, int> moveRightDiagonal = new Tuple<int, int>(CurrentPosition.Item1 + newRow,
            CurrentPosition.Item2 + 1);
        if (!IsOutOfBounds(moveForward) &&
            !WillCollideWithEnemy(moveForward, PieceColor).Item1 &&
            !WillCollideWithAlly(moveForward, PieceColor))
        {
            validMoves.Add(moveForward);
        }
        if (!IsOutOfBounds(moveLeftDiagonal) &&
            WillCollideWithEnemy(moveLeftDiagonal, PieceColor).Item1 &&
            !WillCollideWithAlly(moveLeftDiagonal, PieceColor))
        {
            validMoves.Add(moveLeftDiagonal);
        }
        if (!IsOutOfBounds(moveRightDiagonal) &&
            WillCollideWithEnemy(moveRightDiagonal, PieceColor).Item1 &&
            !WillCollideWithAlly(moveRightDiagonal, PieceColor))
        {
            validMoves.Add(moveRightDiagonal);
        }
        return validMoves;
    }
}

Each Piece class is combined with a PieceDefinitons Class which looks like this

public class PawnDefinitions
{
    private static readonly GeneratePieces generatedPieces = new GeneratePieces(Figure.FigureType.Pawn, 0, 7, 1,
        ImagePaths.WhitePawnImagePath, ImagePaths.BlackPawnImagePath);

    public IEnumerable<FigureDefinition> WhitePawns = generatedPieces.GenerateWhitePieces();
    public IEnumerable<FigureDefinition> BlackPawns = generatedPieces.GenerateBlackPieces();
}

Here we see the Class GeneratePiece I'm going to post it after all the other piece's class's.

KnightPiece Class :

public sealed class KnightPiece : Figure
{
    public KnightPiece(FigureDefinition definition) : base(definition)
    {
        Moves = RemoveFailedTurns(this, GetValidTurns());
        Moves = Moves.Distinct().ToList();
    }

    protected override List<Tuple<int, int>> GetValidTurns()
    {
        int newRow = PieceColor == FigureColor.White ? 1 : -1;
        List<Tuple<int, int>> tempMoves = new List<Tuple<int, int>>
        {
            new Tuple<int,
                int>(CurrentPosition.Item1 + 1,
                    CurrentPosition.Item2 + 2),
            new Tuple<int,
                int>(CurrentPosition.Item1 + 2,
                    CurrentPosition.Item2 + 1),
            new Tuple<int,
                int>(CurrentPosition.Item1 - 1,
                    CurrentPosition.Item2 + 2),
            new Tuple<int,
                int>(CurrentPosition.Item1 - 2,
                    CurrentPosition.Item2 + 1),
            new Tuple<int,
                int>(CurrentPosition.Item1 + 2,
                    CurrentPosition.Item2 - 1),
            new Tuple<int,
                int>(CurrentPosition.Item1 - 2,
                    CurrentPosition.Item2 - 1),
            new Tuple<int,
                int>(CurrentPosition.Item1 + 1,
                    CurrentPosition.Item2 - 2),
            new Tuple<int,
                int>(CurrentPosition.Item1 - 1,
                    CurrentPosition.Item2 - 2)
        };
        List<Tuple<int, int>> validMoves =
            tempMoves.Where(
                tempMove =>
                    !IsOutOfBounds(tempMove) &&
                    !WillCollideWithAlly(tempMove, PieceColor)).ToList();
        return  validMoves;
    }
}

KnightsDefinitons Class :

public class KnightDefinitions
{
    private static readonly GeneratePieces generatedPieces = new GeneratePieces(Figure.FigureType.Knight, 1, 6, 5,
        ImagePaths.WhiteKnightImagePath, ImagePaths.BlackKnightImagePath);

    public IEnumerable<FigureDefinition> WhiteKnights = generatedPieces.GenerateWhitePieces();
    public IEnumerable<FigureDefinition> BlackKnights = generatedPieces.GenerateBlackPieces();
}

BishopPiece Class :

public sealed class BishopPiece : Figure
{
    public BishopPiece(FigureDefinition definition) : base(definition)
    {
        Moves = RemoveFailedTurns(this, GetValidTurns());
        Moves = Moves.Distinct().ToList();
    }

    protected override List<Tuple<int, int>> GetValidTurns()
    {
        int n = 1;
        Tuple<int, int> rightUpDiagonal = new Tuple<int, int>(CurrentPosition.Item1 + n, CurrentPosition.Item2 + n);
        Tuple<int, int> leftUpDiagonal = new Tuple<int, int>(CurrentPosition.Item1 + n, CurrentPosition.Item2 - n);
        Tuple<int, int> rightDownDiagonal = new Tuple<int, int>(CurrentPosition.Item1 - n, CurrentPosition.Item2 + n);
        Tuple<int, int> leftDownDiagonal = new Tuple<int, int>(CurrentPosition.Item1 - n, CurrentPosition.Item2 - n);
        List<Tuple<int, int>> startingMoves = new List<Tuple<int, int>>
        {
            rightUpDiagonal,
            leftUpDiagonal,
            rightDownDiagonal,
            leftDownDiagonal
        };
        List<Tuple<int, int>> validMoves =
            startingMoves.Where(
                startingMove =>
                    !IsOutOfBounds(startingMove) && !WillCollideWithAlly(startingMove, PieceColor))
                .ToList();

        while (!IsOutOfBounds(rightUpDiagonal) && !WillCollideWithAlly(rightUpDiagonal, PieceColor) &&
               !WillCollideWithEnemy(rightUpDiagonal, PieceColor).Item1)
        {
            validMoves.Add(rightUpDiagonal);
            n++;
            rightUpDiagonal = new Tuple<int, int>(CurrentPosition.Item1 + n, CurrentPosition.Item2 + n);
            if (WillCollideWithEnemy(rightUpDiagonal, PieceColor).Item1)
            {
                validMoves.Add(rightUpDiagonal);
                break;
            }
        }

        n = 1;
        while (!IsOutOfBounds(leftUpDiagonal) && !WillCollideWithAlly(leftUpDiagonal, PieceColor) &&
               !WillCollideWithEnemy(leftUpDiagonal, PieceColor).Item1)
        {
            validMoves.Add(leftUpDiagonal);
            n++;
            leftUpDiagonal = new Tuple<int, int>(CurrentPosition.Item1 + n, CurrentPosition.Item2 - n);
            if (WillCollideWithEnemy(leftUpDiagonal, PieceColor).Item1)
            {
                validMoves.Add(leftUpDiagonal);
                break;
            }
        }

        n = 1;
        while (!IsOutOfBounds(rightDownDiagonal) && !WillCollideWithAlly(rightDownDiagonal, PieceColor) &&
               !WillCollideWithEnemy(rightDownDiagonal, PieceColor).Item1)
        {
            validMoves.Add(rightDownDiagonal);
            n++;
            rightDownDiagonal = new Tuple<int, int>(CurrentPosition.Item1 - n, CurrentPosition.Item2 + n);
            if (WillCollideWithEnemy(rightDownDiagonal, PieceColor).Item1)
            {
                validMoves.Add(rightDownDiagonal);
                break;
            }
        }

        n = 1;
        while (!IsOutOfBounds(leftDownDiagonal) && !WillCollideWithAlly(leftDownDiagonal, PieceColor) &&
               !WillCollideWithEnemy(leftDownDiagonal, PieceColor).Item1)
        {
            validMoves.Add(leftDownDiagonal);
            n++;
            leftDownDiagonal = new Tuple<int, int>(CurrentPosition.Item1 - n, CurrentPosition.Item2 - n);
            if (WillCollideWithEnemy(leftDownDiagonal, PieceColor).Item1)
            {
                validMoves.Add(leftDownDiagonal);
                break;
            }
        }
        return validMoves;
    }
}

BishopDefinitons Class :

public class BishopDefinitions
{
    private static readonly GeneratePieces generatedPieces = new GeneratePieces(Figure.FigureType.Bishop, 2, 5, 3,
        ImagePaths.WhiteBishopImagePath, ImagePaths.BlackBishopImagePath);

    public IEnumerable<FigureDefinition> WhiteBishops = generatedPieces.GenerateWhitePieces();
    public IEnumerable<FigureDefinition> BlackBishops = generatedPieces.GenerateBlackPieces();
}

Part 2 here

\$\endgroup\$
  • \$\begingroup\$ Relevant read on meta. (yes, that's 2 links ;-) \$\endgroup\$ – Mathieu Guindon May 11 '16 at 2:20
  • \$\begingroup\$ Consider dividing all of your code into two sections: one section which deals exclusively with the rules of chess and knows nothing about the user interface; and one section that deals exclusively with the user interface and knows nothing about the rules of chess. Then you'll be able to work on one section without having to think about the other section. \$\endgroup\$ – Tanner Swett May 13 '16 at 0:27
4
\$\begingroup\$

Some brief thoughts:

  • Too many tuples. Whatever notion pair-of-ints represents, it is plainly important. Important enough to have a class or struct of its own. Make the program read like its semantics; no one thinks of chess as being a game of tuples of ints.

  • There is a LOT of duplicated code in the valid move checker. Can this be simplified? Can it be broken up into smaller methods?

  • Valid moves end with the player not in check. I don't see that computed anywhere.

  • You will need to know whether the king and rooks have moved; you don't need to know that for any other pieces. However, it doesn't hurt to track this for all pieces.

  • A king may not castle through check. You don't check for that in your valid moves.

  • When a pawn is promoted, does it maintain referential identity? Or is it replaced by a newly allocated piece? Either is fine but think about how you're going to represent that.

  • You do not need to know if a pawn has moved; you need to know when a pawn has moved in order to get the en passant rule right.

  • The whole notion that code in the piece class should determine the legal rules is deeply suspect. When playing chess we do not ask the queen what the legal moves are for the queen; we consult the rules. You think you are doing good OO design by putting stuff in classes, but that is not necessarily good OO design. I would be inclined to have an object called Rulebook, which has a method which takes a piece, a board state, and returns a list of legal moves for that piece. Don't ask the piece, ask the rule book!

  • A word of advice: make everything immutable now. When you write your AI, you're going to want to be able to represent many different variations on the same board. When you make a move, that doesn't change the board. It makes a whole new board. That way to go back to the old board you don't have to undo anything.

\$\endgroup\$
  • \$\begingroup\$ "I would be inclined to have an object called Rulebook, which has a method which takes a piece, a board state, and returns a list of legal moves for that piece. Don't ask the piece, ask the rule book!" - Good idea. I might do this for my own chess program. \$\endgroup\$ – AdmiralAdama Aug 14 '18 at 0:56
2
\$\begingroup\$

That's a lot of code to look through. I'll focus on a section of it for the time being. In the BishopPiece class, all of the loops getting valid moves for a direction are similar. Each loop can be replaced with a method call, with parameters handling the differences. Here is the method:

private List<Tuple<int, int>> GetValidTurnsInADirection(
    Tuple<int, int> currentPosition, int xDirection, int yDirection)
{
    List<Tuple<int, int>> validMoves = new List<Tuple<int, int>>();
    Tuple<int, int> newPosition = new Tuple<int, int>(
            currentPosition.Item1 + xDirection, currentPosition.Item2 + yDirection);
    while (!IsOutOfBounds(newPosition) && !WillCollideWithAlly(newPosition, PieceColor))
    {
        validMoves.Add(newPosition);
        if (WillCollideWithEnemy(newPosition, PieceColor).Item1)
        {
            break;
        }
        newPosition = new Tuple<int, int>(
            newPosition.Item1 + xDirection, newPosition.Item2 + yDirection);
    }
}

GetValidTurns() can then be written like this:

protected override List<Tuple<int, int>> GetValidTurns()
{
    List<Tuple<int, int>> rightUpMoves = GetValidTurnsInADirection(CurrentPosition, 1, 1);
    List<Tuple<int, int>> leftUpMoves = GetValidTurnsInADirection(CurrentPosition, 1, -1);
    List<Tuple<int, int>> rightDownMoves = GetValidTurnsInADirection(CurrentPosition, -1, 1);
    List<Tuple<int, int>> leftDownMoves = GetValidTurnsInADirection(CurrentPosition, -1, -1);
    List<Tuple<int, int>> validMoves = rightUpMoves
        .Concat(leftUpMoves)
        .Concat(rightDownMoves)
        .Concat(leftDownMoves)
        .ToList();
    return validMoves;
}

You can also use that helper method for getting the valid turns for a rook.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.