C# Console Black Jack

Всем хай ,прогаю на c# 3 месяца ,сделал блек джек в консоли, можете оценить код и сказать какие у меня там ошибки, где можно сделать более красиво или эффективно.Помогите улучшить свой говнокод))

using System;
using System.Collections.Generic;

namespace _21
{
    public class Deck
    {
        protected List<string> deck = new List<string>{"2", "2", "2", "2", "3", "3", "3", "4", "4", "4", "4", "4", "5", "5", "5", "5", "6", "6", "6", "6", "7", "7", "7", "7", "8", "8", "8", "8", "9", "9", "9", "9", "j", "j", "j", "j", "q", "q", "q", "q", "k", "k", "k", "k", "a", "a", "a", "a"};
        protected string GiveCard()
        {
            Random rnd = new Random();
            int cardindex = rnd.Next(0, deck.Count);
            string card =  deck[cardindex];
            deck.RemoveAt(cardindex);
            return card;

        }
    }
    public class Player: Deck
    {
        private List<string> playershand = new List<string>();
        private int playershandprice;
        private int balance = 1000;
        private int playersbet;
        public void Bet(int bet)
        {
            balance -= bet;
            playersbet = bet;
        }
        public int GetPlayersBet()
        {
            return playersbet;
        }
        public void GivePrize(int prize)
        {
            balance += prize;
        }
        public void  Hand()
        {
            playershand.Add(GiveCard());
            playershand.Add(GiveCard());
            for (int i = 0; i < 2; i++)
            {
                int cardprice;
                if(int.TryParse(playershand[i], out cardprice))
                {
                    playershandprice += cardprice;     
                }
                else
                {
                    if(playershand[i]!="a")
                    {
                        playershandprice += 10;
                    }
                    else
                    {
                        if(playershandprice == 11)
                        {
                            playershandprice = 2;
                        }
                        else 
                        {
                            playershandprice += 11;
                        }
                    }
                }
            }
        }
        public void ShowPlaersHandPrice()
        {
            Console.WriteLine(playershandprice);
        }
        public void AddCard()
        {
            playershand.Add(GiveCard());
            int cardprice;
            if (int.TryParse(playershand[playershand.Count-1], out cardprice))
            {
                playershandprice += cardprice;
            }
            else
            {
                if (playershand[playershand.Count - 1] != "a")
                {
                    playershandprice += 10;
                }
                else
                {
                    playershandprice += 11;
                }
            }
        }
        public int GetPlaersHandPrice()
        {
            return playershandprice;
        }
        public int GetPlayersBalance()
        {
            return balance;
        }

    }

    public class Dealer: Deck
    {
        private string[] dealershand = {"",""};
        private int dealershandprice;
        private int dealersfirstcard;
        public void Hand()
        {
            dealershand[0] = GiveCard();
            dealershand[1] = GiveCard();
            
            for (int i = 0; i < 2; i++)
            {
                int cardprice;
                if (int.TryParse(dealershand[i], out cardprice))
                {
                    dealershandprice += cardprice;
                }
                else
                {
                    if (dealershand[i] != "a")
                    {
                        dealershandprice += 10;
                    }
                    else
                    {
                        if (dealershandprice == 11)
                        {
                            dealershandprice = 2;
                        }
                        else
                        {
                            dealershandprice += 11;
                        }
                    }
                }
                if (i == 0)
                {
                    dealersfirstcard = cardprice;
                }
            }
        }
        public void ShowDealersFirstCard()
        {
            Console.WriteLine(dealersfirstcard);
        }
        public void ShowDealersHandPrice()
        {
            Console.WriteLine(dealershandprice);
        }
        public int GetDealersHandPrice()
        {
            return dealershandprice;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string flag = "";
            while(flag!="end")
            {
                Console.WriteLine("Добро пожаловать в блек джек");
                Console.WriteLine("Введите start ,чтобы начать играть");
                flag = Console.ReadLine();
                if(flag == "start")
                {
                    Deck deck = new Deck();
                    Player player= new Player();
                    Dealer dealer = new Dealer();
                    Console.Write("Ваш баланс:");
                    Console.WriteLine(player.GetPlayersBalance());
                    Console.Write("введите вашу ставку:");
                    player.Bet(int.Parse(Console.ReadLine()));
                    player.Hand();
                    Console.Write("Ваша рука:");
                    player.ShowPlaersHandPrice();
                    dealer.Hand();
                    Console.Write("1 карта дилера:");
                    dealer.ShowDealersFirstCard();
                    string flag1 = "";
                    while(flag1!="no")
                    {
                        Console.WriteLine("Введите yes , если xотите добрать карту, и no ,если не хотите");
                        flag1 = Console.ReadLine();
                        if(flag1 =="yes")
                        {
                            player.AddCard();
                            Console.Write("Ваша рука:");
                            player.ShowPlaersHandPrice();
                        }
                    }
                    if(player.GetPlaersHandPrice() > 21)
                    {
                        Console.WriteLine("Ваша рука:");
                        player.ShowPlaersHandPrice();
                        Console.Write("Рука дилера:");
                        dealer.ShowDealersHandPrice();
                        Console.WriteLine("Вы проиграли,ваша рука больше 21");

                    }
                    else if(player.GetPlaersHandPrice() < dealer.GetDealersHandPrice())
                    {
                        Console.WriteLine("Ваша рука:");
                        player.ShowPlaersHandPrice();
                        Console.Write("Рука дилера:");
                        dealer.ShowDealersHandPrice();
                        Console.WriteLine("Вы проиграли,рука дилера больше");
                    }
                    else if(player.GetPlaersHandPrice() > dealer.GetDealersHandPrice())
                    {
                        if(player.GetPlaersHandPrice()!=21)
                            player.GivePrize(player.GetPlayersBet()*2);
                        else
                            player.GivePrize(player.GetPlayersBet() * 3);
                        Console.WriteLine("Ваша рука:");
                        player.ShowPlaersHandPrice();
                        Console.Write("Рука дилера:");
                        dealer.ShowDealersHandPrice();
                        Console.WriteLine("Вы Выиграли");
                        Console.Write("Ваш баланс:");
                        Console.WriteLine(player.GetPlayersBalance());
                        
                    }
                    else
                    {
                        player.GivePrize(player.GetPlayersBet());
                        Console.WriteLine("Ваша рука:");
                        player.ShowPlaersHandPrice();
                        Console.Write("Рука дилера:");
                        dealer.ShowDealersHandPrice();
                        Console.WriteLine("Ничья");
                        Console.Write("Ваш баланс:");
                        Console.WriteLine(player.GetPlayersBalance());
                    }
                }
                else
                {
                    flag = "end";
                }
            }
        }
    }
}

Ответы (1 шт):

Автор решения: EvgeniyZ
  1. Много if-else.
  2. Дублируете код.
  3. string flag плохо, используйте специально созданный для этого enum.
  4. Ошибка в игре, где у игрока и дилера своя колода карт.
  5. Есть проблемы с именованием, а именно dealershandprice, принято писать dealersHandPrice, каждое слово с большой буквы.
  6. if (dealershand[i] != "a") - наверно стоит сделать отдельный класс, который будет содержать в себе информацию о карте?
  7. Наследуете Player от Deck, это если бы вы наследовали машину от карандаша. Игрок не может быть колодой карт.
  8. player.Bet(int.Parse(Console.ReadLine())); - а если напишу wqeqe?
  9. Можно брать бесконечное число карт, даже если у игрока уже выше 21.
  10. После игры нужно повторно писать start, зачем тогда баланс, если он сбивается?

Это, пожалуй, минимум, который я нашел, мельком проверив ваш код. Давайте теперь попробуем сделать более-менее приемлемо.

  1. Создадим новый класс, назовем его Card, пусть отвечает за одну конкретную карту (тут играйтесь как хотите, я для примера добавлю название):

     public class Card
     {
         public Card(string name, int value, CardType type)
             => (Name, Value, Type) = (name, value, type);
    
         public string Name { get; init; }
         public int Value { get; init; }
         public CardType Type { get; init; }
     }
    

    Тип тут является простым enum:

     public enum CardType
     {
         Number,
         Jack,
         Queen,
         King,
         Ace
     }
    
  2. Создадим теперь колоду карт (класс Deck):

     public class Deck
     {
         private readonly List<Card> cards = new();
         public Deck() => GenerateCards();
    
         public Card Take()
         {
             if (IsEmpty) return null;
             var card = cards[new Random().Next(cards.Count)];
             cards.Remove(card);
    
             return card;
         }
    
         public bool IsEmpty => !cards.Any();
         public int Count => cards.Count;
    
         private void GenerateCards()
         {
             foreach (var type in Enum.GetValues<CardType>())
             {
                 if (type is CardType.Number)
                 {
                     for (int i = 2; i <= 9; i++)
                     {
                         cards.AddRange(CreateCardsByType(type, i));
                     }
                 }
                 else
                 {
                     cards.AddRange(CreateCardsByType(type));
                 }
             }
         }
    
         private IEnumerable<Card> CreateCardsByType(CardType type, int value = 0)
         {
             List<Card> result = new();
    
             for (int i = 0; i < 4; i++)
             {
                 Card card = type switch
                 {
                     CardType.Number => new($"{value}", value, type),
                     CardType.Jack => new($"Валет", 10, type),
                     CardType.Queen => new($"Дама", 10, type),
                     CardType.King => new($"Король", 10, type),
                     CardType.Ace => new($"Туз", 11, type),
                 };
    
                 result.Add(card);
             }
    
             return result;
         }
     }
    

    Здесь я заменил ваш статичный набор на простую генерацию.

    • List<Card> cards - набор наших карт.
    • public Deck() => GenerateCards(); - вызываем генерацию при инициализации класса.
    • Card Take() - Взять (если есть) карту и удалить ее из колоды.
    • bool IsEmpty - Проверяем, есть ли еще карта. .Any() - это LINQ, можно заменить на нечто простое, по типу cards.Count == 0 (cards is { Count: 0 }).
    • int Count - коллекция карт наружу не отдается, а число карт может и понадобиться.
    • void GenerateCards() - Генерируем колоду карт.
    • IEnumerable<Card> CreateCardsByType(...) - Создаем 4 карты одного типа.
  3. Создаем игрока (класс Player):

     public class Player
     {
         private Deck deck;
         private List<Card> cards = new();
    
         public Player(string name, Deck deck) 
             => (Name, this.deck) = (name, deck);
    
         public string Name { get; init; }
         public int Money { get; private set; } = 1000;
         public int Score { get; private set; }
         public int Cards => cards.Count;
         public bool IsLost => Score > 21;
    
         public bool Take()
         {
             if (IsLost) return false;
             var card = deck.Take();
             cards.Add(card);
             Score += card.Value;
             return !IsLost;
         }
    
         public void Reset()
         {
             Score = 0;
             cards.Clear();
         }
     }
    

    Я не стал тут реализовывать функционал денег, думаю справитесь сами, задача была просто и наглядно показать, как стоит поступить, а конкретно то, что в классе есть только необходимый для игрока функционал.

  4. Так, колода карт есть, игрок есть, дилер, по сути, тоже есть (ведь это тоже в некотором роде игрок, верно?), осталось наклепать класс игры (Game):

     public class Game
     {
         private Deck deck = new();
         private Player[] players;
    
         public Game()
         {
             players = new[]
             {
                 new Player("Дилер", deck),
                 new Player("Игрок", deck)
             };
    
             Start();
         }
    
         public bool IsEnded { get; private set; }
    
         public void Start()
         {
             bool replay = true;
             while (replay)
             {
                 Console.Clear();
                 Turn();
                 IsEnded = players.Any(x => x.IsLost);
    
                 Console.ReadLine();
    
                 if (IsEnded)
                     replay = Replay();
             }
         }
    
         private void Turn()
         {
             foreach (var player in players)
             {
                 var status = player.Take() ? "Еще в игре" : "Проиграл";
                 Console.WriteLine($"У {player.Name} {player.Score}, он {status}");
             }
         }
    
         private bool Replay()
         {
             bool result = false;
    
             while (true)
             {
                 Console.Clear();
                 Console.Write("Повторить? (y/n): ");
                 var value = Console.ReadLine();
    
                 if (value is "y" or "n")
                 {
                     result = value is "y";
                     break;
                 }
             }
    
             if (result)
             {
                 Reset();
             }
    
             return result;
         }
    
         private void Reset()
         {
             deck = new();
             foreach (var player in players)
             {
                 player.Reset();
             }
         }
     }
    

    Опять же, я многое не сделал, лишь показал каркас того, как стоило бы поступить. Этот класс будет поочередно брать карту у каждого игрока из массива, а когда один из них зайдет за пределы, игра завершится и предложит нам переиграть.

Этим ответом я не пытаюсь решить ваши косяки (о которых написал выше), моя задача показать вам как в языке C# используются базовые принципы ООП, а именно, создаются отдельные объекты, со своей логикой и значениями, как они друг с другом взаимодействуют. Старайтесь следовать этим правилам, а также правилам SOLID, это очень сильно облегчит вам жизнь!

Из того, что нужно доделать (домашнее задание?):

  • Реализовать валюту игрока (ее трату и пополнение, а также запоминание, чтобы каждая игра начиналась со старым балансом.).
  • Реализовать логику туза, ведь эта карта может дать как 1, так и 11.
  • Реализовать запрос "взять еще карту?".
  • Реализовать завершение игры, если закончились карты.
  • Постараться вынести все взаимодействие с консолью в отдельный класс.

В общем, удачи вам в изучении C#!

→ Ссылка