5
\$\begingroup\$

Please also see the new version here

So I've been trying to make a blackjack game as my first attempt at an OOP project. The program is working, but not yet finished as it is lacking three major features that I couldn't figure out how to implement.

  1. Not being able to deal the same card more than once
  2. Automatically changing the value of Aces to 1 or 11 depending on the hand
  3. The dealer's secret card

Also, I originally had the player and house hit methods in their respective classes but there were some issues in the event of the player going bust. Mainly I'm intrested code readabillity, commenting, naming, efficiency, code practicallity, class and method splitting. Also I'm afraid that I have made the cove needlessly complicated, if that is the case please mention that too in your review. Here is my old code (I posted a self answer with my changes and addition of the missing features).

Program Class:

using System;

namespace Blackjack_21
{
    class Program
    {
        static void Main(string[] args)
        {
            //Variable declaration
            Console.Write("Insert a name: ");
            string playerName = Console.ReadLine();
            bool play = true;
            while (play == true)
            {
                Game game = new Game();
                game.Play(playerName);
                Console.WriteLine("Press ENTER to continue");
                Console.ReadLine();
                Console.Clear();
                //ask for replay
                Console.Write("Do you want to play again? yes/no: ");
                bool answer = true;
                string ans = Console.ReadLine();
                //Looping until a correct answer is given
                while (answer)
                {
                    if (ans == "y" || ans == "Y" || ans == "yes" || ans == "YES")
                    {
                        play = true;
                        answer = false;
                    }
                    else if (ans == "n" || ans == "N" || ans == "no" || ans == "NO")
                    {
                        play = false;
                        answer = false;
                    }
                    else
                    {
                        Console.Write("error, unexpected input\nPlease type y or n ");
                        ans = Console.ReadLine();
                    }
                }
                Console.Clear();
            }
        }
    }
}

Game Class:

using System;

namespace Blackjack_21
{
    enum Status
    {
        gameOn,
        playerWin,
        houseWin,
        draw,
        blackjack
    }

    class Game
    {
        byte status = (byte)Status.gameOn;
        House house = new House();
        Player player1 = new Player("");
        Card deck = new Card();

        //player hits as many times as he wants when < 21
        public int PlayerHit()
        {
            if (!player1.Bust)
            {
                Console.Write("{0} Hits", player1.Name);
                player1.Hand += deck.DealCard();
            }
            return player1.Hand;
        }

        public int HouseHit()
        {
            while (house.Hand < 16)
            {
                Console.Write("The House Hits");
                house.Hand += deck.DealCard();
            }
            house.Stand();
            return house.Hand;
        }

        public void Play(string pName)
        {

            player1.Name = pName;
            bool playerIsBust = player1.CheckPlayerBust();

            //Initial cards
            PlayerHit();
            PlayerHit();
            Console.Write("The House hits");
            house.Hand += deck.DealCard();

            //Player wins automatically if he has a "blackjack"
            if (player1.HasBlackjack() == true)
            {
                Console.WriteLine($"{player1.Name} has a Blackjack!");
                status = (byte)Status.blackjack;
            }
            else
            {
                //loops as long as the player hasn't gone bust
                while (!playerIsBust && !player1.isStanding)
                {
                    //Player gives input
                    Console.WriteLine($"It is {player1.Name}'s turn:");
                    Console.WriteLine("Press H to hit   Press S to stand" +
                    "\nPress V to view your hand   press D to view the House's hand");

                    string input = Console.ReadLine();
                    //input check
                    if (input == "h" || input == "H")
                    {
                        PlayerHit();
                    }
                    else if (input == "s" || input == "S")
                    {
                        player1.Stand();
                        break;
                    }
                    else if (input == "v" || input == "V")
                    {
                        player1.ViewHand();
                    }
                    else if (input == "d" || input == "D")
                    {
                        house.ViewHand();
                    }
                    else
                    {
                        Console.Write("Unknown command\nPlease type a valid command ");
                        input = Console.ReadLine();
                    }
                    playerIsBust = player1.CheckPlayerBust();
                    /*Check for Aces here:
                    if(player has aces)
                    {
                        make aces value 1 until his hand < 22
                    }
                    */
                }
                if (playerIsBust == true)
                {
                    Console.WriteLine("{0}'s hand is {1} so he goes bust"
                                            , player1.Name, player1.Hand);
                }
                HouseHit();
                CompareHands();
            }
            AnounceWinner();
        }

        public void AnounceWinner()
        {
            switch (status)
            {
                case 1:
                    Console.WriteLine($"{player1.Name} wins!");
                    break;
                case 2:
                    Console.WriteLine("The House wins!");
                    break;
                case 3:
                    Console.WriteLine("Is is a draw");
                    break;
                case 4:
                    Console.WriteLine($"{player1.Name} wins!");
                    break;
            }
        }

        void CompareHands()
        {
            if (player1.Hand > 21)
                player1.Hand = 0;
            if (house.Hand > 21)
                house.Hand = 0;

            if (player1.Hand > house.Hand)
            {
                status = (byte)Status.playerWin;
            }
            else if (house.Hand > player1.Hand)
            {
                status = (byte)Status.houseWin;
            }
            else
            {
                status = (byte)Status.draw;
            }
            if (player1.Bust && house.bust)
            {
                status = (byte)Status.draw;
            }
        }
    }
}

House Class:

using System;

namespace Blackjack_21
{
    class House
    {
        public int Hand = 0;
        public bool bust = false;

        public void Stand()
        {
            CheckHouseBust();
            if (bust)
            {
                Console.WriteLine("The Houses's hand was {0} so it went bust", Hand);
                Hand = 0;
            }
            else
            {
                Console.WriteLine("The House stands at {0}", Hand);
            }
        }

        public void ViewHand()
        {
            Console.WriteLine("The House's hand is {0}", Hand);
        }

        bool CheckHouseBust()
        {
           return bust = (Hand > 21) ? true : false;
        }
    }
}

Player Class:

using System;

namespace Blackjack_21
{
    class Player
    {
        public string Name { get; set; }
        public int Hand = 0;
        public bool Bust = false;
        public bool isStanding = false;

        public Player(string name = "")
        {
            Name = name;
        }

        public bool HasBlackjack()
        {
            return (Hand == 21) ? true : false;
        }

        public void Stand()
        {
            if(!Bust)
            {
                Console.WriteLine("{0} stands at {1}", Name, Hand);
            }
            isStanding = true;
        }

        public void ViewHand()
        {
            Console.WriteLine("{0}'s hand is {1}", Name, Hand);
        }

        public bool CheckPlayerBust()
        {
           return Bust = (Hand > 21) ? true : false;
        }
    }
}

Card Class:

using System;
using System.Collections.Generic;

namespace Blackjack_21
{
    class Card
    {
        //TODO:
        //can't deal the same card
        //value of 1 or 11 for ace to be assigned based on hand

        private readonly Random randCard = new Random(DateTime.Now.Millisecond);

        public int DealCard()
        {
            IDictionary<int, string> numbers = new Dictionary<int, string>
            {
                { 2, "Two" },
                { 3, "Three" },
                { 4, "Four" },
                { 5, "Five" },
                { 6, "Six" },
                { 7, "Seven" },
                { 8, "Eight" },
                { 9, "Nine" },
                { 10, "Ten" },
                { 11, "Jack" },
                { 12, "Queen" },
                { 13, "King" },
                { 14, "Ace" }
            };

            IDictionary<int, string> suits = new Dictionary<int, string>
            {
                { 1, "Spades" },
                { 2, "Diamonds" },
                { 3, "Hearts" },
                { 4, "Clubs" }
            };

            int value = randCard.Next(2, 15);
            int suit = randCard.Next(1, 5);
            Console.Write(" and was dealt the {0} of {1}\n"
                                , numbers[value], suits[suit]);

            //Making the faces have value of 10
            if(value > 10 && value < 14)
            {
                value = 10;
            }
            //Making the Aces have value of 11
            if (value == 14)
            {
                value = 11;
            }
            return value;
        }
    }
}

I don't know if including the namespace and "using System" is needed in every code snippet so I decided to include them.

\$\endgroup\$
3
\$\begingroup\$

You should have one Player class, the Dealer class should inherit the Player class so that it can make use of all the player methods inside of it.

If you need a Method for the dealer that you don't want the player to have, it is okay Those methods can be put into the Dealer class.

That is one of the benefits of OOP (Object Oriented Programming). By extending the Player class you DRY (Don't Repeat Yourself) your code.

\$\endgroup\$
  • 1
    \$\begingroup\$ there is more, I will return. \$\endgroup\$ – Malachi Apr 18 '18 at 22:04
  • \$\begingroup\$ I did this and had dealer and player inherit from a base as some stuff they do different (e.g. hit) \$\endgroup\$ – paparazzo Apr 19 '18 at 17:40
  • \$\begingroup\$ I have a BlackJack program too, not sure if I posted the code, mine might be in VB \$\endgroup\$ – Malachi Apr 19 '18 at 17:43
1
\$\begingroup\$

So, I decided to revisit this project of mine that I had abandoned, thinking that I got what experience I could from it. I changed some parts of the program and was able to add the missing features. Firstly, I took into account Malachi's suggestion of having the dealer class inherit from the player class and made the change. I cleaned up the DealCard() method, splitting it into 2 methods: the CreateDeck() and DealCard() which was slightly changed to be able to give the dealer his secret card and prevent duplicate cards being dealt. Finally the Player class got 2 new methods: the GetTotal() method to add the total value of his cards that are now being put into a list first so the second method, ChangeAces() can work. Also I forgot to mention that the cards are in a list now instead of a dictionary.

Here is the updated parts of the code:

The Program class is the same as before

The Game class:

using System;

class Game
{
    byte status = (byte)Status.gameOn;
    House house = new House();
    Player player1 = new Player("");
    Card deck = new Card();

    //player hits as many times as he wants when < 21
    public void PlayerHit()
    {
        player1.CheckIfBust();
        if (!player1.Bust)
        {
            Console.Write("{0} Hits", player1.Name);
            player1.hand.Add(deck.DealCard(false));
            player1.GetTotal();
        }
    }

    //Initial cards
    public void InitialHits()
    {
        PlayerHit();
        PlayerHit();

        house.CheckIfBust();
        if (!house.Bust)
        {
            Console.Write("The House Hits");
            house.hand.Add(deck.DealCard(false));
            Console.WriteLine("The House gets a secret card");
            house.hand.Add(deck.DealCard(true));
            house.CheckIfBust();
            house.ChangeAces();
        }
    }

    //House hits untill it reaches 16
    public int HouseHit()
    {
        while (house.total < 16)
        {
            Console.Write("The House Hits");
            house.hand.Add(deck.DealCard(false));
            house.CheckIfBust();
            house.ChangeAces();
        }
        house.Stand();
        return house.total;
    }

    public void Play(string pName)
    {
        player1.Name = pName;
        player1.CheckIfBust();
        deck.CreateDeck();

        //Initial cards
        InitialHits();

        //Player wins automatically if he has a "blackjack"
        if (player1.HasBlackjack() == true)
        {
            Console.WriteLine($"{player1.Name} has a Blackjack!");
            status = (byte)Status.blackjack;
        }
        else
        {
            //loops as long as the player hasn't gone bust
            while (!player1.Bust && !player1.isStanding)
            {
                //Player gives input
                Console.WriteLine($"It is {player1.Name}'s turn:");
                Console.WriteLine("Press H to hit   Press S to stand" +
                    "\nPress V to view your hand");

                string input = Console.ReadLine();
                //input check
                if (input == "h" || input == "H")
                {
                    PlayerHit();
                }
                else if (input == "s" || input == "S")
                {
                    player1.Stand();
                    break;
                }
                else if (input == "v" || input == "V")
                {
                    player1.ViewHand();
                }
                else
                {
                    Console.Write("Unknown command\nPlease type a valid command ");
                    input = Console.ReadLine();
                }
                player1.CheckIfBust();
                player1.ChangeAces();
            }
            if (player1.Bust)
            {
                Console.WriteLine("{0}'s hand is {1} so he goes bust"
                    , player1.Name, player1.total);
            }
            HouseHit();
            CompareHands();
        }
        AnounceWinner();
    }

    public void AnounceWinner()
    {
        switch (status)
        {
            case 1:
                Console.WriteLine($"{player1.Name} wins!");
                break;
            case 2:
                Console.WriteLine("The House wins!");
                break;
            case 3:
                Console.WriteLine("Is is a draw");
                break;
            case 4:
                Console.WriteLine($"{player1.Name} wins!");
                break;
        }
    }

    void CompareHands()
    {
        if (player1.total > 21)
            player1.total = 0;
        if (house.total > 21)
            house.total = 0;

        if (player1.total > house.total)
        {
            status = (byte)Status.playerWin;
        }
        else if (house.total > player1.total)
        {
            status = (byte)Status.houseWin;
        }
        else
        {
            status = (byte)Status.draw;
        }
        if (player1.Bust && house.Bust)
        {
            status = (byte)Status.draw;
        }
    }
}

The Player class:

using System;
using System.Collections.Generic;

class Player
{
    public string Name { get; set; }
    public List<int> hand = new List<int>();
    public bool Bust = false;
    public bool isStanding = false;
    public int total;

    public Player(string name = "")
    {
        Name = name;
        total = 0;
    }

    public int GetTotal()
    {
        total = 0;
        foreach (int card in hand)
        {
            total += card;
        }
        return total;
    }

    public bool HasBlackjack()
    {
        return (total == 21) ? true : false;
    }

    public virtual void Stand()
    {
        CheckIfBust();
        if (!Bust)
        {
            Console.WriteLine("{0} stands at {1}", Name, total);
        }
        isStanding = true;
    }

    public virtual void ViewHand()
    {
        Console.WriteLine("{0}'s hand is {1}", Name, total);
    }

    public bool CheckIfBust()
    {
        GetTotal();
        return Bust = (total > 21) ? true : false;
    }

    public void ChangeAces()
    {
        while (Bust && hand.Contains(11))
        {
            hand[hand.FindIndex(index => index.Equals(11))] = 1;
            CheckIfBust();
        }
    }
}

The Dealer class:

using System;

class House : Player
{
    public override void Stand()
    {
        CheckIfBust();
        if (Bust)
        {
            Console.WriteLine("The Houses's hand was {0} so it went bust", total);
            total = 0;
        }
        else
        {
            Console.WriteLine("The House stands at {0}", total);
        }
    }
}

The Card class:

using System;
using System.Collections.Generic;

class Card
{
    private readonly Random randCard = new Random(DateTime.Now.Millisecond);
    public List<string> Deck = new List<string>();

    public void CreateDeck()
    {
        List<string> values = new List<string>
        {
            "2",
            "3",
            "4",
            "5",
            "6",
            "7",
            "8",
            "9",
            "10",
            "Jack",
            "Queen",
            "King",
            "Ace"
        };

        List<string> suits = new List<string>
        {
            "Spades",
            "Diamonds",
            "Hearts",
            "Clubs"
        };

        foreach (string value in values)
        {
            foreach (string suit in suits)
            {
                Deck.Add($"{value} of {suit}");
            }
        }
    }

    public int DealCard(bool secret)
    {
        int value = 0;
        int randomCard = randCard.Next(Deck.Count);
        string card = Deck[randomCard];
        Deck.RemoveAt(randomCard);

        if (!secret)
        {
            Console.Write(" and was dealt the {0}\n", card);
        }

        //Making the numbers exept 10 have their value
        if (card[0] == '2' || card[0] == '3' || card[0] == '4' || card[0] == '5' ||
            card[0] == '6' || card[0] == '7' || card[0] == '8' || card[0] == '9')
        {
            value = int.Parse(card[0].ToString());
        }
        //Making the faces and 10 have a value of 10
        else if (card[0] == '1' || card[0] == 'J' || card[0] == 'Q' || card[0] == 'K')
        {
            value = 10;
        }
        //Making the Aces have a value of 11
        else if (card[0] == 'A')
        {
            value = 11;
        }
        return value;
    }
}
\$\endgroup\$
  • \$\begingroup\$ you should post another question with the new code. I apologize for not getting back to you, I have been meaning to look through my old BlackJack application(s) and see what you did better and share what I think would be helpful to you. \$\endgroup\$ – Malachi May 2 '18 at 18:45
  • \$\begingroup\$ Ok I will post a new question when I have some free time. Just one question, I've read in the guidelines that if I find a solution to my problem it is advised that I posted a self answer. Why is in this case better to post a seperate question? Also should i take this question down or leave it be? \$\endgroup\$ – BlackBox May 2 '18 at 18:53
  • \$\begingroup\$ this answer is good, you explained what you changed and why, so it can stay here, along with the question itself. a follow up question is also acceptable to see if there is anything that others might see in your new code. Check out this meta answer about follow up/iterative reviews \$\endgroup\$ – Malachi May 2 '18 at 19:03

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.