I am looking for some advice to this code I made, which enables a fellow programmer to create his own AI for Connect Four whilst not really having to code the game itself.Match classpublic sealed class Match{    public const int cWidth = 7;    public const int cHeight = 6;    private static Random Random = new Random();    private int _currentTurn;    private List<BasePlayer> _players;    private bool _isGameOver;     private Disc[,] _discs;    internal Disc[,] Discs => _discs;    internal BasePlayer CurrentPlayer { get; set; }    public int Width { get; private set; }    public int Height { get; private set; }    public Disc RecentDisc { get; internal set; }    public bool IsPositionWithinBoundries(int x, int y)    {        if (x < 0 || x >= Width)            return false;        if (y < 0 || y >= Height)            return false;        return true;    }    public bool IsRowValidMove(int x)    {        if (!IsPositionWithinBoundries(x, 0))            return false;        for (int y = 0; y < Height; y++)            if (_discs[x, y] == null)                return true;        return false;    }    public int GetColumnHeight(int x)    {        if (!IsPositionWithinBoundries(x, 0))            return -1;        int y = 0;        for (; y < Height; y++)            if (_discs[x, y] == null)                return y;        return y;    }    public Disc GetDiscAt(int x, int y)    {        if (!IsPositionWithinBoundries(x, y))            return null;        return _discs[x, y];    }    public Disc GetLastDiscInColumn(int x)    {        var y = GetColumnHeight(x);        return GetDiscAt(x, y - 1);    }    public static Match CreateMatch(BasePlayer player1, BasePlayer player2)    {        return CreateMatch(player1, player2, cWidth, cHeight);    }    public static Match CreateMatch(BasePlayer player1, BasePlayer player2, int width, int height)    {        if (player1 == null)            throw new ArgumentNullException(nameof(BasePlayer) + 1 is null);        if (player2 == null)            throw new ArgumentNullException(nameof(BasePlayer) + 2 is null);        if (width < 1)            throw new ArgumentNullException(nameof(width) + 1 is null);        if (height < 1)            throw new ArgumentNullException(nameof(BasePlayer) + 2 is null);        return new Match(player1, player2, width, height);    }    public void StartMatch()    {        if (_isGameOver)            return;        for (;;)        {            _isGameOver = true;             foreach (var player in _players)            {                _currentTurn++;                if (CheckGridFull())                {                    EndGame(Draw - Grid is filled);                    return;                }                CurrentPlayer = player;                CurrentPlayer.PlanMove();                DoMove(CurrentPlayer);                if (CheckWin())                {                    EndGame(CurrentPlayer.Name +  wins);                    return;                }            }        }    }    private bool CheckWin()    {        if (CheckDirectionsForDisc(RecentDisc))            return true;        return false;    }    private bool CheckGridFull()    {        for (int x = Width - 1; x >= 0; x--)        {            for (int y = Height - 1; y >= 0; y--)            {                if (GetDiscAt(x, y) == null)                    return false;            }        }        return true;    }    private bool CheckDirectionsForDisc(Disc disc)    {        return            CheckDirectionForDisc(disc, EDirection.Diagonal) ||            CheckDirectionForDisc(disc, EDirection.Vertical) ||            CheckDirectionForDisc(disc, EDirection.Horizontal);    }    private bool CheckDirectionForDisc(Disc disc, EDirection direction)    {        var streak = 1;        for (int i = 1; i < 4; i++)        {            var x = disc.X + i * (direction.GetOffsetX());            var y = disc.Y + i * (direction.GetOffsetY());            var curDisc = GetDiscAt(x, y);            if (curDisc != null && curDisc.Owner == disc.Owner)                streak++;            else                break;        }        for (int i = -1; i > -4; i--)        {            var x = disc.X + i * (direction.GetOffsetX());            var y = disc.Y + i * (direction.GetOffsetY());            var curDisc = GetDiscAt(x, y);            if (curDisc != null && curDisc.IsMine())                streak++;            else                break;        }        return streak >= 4;    }    internal void EndGame(String message)    {        PrintField();    }    private void PrintField()    {        for (int y = Height - 1; y >= 0; y--)        {            System.Diagnostics.Debug.WriteLine();            for (int x = 0; x < Width; x++)            {                {                    var disc = GetDiscAt(x, y);                    if (disc == null)                        System.Diagnostics.Debug.Write(-);                    else if (disc.Owner == _players[0])                        System.Diagnostics.Debug.Write(X);                    else                        System.Diagnostics.Debug.Write(0);                }            }        }    }    internal void DoMove(BasePlayer player)    {        var y = GetColumnHeight(player.NextColumn);        if (IsPositionWithinBoundries(player.NextColumn, y))            Disc.CreateDisc(this, player.NextColumn, y);    }    private Match(BasePlayer player1, BasePlayer player2, int width, int height)    {        Width = width;         Height = height;        _discs = new Disc[Width, Height];        player1.Match = this;        player2.Match = this;        _players = new List<BasePlayer>();        //var swapPlayers = Random.NextDouble() >= 0.5;        _players.Add(player1);        //if (swapPlayers)        _players.Add(player2);        //else        //    _players.Insert(0, player2);    }}BasePlayer which library users would have to extendpublic abstract class BasePlayer{    public Match Match { get; internal set; }    internal String Name { get; private set; }    private int _nextRow = 0;    public int NextColumn    {        get { return _nextRow; }        protected set        {            //if(Match.IsRowValidMove(row))            _nextRow = value;        }    }    public abstract void PlanMove();    public abstract void Initialize();}Discpublic sealed class Disc{    private Match _match;    public int X { get; }    public int Y { get; }    internal BasePlayer Owner { get; }    internal static Disc CreateDisc(Match match, int x, int y)    {        if (match == null)            throw new ArgumentNullException(nameof(Match));        if (match.CurrentPlayer == null)            throw new NullReferenceException(nameof(match.CurrentPlayer) +  == null);        if (!match.IsPositionWithinBoundries(x, y))            throw new ArgumentOutOfRangeException(x and y out of boundries);        if (!match.IsRowValidMove(x))            throw new ArgumentOutOfRangeException(x exceeds row);        if (match.GetDiscAt(x, y) != null)            throw new ArgumentException(position already occupied);        return new Disc(match, x, y, match.CurrentPlayer);    }    private Disc(Match match, int x, int y, BasePlayer owner)    {        _match = match;        X = x;        Y = y;        Owner = owner;        match.Discs[x, y] = this;        match.RecentDisc = this;     }    public bool IsMine()    {        return _match.CurrentPlayer == Owner;    }    public override string ToString()    {        return Owner.Name +   + base.ToString();    }}enumsinternal enum EDirection{    Horizontal, Vertical, Diagonal}internal static class EDirectionExtension{    internal static int GetOffsetX(this EDirection direction)    {        switch (direction)        {            case EDirection.Horizontal:            case EDirection.Diagonal:                return 1;            case EDirection.Vertical:            default:                return 0;        }    }    internal static int GetOffsetY(this EDirection direction)    {        switch (direction)        {            case EDirection.Vertical:            case EDirection.Diagonal:                return 1;            default:            case EDirection.Horizontal:                return 0;        }    }}
Library to play and create AI for Connect Four
c#;library;connect four