[C#] Guess The Number (My first game code)

Started by
11 comments, last by Gohalien 16 years, 3 months ago
POKER GAME IS ON HOLD, I STARTED WITH A MORE SIMPLE GAME Learning C# making the game: GuessTheNumber ----- Hello people, I am trying to make an online poker game to learn more about game dev, sockets, svn and in a future directX. I have current experience using C# with mysql. Since I learned a lot from this forum, I will be sharing my experience developing this game. First of all, since I am using already http://www.codeproject.com/KB/IP/ChatAsynchTCPSockets.aspx?msg=2375668#xx2375668xx example about Asynch TCP Sockets (Client <-> Server) comunication I started with the Card class. What I have done is how to generate a card and check if this card isnt already in game:

    class Deck
    {
        public DataTable IngameCards = new DataTable();
        private int[] number = new int[13] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; //J=11, Q=12, K=13, A=14
        private string[] color = new string[4] { "H", "S", "C", "D" }; //Heart, Spade, Club, Diamond
        public Deck()
        {
            IngameCards.Columns.Add("NUMBER", typeof(Int32));
            IngameCards.Columns.Add("COLOR", typeof(String));
        }
        public object[] GenerateCard()
        {
            object[] card_generated = new object[2];
            bool exist = false; //Card "already ingame" flag
            Random random = new Random();
            card_generated[0] = number[random.Next(13)];
            card_generated[1] = color[random.Next(4)];
            foreach (DataRow row in IngameCards.Rows) //Now we must check if that card is already in game (need optimization)
            {
                if (!exist)
                {
                    if (((Int32)row[0] == (Int32)card_generated[0]) && ((String)row[1] == (String)card_generated[1]))
                    {
                        exist = true; //Card is in already in game
                        break; //We dont need to check the other cards in game.
                    }
                }
            }
            if (!exist)
            {
                IngameCards.Rows.Add(card_generated); //We add the generated card to the IngameCards datatable
            }
            else
            {
                card_generated = GenerateCard(); //We seek a new card.
            }
            return card_generated;
        }
        public void ClearDeck()
        {
            IngameCards.Clear();
        }
    }





Now if we need to deal cards, we can check if the cards are not repeating...

        private void button1_Click(object sender, EventArgs e)
        {
            Deck TableOneDeck = new Deck();
            TableOneDeck.ClearDeck();
            for (int i=0; i < 10; i++)
            {
                this.dataGridView1.DataSource = TableOneDeck.IngameCards;
                object[] Player1Card1 = TableOneDeck.GenerateCard();
                object[] Player1Card2 = TableOneDeck.GenerateCard();
                this.textBox1.Text += string.Format("Card 1: {0}{1} Card 2: {2}{3}\r\n", Player1Card1[0], Player1Card1[1], Player1Card2[0], Player1Card2[1]);
            }
        }





Trust me, they dont repeat ^^ [Edited by - Gohalien on January 9, 2008 3:40:09 PM]
Advertisement
Made a better logic to create & deal cards:
    class Deck    {        public DataTable dt_Deck = new DataTable();        private int[] number = new int[13] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; //J=11, Q=12, K=13, A=14        private string[] color = new string[4] { "H", "S", "C", "D" }; //Heart, Spade, Club, Diamond        public Deck()        {            dt_Deck.Columns.Add("NUMBER", typeof(Int32));            dt_Deck.Columns.Add("COLOR", typeof(String));            CreateCards();        }        private void CreateCards()        {            //Here we create the 52 cards            object[] card_generated = new object[2];            for (int i = 0; i &lt; 13; i++)            {                for (int ii = 0; ii &lt; 4; ii++)                {                    card_generated[0] = number;                    card_generated[1] = color[ii];                    dt_Deck.Rows.Add(card_generated); //We add the card to the Deck datatable                }            }        }        public object[] DealCard()        {            //Note: If we don't include the Thread.Sleep, the cards are dealed so fast that the "random" function isn't "really random"            Random random = new Random();            object[] card_selected = new object[2];            if (dt_Deck.Rows.Count &gt; 0) //Here checks if our deck datatable have cards            {                int deck_row = random.Next(dt_Deck.Rows.Count);                card_selected[0] = dt_Deck.Rows[deck_row]["NUMBER"];                card_selected[1] = dt_Deck.Rows[deck_row]["COLOR"];                dt_Deck.Rows.RemoveAt(deck_row); //Delete the dealed card from the deck            }            Thread.Sleep(random.Next(1,10)); //Randomly sleep the thread to make the "random" more... ramdom -.-            return card_selected;        }        public void NewDeck()        {            dt_Deck.Clear();            CreateCards();        }[/source lang="c#"]Now in a form...            Deck TableOneDeck = new Deck();            for (int player = 0; player &lt; 26; player++)            {                this.dataGridView1.DataSource = TableOneDeck.dt_Deck;                object[] Card1 = TableOneDeck.DealCard();                object[] Card2 = TableOneDeck.DealCard();                this.textBox1.Text += string.Format("Player: {0} Card 1: {1}{2} Card 2: {3}{4}\r\n", player, Card1[0], Card1[1], Card2[0], Card2[1]);            }


[Edited by - Gohalien on January 4, 2008 2:58:19 PM]
I skimmed over your first post, but you made quite a few vital flaws in there. First of all, you're using an SQL server to store real-time data. It's a really bad plan to store drawn cards in a database.

It's slow as hell, esp. if you're running several games at the same time. It's better to create a list of 4 * 13 = 52 cards, shuffle it and remove cards from it as if it were a stack of cards.

You are also calling GenerateCard recursively, which will result in a StackOverflow sooner or later and crashes the application. Not what you want if you're running said application as a server. You are also using Random to pull a card, which is exploitable if someone is able to figure out the random number generator.

In your 2nd post, you show a better Deck class, which is still hitting the database. Don't. And instead of a DataSet, use a List.

To make things ever easier, why not create a Card class aswell?

Toolmaker

Use the source tags, so the tables don't break [smile].

I don't want to start a match to write better code than you, but I wrote a simple example for cards/deck which show a different way of doing it(In this case, definitely better, because there is no database involved).

Shuffling is done by creating a GUID for each card and then sorting the List of cards based on the Guid. It's fast, uses built-in language constructs and quite simple to implement.

using System;using System.Collections.Generic;using System.Text;namespace PunchTest{    // Derive from byte so transmitting across the internet is simple    public enum CardFace : byte { Hearts, Spades, Club, Diamond }    public class Card : IComparable<Card>    {        private CardFace face;        private int value;        private Guid uid;        public Card(CardFace cardFace, int cardValue)        {            this.face = cardFace;            this.value = cardValue;            uid = new Guid(); // Used to sort the card        }        public CardFace CardFace        {            get { return face; }        }        public int Value        {            get { return value; }        }        public Guid Guid        {            get { return uid; }        }        #region IComparable<Card> Members        public int CompareTo(Card other)        {            return this.uid.CompareTo(other.Guid);        }        #endregion    }    public class Deck    {        private List<Card> cards = new List<Card>();        /// <summary>        /// Default constructor        /// </summary>        public Deck()        {            PopulateCards();        }        /// <summary>        /// Creates all cards and shuffles the deck        /// </summary>        private void PopulateCards()        {            for (int i = 2; i < 13; i++)            {                cards.Add(new Card(CardFace.Club, i));                cards.Add(new Card(CardFace.Diamond, i));                cards.Add(new Card(CardFace.Hearts, i));                cards.Add(new Card(CardFace.Spades, i));            }            cards.Sort();        }        /// <summary>        /// Draws a card from the deck        /// </summary>        /// <returns></returns>        public Card TakeCard()        {            Card c = cards[0];            cards.RemoveAt(0);            return c;        }        public int Count        {            get { return cards.Count; }        }    }}


Toolmaker

Ok, nice example, I made this in the night before reading your code.
class Card    {        public enum Number        {            Two = 0,            Three,            Four,            Five,            Six,            Seven,            Eight,            Nine,            Ten,            Jack,            Queen,            King,            Ace        }        public enum Suit        {            Hearts = 0,            Spades,            Diamonds,            Clubs        }        public Number number;        public Suit suit;        public Card(int n, int s)        {            number = (Number)n;            suit = (Suit)s;        }    }


Modified my code of Deck:

class Deck    {        DataTable deck = new DataTable();        public Deck()        {            deck.Columns.Add("CARDS", typeof(Card));            CreateCards();        }        private void CreateCards()        {            //Here we create the 52 cards            Card card_generated;            for (int i = 0; i < 13; i++) //Card number            {                for (int ii = 0; ii < 4; ii++) //Card suit                {                    card_generated = new Card(i, ii);                    deck.Rows.Add(card_generated); //We add the card to the Deck                }            }        }        public Card DealCard()        {            //Note: If we don't include the Thread.Sleep, the cards are dealed so fast that the "random" function isn't "really random"            Random random = new Random();            Card card_selected = new Card(0,0);            if (deck.Rows.Count > 0) //Here checks if our deck datatable have cards            {                int deck_row = random.Next(deck.Rows.Count);                card_selected = (Card)deck.Rows[deck_row]["CARDS"];                deck.Rows.RemoveAt(deck_row); //Delete the dealed card from the deck            }            Thread.Sleep(random.Next(1,10)); //Randomly sleep the thread to make the "random" more... ramdom -.-            return card_selected;        }        public void NewDeck()        {            deck.Clear();            CreateCards();        }    }


I am still using the deck as datatable I should use the deck as a
 Card[] Deck = new Card[52]; 

as you say in your first post, but here is my problem, when I deal a card, I don't know how to remove the selected card from the deck, I know using datatable I can use
 deck.Rows.RemoveAt(deck_row);
when I can fix that problem I will be using the deck as new Card[52] instead datatable.

Btw, what is IComparable ?
Heh don't worry, I am learning C# my code is ugly ^^
I will learn and apply your code, is way better.
You should look at my Deck implementation. I use a List<Card>, which is a Linked List. Consider it an auto-resizing array(altho it's more complex than that, but it will suffice for now). Removing and adding to it will make the array shrink or enlarge the array data automatically. And it's faster than a DataTable.

Also, don't use my code directly out of the box. Examine it, read it, understand it. And then write your own implementation to learn some more.

And IComparable is a special type, it's an interface. It basically tells the compiler that Card will implement the behaviour IComparable describes. In this case, IComparable only has an int CompareTo(Card other) which you need to write.

When you then call cards.Sort(), the List will not see them as cards, but as IComparable's. So, in the code it will run over all the items in the list, and use item.CompareTo(otherItem) and then base the sorting on that.

Say(example time), you want to order items by their price. You have a Product class:
public class Product : IComparable<Product>{    private float price;    ...    public int CompareTo(Product other)    {        if (price < other.Price)            return -1; // This one is cheaper than other        else if (price > other.Price)            return 1; // More expensive than other        else            return 0; // Same price    }}


If you store 2 Products in a List, with prices of 0.77 and 0.55 and then call list.Sort(), it will compare the prices of the 2(by calling CompareTo) and then see that the 0.55 one has to be placed before the 0.77 one.

Toolmaker

Found nice poker librarys
http://www.codeproject.com/KB/cs/pokerlibraryandbotchallan.aspx
But, I will try to make them myself taking a look from that project (copy/paste doesn't teach me anything). Anyway, I think your code is better ^^ both similar, mine was ugly but again, I am learning ^^

Ok, here we go, new Deck class:
class Deck    {        Random random = new Random(DateTime.Now.Millisecond);        private List<Card> deck = new List<Card>();        public Deck()        {            CreateCards();        }        private void CreateCards()        {            //Here we create the 52 cards            for (int i = 2; i < 15; i++) //Card number            {                deck.Add(new Card(i, Card.CardSuit.Clubs));                deck.Add(new Card(i, Card.CardSuit.Diamonds));                deck.Add(new Card(i, Card.CardSuit.Hearts));                deck.Add(new Card(i, Card.CardSuit.Spades));            }            deck.Sort();        }        public Card DealCard()        {            Card c = new Card(0,Card.CardSuit.Spades);            try            {                c = deck[0];                deck.RemoveAt(0);            }            catch (ArgumentOutOfRangeException i)            {                Console.Write("No more cards! " + i);            }            return c;        }        public void NewDeck()        {            deck = new List<Card>();            CreateCards();        }        public void ShufleDeck()        {            Card card;            int temp;            //http://www.codeproject.com/KB/cs/pokerlibraryandbotchallan.aspx            for (int i = 0; i < deck.Count; i++)            {                temp = random.Next(i, deck.Count); card = deck[temp];                deck[temp] = deck;                deck = card;            }        }    }


And of course, my main idea is to learn, so far I had no problems understanding your routines, where I had problem understanding was at ICompare/sort, but google and your explanation in your latest post helped me a lot.

BTW, I am curiouse, datatable is slow ? I thought datatable was a nice, fast way to store/sort data, thanks you for teaching me about list, it is very intuitive to use. I am still a bit confused about uid/guid, I am going now to learn more about it. Thanks for your time and willing to help us noobs.

[Edited by - Gohalien on January 4, 2008 4:03:49 PM]
A GUID is a Global Unique Identifier, a 128-bit(16-byte) 'number', which should be unique at all times(There are a lot of combinations to generate here, so the chance of getting a double value is quite small. But given enough time, you're bound to generate the same number).

I use it as a way to sort the cards, because I'm lazy :). Your ShuffleDeck() basically does what my IComparable / List.Sort() combination does. There is no need to sort the deck if you're going to shuffle manually btw, but there is no harm in it.

I see you're handling exceptions too, good job/idea. However, in this case, it might be better to have the function that calls DealCard deal with it.

Consider this extremely simplified (psuedo) program:
Deck deck = new Deck();// etc.while (!gameOver){    Options playerSelection = GetPlayerInput();    if (playerSelection == Options.TakeCard)    {        try        {            player.GiveCard(deck.DealCard());        }        catch (ArgumentOutOfRangeException e)        {            ShowError("Oops! There are no more cards on the deck!");        }    }}


In this case, you let the caller of DealCard handle the exception, instead of doing it inside the DealCard() function. Because if you do handle it inside the function, there is no way to find out something went wrong and you keep drawing invalid cards from the deck.

Good luck with learning. Perhaps, you should step a notch down from writing poker, to writing a more simpler game, such as GuessTheNumber:

Computer generates a random number, and player has to guess which number it is. If the guess is lower than the number, it will show "Higher" and when it's higher, it will show "Lower". If the guess equals the number, it will show "You won in x guesses", where x is the amount of attempts.

(Number = 60)
Guess? 50
Higher
Guess? 75
Lower
Guess? 63
Lower
Guess? 58
Higher
Guess? 60
You won in 5 guesses!

Toolmaker

Thanks, I can understand now about the unique id function in C#.
I will take your advice, but I will try to make this work, I know it is hard, but I will learn a lot from it, will take me a while, but I know I can do it ^^

[Edited by - Gohalien on January 4, 2008 7:36:45 PM]
Quote:Original post by Toolmaker
A GUID is a Global Unique Identifier, a 128-bit(16-byte) 'number', which should be unique at all times(There are a lot of combinations to generate here, so the chance of getting a double value is quite small. But given enough time, you're bound to generate the same number).

I use it as a way to sort the cards, because I'm lazy :). Your ShuffleDeck() basically does what my IComparable / List.Sort() combination does. There is no need to sort the deck if you're going to shuffle manually btw, but there is no harm in it.

I see you're handling exceptions too, good job/idea. However, in this case, it might be better to have the function that calls DealCard deal with it.

Consider this extremely simplified (psuedo) program:
Deck deck = new Deck();// etc.while (!gameOver){    Options playerSelection = GetPlayerInput();    if (playerSelection == Options.TakeCard)    {        try        {            player.GiveCard(deck.DealCard());        }        catch (ArgumentOutOfRangeException e)        {            ShowError("Oops! There are no more cards on the deck!");        }    }}


In this case, you let the caller of DealCard handle the exception, instead of doing it inside the DealCard() function. Because if you do handle it inside the function, there is no way to find out something went wrong and you keep drawing invalid cards from the deck.

Good luck with learning. Perhaps, you should step a notch down from writing poker, to writing a more simpler game, such as GuessTheNumber:

Computer generates a random number, and player has to guess which number it is. If the guess is lower than the number, it will show "Higher" and when it's higher, it will show "Lower". If the guess equals the number, it will show "You won in x guesses", where x is the amount of attempts.

(Number = 60)
Guess? 50
Higher
Guess? 75
Lower
Guess? 63
Lower
Guess? 58
Higher
Guess? 60
You won in 5 guesses!

Toolmaker


I took your advice, and put poker in hold, made the game you recomended me, and here it is, I will post the game here and I will love you to take a look of my code, and teach me or give me some tutorials to optimize it.

Sorry for my bad english ^^
The game (GuessTheNumber): DOWNLOAD!

This topic is closed to new replies.

Advertisement