1
\$\begingroup\$

This is just a single player versus a dealer. I'm not worried about execution time. It does not perform bets - it only track wins and losses- and has no splitting. Other than that I tried to get it all in.

I take it most people know Backjack. Number are the number, face cards are 10, and ace is 1 or 11. Player has no rules about hitting (take a card). Dealer has to hit to 17. Closest to 21 without going over wins. Dealer wins a tie.

class Program
{
    static void Main(string[] args)
    {
        BlackJack blackJack = new BlackJack();
        //blackJack.PlayHand();
        blackJack.PlayGame();
    }
    public class Card
    {
        private enum CardRank { Two = 0, Three = 1, Four = 2, Five = 3, Six = 4, Seven = 5, Eight = 6, Nine = 7, Ten = 8, Jack = 9, Queen = 10, King = 11, Ace = 12 }
        private enum CardSuit { Spade = 0, Club = 1, Heart = 2, Diamond = 3 }
        int i;
        public override string ToString()
        {
            return ((CardRank)(i % 13)).ToString() + " " + ((CardSuit)(i / 13)).ToString();
        }
        public int Value
        {
            get
            {
                int value;
                if (i % 13 == 12)
                {
                    value = 11;
                }
                else
                {
                    value = i % 13 + 2;
                    if (value > 10)
                    {
                        value = 10;
                    }
                }
                return value;
            }
        }
        public Card(int I)
        {
            i = I;
        }
    }
    public abstract class Seat
    {
        public int AceCount { get; private set; } = 0;
        public List<Card> Cards { get; private set; } = new List<Card>(); 
        public bool Busted
        { get { return Total > 21; } }
        public bool HasBlackJack
        { get{ return (Total == 21 && Cards.Count == 2); } }
        public int Total
        {
            get
            {
                int total = 0;
                AceCount = 0;
                foreach (Card c in Cards)
                {
                    total += c.Value;
                }
                if (total > 21 && AceCount > 0)
                {
                    for (int i = 0; i < AceCount; i++)
                    {
                        total -= 10;
                        if (total <= 21)
                        {
                            break;
                        }
                    }
                }
                return total;
            }
        }
        public abstract bool CanHit { get; }
        public void HitMe(Card C)
        {
            Cards.Add(C);
        }
        public virtual string DisplayCards
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                foreach (Card c in Cards)
                {
                    if(sb.Length > 0)
                    {
                        sb.Append(", ");
                    }
                    sb.Append(c.ToString());
                } 
                string cards = sb.ToString();
                return $"{cards} {Total}";
            }
        }
    }
    public class Dealer : Seat
    {
        public override bool CanHit
        {
            get
            {
                if (AceCount > 0)
                {
                    return Total <= 18;
                }
                else
                {
                    return Total <= 17;
                }
            }
        }
        public override string DisplayCards
        {
            get
            {
                if(Cards.Count == 2)
                {
                    return $"{Cards[0].ToString()} {Cards[0].Value}";
                }
                else
                {
                    return base.DisplayCards;
                }
            }
        }
    }
    public class Player : Seat
    {
        public override bool CanHit
        {
            get
            {
                return Total < 21;
            }
        }

        public int Wins { get; set; } = 0;
        public int Loss { get; set; } = 0;
        public string WinLoss
        {
            get { return $"Win {Wins} Loss {Loss}"; }
        }
    }
    public class BlackJack
    {
        Random rand = new Random();
        private List<Card> Deck;
        private int deckPos = -1;
        Dealer dealer = new Dealer();
        Player player = new Player();
        private void Shuffle()
        {
            deckPos = -1;
            int swap;
            Card temp;
            for (int i = Deck.Count - 1; i > 0; i--)
            {
                swap = rand.Next(i + 1);  //.net rand is not inclusive
                if (swap != i)
                {
                    temp = Deck[i];
                    Deck[i] = Deck[swap];
                    Deck[swap] = temp;
                }
            }
        }
        public BlackJack()
        {
            Deck = new List<Card>();
            for (int i = 0; i < 52; i++)
            {
                Card card = new Card(i);
                Console.WriteLine(card.ToString());
                Deck.Add(card);
            }
            Console.WriteLine("");
            Shuffle();
        }
        private Card Pop()
        {
            deckPos++;
            return (Deck[deckPos]);
        }
        private Card Peek()
        {
            return (Deck[deckPos]);
        }
        public void PlayGame()
        {           
            while (true)
            {
                Console.WriteLine("C to continue Q to quit");
                string p = Console.ReadLine();
                if (p == "C" || p == "c")
                {
                    PlayHand();
                }
                else if (p == "Q" || p == "q")
                {
                    Console.WriteLine("Bye");
                    break;
                }
            }
        }
        public void PlayHand()
        {
            if (deckPos > 30)
            {
                Shuffle();
            }

            dealer.Cards.Clear();
            player.Cards.Clear();

            Console.WriteLine("");
            Console.WriteLine("");

            Card card = Pop();
            dealer.HitMe(card);
            dealer.HitMe(Pop());
            Console.WriteLine("Dealer");
            Console.WriteLine(dealer.DisplayCards);
            if (dealer.HasBlackJack)
            {
                Console.WriteLine("Player H to hit  S to Stay");
                player.Loss++;
            }
            else
            {
                Console.WriteLine("Player H to hit  S to Stay");
                player.HitMe(Pop());
                player.HitMe(Pop());
                Console.WriteLine(player.DisplayCards);
                string p;
                while (true)
                {
                    p = Console.ReadLine();
                    if (p == "H" || p == "h")
                    {
                        if (player.CanHit)
                        {
                            player.HitMe(Pop());
                            Console.WriteLine(player.DisplayCards);
                            if (player.Busted)
                            {
                                break;
                            }
                        }
                        if (player.HasBlackJack)
                        {
                            break;
                        }
                    }
                    else if (p == "S" || p == "s")
                    {
                        break;
                    }
                }

                if (player.Busted)
                {
                    Console.WriteLine("Player busted out dealer wins");
                    player.Loss++;
                }
                else if (player.HasBlackJack)
                {
                    Console.WriteLine("Blackjack player wins");
                }
                else
                {
                    Console.WriteLine("Dealer");
                    while (dealer.CanHit)
                    {
                        dealer.HitMe(Pop());
                        Console.WriteLine(dealer.DisplayCards);
                        if (dealer.Busted)
                        {
                            break;
                        }
                    }
                    if (dealer.Busted)
                    {
                        Console.WriteLine("Dealer busted out player wins");
                        player.Wins++;
                    }
                    else if (dealer.Total >= player.Total)
                    {
                        Console.WriteLine("Dealer wins");
                        player.Loss++;
                    }
                    else
                    {
                        Console.WriteLine("Player wins");
                        player.Wins++;
                    }
                }
            }
            Console.WriteLine(player.WinLoss);
        }
    }
}
\$\endgroup\$
5
\$\begingroup\$

First of all your code have a bad formatting. It is hard to read it without, for example, empty lines between methods and properties.

Also it is almost always better to follow the rule: one file - one class.

One of big disadvantages of your code is you totally ignore the great feature of C# - LINQ.

Some examples of how you can make your code better. This property

public int Total
{
    get
    {
        int total = 0;
        AceCount = 0;
        foreach (Card c in Cards)
        {
            total += c.Value;
        }
        if (total > 21 && AceCount > 0)
        {
            for (int i = 0; i < AceCount; i++)
            {
                total -= 10;
                if (total <= 21)
                {
                    break;
                }
            }
        }
        return total;
    }
}

can be rewritten as (by the way I don't understand when for will be executed if you set AceCount = 0 above it)

 public int Total
 {
     get
     {
         AceCount = 0;
         int total = Cards.Sum(c => c.Value);
         for (int i = 0; i < AceCount && total > 21; i++)
         {
             total -= 10;
         }
         return total;
     }
 }

This property

public virtual string DisplayCards
{
    get
    {
        StringBuilder sb = new StringBuilder();
        foreach (Card c in Cards)
        {
            if(sb.Length > 0)
            {
                sb.Append(", ");
            }
            sb.Append(c.ToString());
        } 
        string cards = sb.ToString();
        return $"{cards} {Total}";
    }
}

can be rewritten as

public virtual string DisplayCards
{
    get
    {
        var cards = string.Join(", ", Cards);
        return $"{cards} {Total}";
    }
}

In my opinion these two examples ideally show your coding style and how it can be improved.

Also these lines

Console.WriteLine(card.ToString());
Console.WriteLine("");

look very strange. They should be replaced with

Console.WriteLine(card);
Console.WriteLine();

And please get rid of magic number constants. Define named const's like

private const int DeckSize = 52;
\$\endgroup\$
  • \$\begingroup\$ It's always a good idea to encapsulate magic-conditions like this one i < AceCount && total > 21. \$\endgroup\$ – t3chb0t Jul 17 '17 at 6:40
  • \$\begingroup\$ Yes, I agree with you. \$\endgroup\$ – Maxim Jul 17 '17 at 6:44
  • \$\begingroup\$ I had it var cards = string.Join(Cards , ", "); so it was not working and I gave up. In the Cards.Sum is where I am supposed to be counting aces so I cannot use LINQ. Good feedback. \$\endgroup\$ – paparazzo Jul 17 '17 at 14:59
  • \$\begingroup\$ string.Join(", ", Cards) is an exact eqivalent of foreach with StringBuilder. If it was not working you did something wrong. \$\endgroup\$ – Maxim Jul 18 '17 at 1:50

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.