About a year ago, my brother and I came up with this cool idea for how to solve the problem of determining if any given cell on a Chinese Checkers board was adjacent to the cell in question.Recently we started digging our teeth back into the idea and how we could work on variations of the game in which more than directly adjacent cells are of interest. I took up the task of figuring out how to adjust the grid system.We'd previously developed some concept code in both C++ and C#, but decided for this project to work in C# (which I'm a little rusty in; been working with C and C++ mostly lately). To start, I merged the concepts of coordinates and direction into a more mathematical vector concept. However, it quickly became apparent that it is valuable to have a type-enforced way of separating unit vectors from vectors of non-unit magnitude.For instance, suppose I want Grid.Cell.Neighbor to require a unit vector as a parameter. I could include conditional code, and either return null or throw exceptions, but that kind of overhead seems excessive for this simple task (not to mention forcing calling code to add potential run-time error checking for something that should be able to be validated at compile-time).That said, I like the idea of keeping the Vector struct/class as simple as possible because it's not a terribly complex structure (and wouldn't warrant a polymorphic class in my typical C++ projects; but instead a simple typedef).I'm looking for suggestions on the cleanest, lightest (is that an oxymoron?), or in other words most preferred C# way of enforcing the use of unit vectors. Additionally, I'm open to any comments about the interfaces publicly accessible outside of the Grid class as far as good practice for C# goes.using System;using System.Collections.Generic;namespace Cs_Console_Test {    public class Program {        const string TableHeader =  X  \t YL \t YR ;        static string TableRow(Grid.Vector vec) {            return String.Format({0,4}\t{1,4}\t{2,4}, vec.X, vec.YL, vec.YR);        }        // Tests the Neighbor member function        static void PrintNeighbors(Grid.ICell Center) {            Console.WriteLine(    Neighbors of ({0}, {1}, {2}),                Center.Location.X, Center.Location.YL, Center.Location.YR);            Console.WriteLine(TableHeader + \t->\tValue);            foreach (Grid.Vector unit in Grid.Vector.Units) {                Grid.Vector? Translated = Center.Location + unit;                Grid.ICell Neighbor = Center.Neighbor(unit);                string Location =                    Translated == null                    ? X: null,\tYL: null,\tYZ: null                    : TableRow(Translated.Value);                string Value =                    Neighbor == null                    ? null                    : Grid.ICell;                Console.WriteLine(Location + \t->\t + Value);            }            Console.WriteLine();        }        static void PrintNeighborsOf(Grid Board, Grid.Vector Direction) {            Grid.Vector Center = new Grid.Vector(0, 0, 0);            // Test the addition operator            Grid.Vector? Translated = Center + Direction;            if (Translated != null) {                Grid.ICell Cell = Board[Translated.Value];                if (Cell != null) {                    PrintNeighbors(Cell);                } else {                    Console.WriteLine(No Grid.ICell found at ({0}, {1}, {2})\n,                        Translated.Value.X, Translated.Value.YL, Translated.Value.YR);                }            } else {                Console.WriteLine(Unable to translate by ({0}, {1}, {2}): Intregal Overflow\n,                        Direction.X, Direction.YL, Direction.YR);            }        }        static void PrintLocation(Grid.Vector lhs, Grid.Vector rhs) {            Grid.Vector? Translated = lhs + rhs;            if (Translated == null) {                Console.WriteLine(Unable to translate by ({0}, {1}, {2}): Intregal Overflow\n,                    rhs.X, rhs.YL, rhs.YR);            } else {                Console.WriteLine(This should not get printed\n);            }        }        public static void Main(string[] args) {            Grid Board = new Grid();            PrintNeighborsOf(Board, new Grid.Vector(0, 0, 0));            PrintNeighborsOf(Board, new Grid.Vector(4, 4, 4));            PrintNeighborsOf(Board, new Grid.Vector(5, 0, 0));            PrintLocation(new Grid.Vector(1, 0, 0), new Grid.Vector(127, 0, 0));            Console.Write(Press any key to continue . . . );            Console.ReadKey();        }    }    public partial class Grid {        public struct Vector {            // Enumerate all six of the cardinal unit vectors            public static readonly Vector[] Units = new Vector[6]{                new Vector( 1,  0,  0), new Vector(-1,  0,  0),                new Vector( 0,  1,  0), new Vector( 0, -1,  0),                new Vector( 0,  0,  1), new Vector( 0,  0, -1),            };            // sbyte => range of [-127, 128]; plenty big for this app, and small enough to GetHashCode()            public sbyte X  { get; set; } // Horizontal Axis            public sbyte YL { get; set; } // Left-slanted Vertical Axis            public sbyte YR { get; set; } // Right-slanted Vertical Axis            public Vector(sbyte x, sbyte yl, sbyte yr) { X = x; YL = yl; YR = yr; }            // Overloaded Arithmetic Operators; Prototypes found at: http://stackoverflow.com/a/20596352/310560            // Since we don't have a clear cut way to give a comparable numerical value to a set of 3 signed integers            // The logic for `Compare` will be based on `==` and `<` (the typical C++ approach)            // Where the typical C# approach would base `==` and `<` off `Compare`            public static bool operator==(Vector lhs, Vector rhs) {                return                    lhs.X  == rhs.X  &&                    lhs.YL == rhs.YL &&                    lhs.YR == rhs.YR;            }            // The < operator is important for any data algorithms which require sorting            public static bool operator< (Vector lhs, Vector rhs) {                return                    lhs.X  < rhs.X  || (lhs.X  == rhs.X  &&                    lhs.YL < rhs.YL || (lhs.YL == rhs.YL &&                    lhs.YR < rhs.YR));            }            // We'll just implement the rest of the comparison operators simply because we can,  and complementary            // operators should always be implemented together (though why C# doesn't do this already I'm not sure)            public static bool operator> (Vector lhs, Vector rhs) { return   rhs <  lhs; }            public static bool operator<=(Vector lhs, Vector rhs) { return !(lhs >  rhs); }            public static bool operator>=(Vector lhs, Vector rhs) { return !(lhs <  rhs); }            public static bool operator!=(Vector lhs, Vector rhs) { return !(lhs == rhs); }            // Attached Object Comparison Overloads            public override bool Equals(object obj) { return (obj is Vector) && (this == (Vector)obj); }            public bool Equals(Vector rhs) { return Equals(this, rhs); }            public int CompareTo(Vector rhs) { return CompareTo(this, rhs); }            // Detached Object Comparison Overloads            public static bool Equals(Vector lhs, Vector rhs) { return lhs == rhs; }            private static int CompareTo(Vector lhs, Vector rhs) {                return lhs == rhs ?  0                    : (lhs <  rhs ? -1                    :                1);            }            // Used for indexing in hash tables (i.e. Dictionary<Vector, Cell>)            public override int GetHashCode() {                return (X << 16) | (YL << 8) | (YR << 0);            }            // Addition operator used for translation            public static Vector? operator+(Vector lhs, Vector rhs) {                int x  = lhs.X  + rhs.X,                    yl = lhs.YL + rhs.YL,                    yr = lhs.YR + rhs.YR;                try {                    Vector shifted  = new Vector(                        checked((sbyte)(lhs.X + rhs.X)),                        checked((sbyte)(lhs.YL + rhs.YL)),                        checked((sbyte)(lhs.YR + rhs.YR)));                    return shifted;                } catch {                    return null;                }            }        }    }    public partial class Grid {        private readonly Dictionary<Vector, ICell> Board;        public ICell this[Vector i] {            get { if (Board.ContainsKey(i)) return Board[i]; return null; }            private set { Board[i] = value; }        }        public Grid() {            Board = new Dictionary<Vector, ICell>();            // Add the inner hexagonal part of the Chinese Checkers board            // Will add logic for the outer triangles after finished testing            for (sbyte i = -4; i < 5; ++i) {                for (sbyte j = -4; j < 5; ++j) {                    for (sbyte k = -4; k < 5; ++k) {                        new Cell(this, new Vector(i, j, k));                    }                }            }        }    }    public partial class Grid {        public interface ICell {            Grid Parent { get; }            Vector Location { get; }            ICell Neighbor(Vector direction);        }        // It's important this class is private to insure Grid exclusively manages all cells        // Thus it is important to maintain the interface ICell for public interaction        private class Cell : ICell {            private readonly Grid _parent;            public Grid Parent { get { return _parent; } }            private readonly Vector _location;            public Vector Location { get { return _location; } }            public ICell Neighbor(Vector direction) {                Vector? loc = Location + direction;                if (loc == null) { return null; }                return Parent[loc.Value];            }            public Cell(Grid par, Vector loc) {                _parent = par; _location = loc;                Parent[Location] = this;            }        }    }}
3-axis coordinate system for a Chinese Checkers board
c#;object oriented;coordinate system;checkers draughts
Only scratching the surface of struct Vector A struct should be immutable if possible. You have public setters  public sbyte X  { get; set; } // Horizontal Axispublic sbyte YL { get; set; } // Left-slanted Vertical Axispublic sbyte YR { get; set; } // Right-slanted Vertical Axis  and in addition if you need a comment for a variable/property then it should be named better.  public static Vector? operator+(Vector lhs, Vector rhs) {    int x  = lhs.X  + rhs.X,        yl = lhs.YL + rhs.YL,        yr = lhs.YR + rhs.YR;    try {        Vector shifted  = new Vector(            checked((sbyte)(lhs.X + rhs.X)),            checked((sbyte)(lhs.YL + rhs.YL)),            checked((sbyte)(lhs.YR + rhs.YR)));        return shifted;    } catch {        return null;    }}  Please don't declare and/or initialize multiple variables on the same line. This becomes just unreadable. Nevertheless this variables x,yl and yr are never used so get rid of them.  Although I place simple properties which are having only get; and/or set; on the same line I wouldn't do it for public Vector(sbyte x, sbyte yl, sbyte yr) { X = x; YL = yl; YR = yr; } for the sake of readability.  As a little side note, almost every C# developer would expect to see the opening brace { at a new line.