Basic domino game architecture I am trying to write a simple domino (five-game) game with 2 players. I must say I am very bad at OOP design, and so I don't want to get myself too deep into coding then realize I have a bad design to start with. I'd appreciate if someone shed some light on how to better design this.

Here is summary of the game:


there are 28 dominos   
there will be 2 players  
initially, each player receives 7 random tiles, the remaining 14 tiles are left to the bone yard pool 
initially, whichever player got the heaviest tile gets to play first
whoever scores x points first wins the game (e.g: 100,150,etc)


Here is what I have so far:

Player class:

public class Player
{
    //properties
    string ID { get; set; } //player id  
    int Wins { get; set; }
    int Losses { get; set; }
    bool IsMyTurn { get; set; } 
    List&lt;Domino&gt; Hand { get; set; } //each player will have a list of domino tiles

    void ShowHand(); //just display what's in their hands if needed
    void PlayerStatistic();  //get user's historical wins/losses, number game played, etc.

}


Game class:

public class Game
{
    //properties
    int ID { get; set; } //game id, assuming each player can play multiple games as once
    Player PlayerTurn { get; set; } //signifies if it is his turn
    Dominos DominoSet { get; set; }
    List&lt;string&gt; Board { get; set; }

    void Move(Player player, Domino domino); 

    public Game(Player player1, Player player2)
    { 
        ID = 0; //TODO
        DominoSet = new Dominos();  //create a set of dominos
        Board = new List&lt;string&gt;(); //create a new game board that store all the moves 
        Domino curDomino = InitializePlayers(player1, player2, DominoSet.AllDominos);
        DominoSet.AllDominos.RemoveRange(0, 14); //remove all dominos that have been assigned to 2 players
        //...and other good stuffs
    }

}


Domino class, which was intended to describe a single domino:

public class Domino 
{ 
    public string Name { get; set; } // "0,1", "0,2", etc.
    public int Head { get; set; } //each domino has 2 side, i called it head and tail
    public int Tail { get; set; }
    public int Weight { get; set; } //a 0-27, e.g: a "6,6" domino would have the highest predifined weight of 27
    public bool IsDouble { get; set; }  //whether the tile is double or not

    public Domino(string name, int head, int tail, int weight){
       Name = name;
       Head = head;
       Tail = tail;
       Weight = weight;
       IsDouble = (head == tail ? true : false);
    }
}


Dominos class, which was intended for use to store a list of 28 domino set, and perform other actions such as shuffle dominos, track domino, etc.:

public class Dominos 
{  
       private string[] _tiles = {"0,1","0,2","0,3","0,4","0,5","0,6",
                               "1,2","1,3","1,4","1,5","1,6",
                               "2,3","2,4","2,5","2,6",
                               "3,4","3,5","3,6",
                               "4,5","4,6",
                               "5,6",
                               "0,0","1,1","2,2", "3,3", "4,4","5,5","6,6"};
       public List&lt;Domino&gt; AllDominos { get; set; }       
       public void Shuffle(){} //radomize the dominos

       public Dominos()
       {
           AllDominos = new List&lt;Domino&gt;();

           for (int i = 0; i &lt; _tiles.Length; i++)
           {
               string[] word = _tiles[i].Split(',');
               Domino d = new Domino(_tiles[i], int.Parse(word[0]), int.Parse(word[1]), i);
               AllDominos.Add(d);  
           }
           Shuffle();//randomize the domino list
       }        
}


Main program to wire things together:

class Program
{
    static void Main(string[] args)
    {
        Player _player1 = new Player("Player1");
        Player _player2 = new Player("Player2"); 
        Game _game = new Game(_player1, _player2);
    }
}
I think this is a good exercise for trying out OOP design.  When I was learning OOP I found that working with real-life objects helped a lot and slowly I started getting use to objects that represented abstract concepts.  In this example you have a mixture, real-life dominoes and abstract concepts such as the 'Game' object.

The Domino Class is a good candidate for an immutable object.  It is often a good idea to create immutable objects when you can as this can be a powerful mechanism to stop erroneous state from being accidentally recycled.

There seems to be quite a bit of duplication going on within the Domino Class.  This can sometimes lead to confusion when debugging.  If you are debugging, and the name of a domino says "3,2" but the Head and Tail show 4 and 1 then you are left not sure about what happened.  Best to try and avoid this from happening.

I think it is also a good idea to make sure that all the logic that represents dominoes is wrapped up in the Domino class.  This will help to avoid abstraction leakage.  It is fairly obvious that a domino has a head and a tail and this is exposed via your properties but it may not be obvious how the domino name should be formatted (e.g. should it use a comma 3,4 or a colon 3:4) or how a domino's weight is determined.  If you have to evaluate the weight of a domino outside of the domino class then this might be considered as abstraction leakage.

Putting these concepts together, here is how I would program the Domino Class:

// Represents a tile used in a domino game.
// This class is designed to be immutable.
public class Domino
{
    // Private constructor means that dominos can
    // only be crated from a static method in this class.
    private Domino(string name, int weight)
    {
        // domino name can either be in the format H,T or H:T
        string[] nameParts = name.Split(',', ':');
        if (nameParts.Length != 2)
        {
            throw new ArgumentException("Invalid name format.");
        }

        try
        {
            Head = Int32.Parse(nameParts[0]);
            Tail = Int32.Parse(nameParts[1]);
        }
        catch
        {
            throw new ArgumentException("Invalid name format.");
        }

        Weight = weight;
    }

    // The properties of the domino class have a private set
    // because this class is designed to be immutable.
    public int Head { get; private set; }
    public int Tail { get; private set; }
    public int Weight { get; private set; }

    public bool IsDouble { get { return (Head == Tail); } }

    // Avoid duplicating the head and tail values by
    // generating the name every time it is needed.
    // This will be quick enough that it won't impact
    // performance.  Only worry about performance once
    // it starts to become a problem.
    public string Name { get { return Head.ToString() + "," + Tail.ToString(); } }

    public override string ToString() { return Name; }

    // Returns a full collection of shuffled dominos
    // this static method is part of the domino class
    // and so it can use the private constructor.
    public static DominoCollection GetAll()
    {
        string[] tiles = new string[]
        {
            "0,1","0,2","0,3","0,4","0,5","0,6",
            "1,2","1,3","1,4","1,5","1,6",
            "2,3","2,4","2,5","2,6",
            "3,4","3,5","3,6",
            "4,5","4,6",
            "5,6",
            "0,0","1,1","2,2", "3,3", "4,4","5,5","6,6"
        };

        DominoCollection dominos = new DominoCollection();
        for (int i = 0; i &lt; tiles.Length; i++)
        {
            dominos.Add(new Domino(tiles[i], i));
        }

        dominos.Shuffle();
        return dominos;
    }
}


I would define the DominoCollection class to make as much use of the built-in features as possible by inheriting it from the generic collection class:

public class DominoCollection : Collection&lt;Domino&gt;
{
    // shuffels the dominos in this collection
    public void Shuffle()
    {
        Random r = new Random();

        int n = Count;
        while (n-- &gt; 1)
        {
            int k = r.Next(n + 1);
            Swap(k, n);
        }
    }

    // swap the domino in index 1 with the domino in index 2
    private void Swap(int i1, int i2)
    {
        Domino temp = this[i1];
        this[i1] = this[i2];
        this[i2] = temp;
    }
}


Now you can get the full set of dominoes like this:

DominoCollection dominos = Domino.GetAll();


This still might not be considered ideal and there maybe some who suggest that the Domino.GetAll() method should not be a static method of Domino but instead should be in the collection class (similar to your original code) but at least you will have considered the alternatives and thought about the concept of abstraction leakage.