カードゲーム戦争

私は自分が作った簡単な戦争カードゲームプログラムのレビューを探している初心者プログラマです。各プレイヤーはデッキを手に入れ、あなたは一度に1枚のカードを引いて2を比較する。デッキがなくなり、最も多くのポイントを獲得した人が勝つまでそれをやります。何かアドバイスは大歓迎です!これを見ていただきありがとうございます。

enum Suit
{
    Hearts,
    Diamonds,
    Spades,
    Clovers
}

enum Face
{
    Two,
    Three,
    Four,    
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
    Ace
}

// MAKING WAR CARD GAME

class Program
{
    public static bool isPlaying = true;
    public static Player human = new Player();
    public static Player cpu = new Player();
    public static int round = 1;
    static void Main(string[] args)
    {
        GameFlow.Start(human, cpu);
        while (isPlaying)
        {
            for (int i = 0; i < 52; i++)
            {
                GameFlow.Gameplay(human, cpu, round);
                round++;
            }

            round = 1;
            GameFlow.Ending(human, cpu);
        }
    }
}
class Player
{
    public string name = "CPU";
    public Card currentCard = new Card();//the current card in play
    public int wins = 0 , drawCount = 0;
    public Deck myDeck = new Deck();

    public void DrawOne()
    {
        currentCard = myDeck.CurrentlyInDeck[drawCount];
        drawCount++;
        if (drawCount == 52)
        {
            drawCount = 0;
        }
    }

    public void SetName()
    {
        while (name == "CPU" || name == "")
        {
            Console.WriteLine("What is your name human?");
            name = Console.ReadLine();
            if(name == "CPU" || name == "")
            {
                Console.WriteLine("Invalid name!! Try a real name you assclown!");
                Console.ReadLine();
            }
            Console.Clear();
        }
    }
class Card
{
    public Suit suit;
    public Face face;

    public void PrintCard()
    {
        Console.Write("{0} of {1}", face, suit);
    }
}
class Deck
{
    public List CurrentlyInDeck;

    public void GenerateDeck()//Must use this to populate deck
    {
        CurrentlyInDeck = new List(52);
        int place = 0;//tracks which card number
        for (int i = 0; i < 4; i++)
        {
            for (int f = 0; f < 13; f++)
            {
                Card card = new Card();
                CurrentlyInDeck.Add(card);
                CurrentlyInDeck[place].suit = (Suit)i;
                CurrentlyInDeck[place].face = (Face)f;
                place++;
            }
        }
    }

    private static readonly Random rand = new Random();

    public void Shuffle() 
    {
        Card holder = new Card();
        int random;

        for (int i = 0; i < 52; i++)
        {
            random = rand.Next(0, 51);
            holder = CurrentlyInDeck[i];
            CurrentlyInDeck[i] = CurrentlyInDeck[random];
            CurrentlyInDeck[random] = holder;
        }
    }

    public void PrintDeck()//prints all cards in the deck , used for testing
    {
        foreach (var card in CurrentlyInDeck)
        {
            card.PrintCard();
        }
    }
}
static class Create
{
    public static void Name(Player a)
    {
        a.SetName();
    }

    public static void TheirDecks(Player hum , Player cpu)
    {
        hum.myDeck.GenerateDeck();
        cpu.myDeck.GenerateDeck();

        hum.myDeck.Shuffle();
        cpu.myDeck.Shuffle();
    }
}
static class GameFlow
{
    static public void Start(Player hum, Player cpu) //Creates initial situation 
    {
        Words.Begin();
        Create.TheirDecks(hum, cpu);
        Create.Name(hum);
    }

    static public void ShuffleDecks(Player hum, Player cpu)
    {
        hum.myDeck.Shuffle();
        cpu.myDeck.Shuffle();
    }

    static public void DrawCards(Player hum, Player cpu)//changes the current card
    {
        hum.DrawOne();
        cpu.DrawOne();
    }

    static public void Gameplay(Player hum, Player cpu, int round)//The normal gameplay loop
    {
        GameFlow.DrawCards(hum, cpu);
        Words.Template(hum, cpu, round);
        GameFlow.CheckWin(hum, cpu);
    }

    static public void Ending(Player hum, Player cpu)//Once all the cards have been played
    {
        string answer = "";
        bool correctForm = false;
        Words.LastWords(hum, cpu);
        while (!correctForm)
        {
            Console.WriteLine("Would you like to play again? Enter y for yes or n for no.");
            answer = Console.ReadLine();
            answer = answer.ToLower();

            if(answer != "y" && answer != "n")
            {
                Console.WriteLine("Thats not a valid option you idiot!");
                Console.WriteLine("Press enter to continue.");
                Console.ReadLine();
                Console.Clear();
                Words.LastWords(hum, cpu);
            }

            if(answer == "y" || answer == "n")
            {
                correctForm = true;
            }
        }

        if (answer == "y")
        {
            PlayingAgain(hum ,cpu);
            Console.Clear();
        }

        if (answer == "n")
        {
            Console.WriteLine("Thanks for playing!");
            Console.WriteLine("Press enter to exit.");
            Environment.Exit(0);
        }
    }

    static public void PlayingAgain(Player hum , Player cpu)
    {
        ShuffleDecks(hum, cpu);
        hum.wins = 0;
        cpu.wins = 0;
    }

    static public void CheckWin(Player hum , Player cpu)
    {
        Console.WriteLine("");
        if ((int)hum.currentCard.face > (int)cpu.currentCard.face)
        {
            hum.wins++;
            Console.WriteLine();
            Console.WriteLine("You win this one! Nice!");
            Console.WriteLine("Press enter to keep going.");
            Console.ReadLine();
            Console.Clear();
        }

        else if ((int)hum.currentCard.face < (int)cpu.currentCard.face)
        {
            cpu.wins++;
            Console.WriteLine();
            Console.WriteLine("Looks like the CPU won this one. You suck!");
            Console.WriteLine("Press enter to keep going.");
            Console.ReadLine();
            Console.Clear();
        }

        else
        {
            Console.WriteLine();
            Console.WriteLine("Its a draw!");
            Console.WriteLine("Press enter to keep going.");
            Console.ReadLine();
            Console.Clear();
        }
    }
}
static class Words//The dialogue of the program
{
    public static void Begin()
    {
        Console.WriteLine("Hello , welcome to war!");
        Console.WriteLine("The rules are simple. Its you vs a cpu");
        Console.WriteLine("You each pull 1 card from your decks and see which is highest.");
        Console.WriteLine("Your card will be the one under your name , while the other is the CPUs.");
        Console.WriteLine("The one with the most points at the end wins all the glory!");
        Console.WriteLine("Understand?");
        Console.WriteLine("Press enter if you're ready for the showdown.");
        Console.ReadLine();
        Console.Clear();
    }

    public static void Template(Player hum , Player cpu , int round) //Prints the normal screen
    {
        int distance = 17; //distance between the 2 names
        Console.Write("{0} wins = {1}", hum.name, hum.wins);
        Console.Write("          R{0}", round);
        distance -= hum.name.Length;
        while (distance > 0)
        {
            Console.Write(" ");
            distance--;
        }
        Console.Write("{0} wins = {1}" , cpu.name , cpu.wins);
        Console.WriteLine();
        Console.WriteLine();
        hum.currentCard.PrintCard();
        Console.Write("     vs     ");
        cpu.currentCard.PrintCard();
    }

    public static void LastWords(Player hum , Player cpu)
    {
        Console.WriteLine("Thats all the cards!");
        Console.WriteLine("So this is the final score: {0} = {1} and {2} = {3}" , hum.name, hum.wins, cpu.name, cpu.wins);

        if(hum.wins > cpu.wins)
        {
            Console.WriteLine("You have beaten the CPU in this game of luck!!!! Congrats!!!");
        }

        else if(cpu.wins > hum.wins)
        {
            Console.WriteLine("The CPU has detroyed you and everything you have loved. Congrats!!!");
        }

        else
        {
            Console.WriteLine("Oh my! Its a draw. You both are equal warriors of luck.");
        }
    }
}
6
nl ru de
ゲームのしくみについての要約を追加してください。
追加された 著者 Simon Forsberg,
追加された 著者 Marek,
@SimonForsberg public static void Begin()にはかなりまともな説明が含まれています。
追加された 著者 user52915,
私はプログラミングの初心者です - これはただ不思議に役立ちました。投稿ありがとうございます。
追加された 著者 Shashank P,

4 答え

もう一つの答えはすでに Fisher-Yates Shuffle に言及していますが、これは間違いなくあなたのものです。使いたい。私はあなたのシャッフルを少し分解してそれがなぜ欠陥があるのか​​をあなたに示すと思いました。

This is what I consider to be one of the best web pages on the entire internet, and it has a section on shuffling that you might find interesting: Visualizing Algorithms by Mike Bostock

マイクもこのリソースを作成しました:シャッフルしますか?このページに移動して変更するあなたが実装したものである「ナイーブスワップ(私はランダム)」の選択。シャッフルがかなり偏っていることがわかります。

naïve swap (i ↦ random)

それでも十分でない場合は、誤って追加のバイアスを追加したことになります。アルゴリズムをJSに変換して実行しました。

naive swap

右下にその非常に濃い紫色の四角が見えますか。それは最後の要素に対して非常に負のバイアスを示しています。その理由を見てみましょう。

public void Shuffle() 
{
    Card holder = new Card();
    int random;

    for (int i = 0; i < 52; i++)
    {
        random = rand.Next(0, 51);//!!!
        holder = CurrentlyInDeck[i];
        CurrentlyInDeck[i] = CurrentlyInDeck[random];
        CurrentlyInDeck[random] = holder;
    }
}

ランダムのドキュメント。次のは言う(私のことを強調する):

minValue以上、未満 maxValue以上の32ビット符号付き整数。つまり、戻り値の範囲にはminValueは含まれますが、maxValueは含まれません。

つまり、0から50までの乱数のみを生成しているということです。つまり、ループの最後の繰り返しで交換されるだけなので、最後のアイテムを元の場所に配置することはできません。それはシャッフルにとってはとても悪いことです。

8
追加された
うわー、このWebページは大幅に改善されました。前回行ったことがあるのは、マッチスティックを並べ替えることで並べ替えがどのように機能するかをのみ示したことです。
追加された 著者 t3chb0t,
情報をありがとう私はこれを調べます。リソースをありがとう。
追加された 著者 user145742,

まず私があなたがクラスと関数の名前に入れた考えを賞賛すると言わせてください。たとえば、 Create.TheirDecks()は一種の流暢な感じがします。 「あなたのコードは文章のように読むべきだ」と私はよくアドバイスしますが、すでにそれを心に留めているようです。

これは(まだ)完全な答えではありませんが、これはDeckクラスに関する私のメモです。

  1. There is a temporal coupling for users of the Deck class. If I instantiate a new Deck(), and then Shuffle() it, I will get a null reference exception because I failed to call GenerateDeck() in between. Is there ever value in having a new deck with no cards? I would say no, new decks should always have cards. That would mean the code to add cards belongs in (or should be called from) the constructor.

  2. The code to fill the deck with cards uses two for loops. I am against use the use of for, for anything but generating a sequence of numbers. I would highly recommend using foreach to iterate through your Suits and Faces; you can do this with Enum.GetValues.

  3. The Cards in the deck are exposed as a public list. This means that when I want to draw from the deck, I have to manage the list myself. This is an excellent opportunity to practice encapsulation. You can make things simpler for users of the class, and at the same time make it easier for yourself to refactor in the future, by providing a public Card Draw() method and making CurrentlyInDeck private. Then, if you later decide that you want to make CurrentlyInDeck a Stack, or a Queue? You can make that change without touching any code outside of the class (and be sure you haven't broken code anywhere else either).

  4. The Shuffle method is pretty good! This is one of those rare cases where I agree that a for loop is the right choice. If you want to implement a proper Fisher-Yates shuffle, I believe you'll only need to change to rand.Next(i, 51). The other change I might make here is to add a private void SwapCards(int i, int j) method, so that the outline of the algorithm is as readable as possible.

  5. The PrintDeck method is a bit odd.

    1. It's a method of the Deck class, and it's also got Deck in the name. If nothing else, I'd rename this to just Print.
    2. It prints (because of the behavior of Card) directly to Console. Say I want to print to a file, or log to a database? I think you'd be better off returning a string representation the deck, so that I can do what I want with it. In fact, the best way to do that would probably be to override ToString()
    3. If it's code used only for testing, now that your class is developed, you might as well just delete it.
  6. This is a very minor point, but I would much rather see i += 1 than i++. The fact that the ++ hieroglyph adds one to the variable it comes after (or sometimes before!) is not obvious or explicit, and typing 6 characters instead of 3 will certainly not harm any developer. The fact that ++ even exists in C# is a major regret (#3) of one of the language's designers.

私はそれがたくさんのメモであることを知っています、しかし私はこれが初心者のためのかなり良いコードであることを強調したいです!もう少し時間が来たら、もう少しメモを付けてもう一度チェックインします。

7
追加された
情報ありがとうございました!
追加された 著者 user145742,

他に誰も言及していないようなものを追加しましょう。定数または読み取り専用でない限り、パブリックフィールドを使用しないでください。代わりにパブリックプロパティを使用してください。たとえば、 Card クラスを考えてみましょうが、あなたは他のクラスに罪があります。

class Card
{
    public Suit suit;
    public Face face;

    public void PrintCard()
    {
        Console.Write("{0} of {1}", face, suit);
    }
}

私は見たいと思うでしょう:

  • 明示的なアクセス修飾子
  • フィールドではなく読み取り専用のプロパティ
  • プロパティ名はPascalCasedにしてください。
  • メソッド名 PrintCardPrint に単純化できますが、実際には ToString または Name プロパティになります。

だからそれは次のように書き直されるでしょう:

public class Card
{
    public Suit Suit { get; }
    public Face Face { get; }

    public Card(Face face, Suit suit)
    {
        Face = face;
        Suit = suit; 
    }

    public string Name => $"{Face}{Suit}";
    public override string ToString() => Name;
}

あなたはマジックナンバーやデッキサイズに基づいてそれらを移入する必要はありません。直接設定してください。例:

public void GenerateDeck()
{
    CurrentlyInDeck = new List(); //Not concerned with count.
    var suits = Enum.GetValues(typeof(Suit)).Cast().ToList();
    var faces = Enum.GetValues(typeof(Face)).Cast().ToList();
    foreach (var suit in suits)
    {
        foreach (var face in faces)
        {
            CurrentlyInDeck.Add(new Card(face, suit));
        }
    }
}
2
追加された

私は public void SetName()が好きではありません。これは単体テストを問題にします。ユーザー名をコンストラクターに渡します。

悪いシャッフルはFischer Yatesを使います。

スーツと顔を card に直接割り当ててから追加します。

大域変数を参照するのではなく、公開無効Shuffle()にデッキを渡してください。

isPlaying is always true. What purpose does it serve?

これは、より少ないコードで実行できます。

namespace War
{
    class Program
    {
        static void Main(string[] args)
        {
            War();
        }
        private static Random rand = new Random();
        public static void War()
        {
            Console.WriteLine("What is you name?");
            string name = Console.ReadLine();
            Player human = new Player(name);
            Player computer = new Player("computer");
            bool play = true;
            while (play)
            {
                for (int i = 0; i < 52; i++)
                {
                    if (human.Deck[i].RankI > computer.Deck[i].RankI)
                    {
                        human.Wins++;
                    }
                    else
                    {
                        computer.Wins++;
                    }
                }
                Console.WriteLine($"computer {computer.Wins}  {name} {human.Wins}");
                Console.WriteLine("Enter c to play again");
                string playS = Console.ReadLine();
                if (playS == "c" || playS == "C")
                {
                    human.Deck = Shuffle(human.Deck);
                    computer.Deck = Shuffle(computer.Deck);
                    human.Wins = 0;
                    computer.Wins = 0;
                }
                else
                {
                    play = false;
                }
            }
        }
        public enum suit { Hearts, Diamonds, Clubs, Spades };
        public enum rank { Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace };
        public class Card
        {
            private int I;
            public suit Suit { get { return (suit)(I/13); } }
            public rank Rank { get { return (rank)(RankI); } }
            public int RankI { get { return I % 13; } }
            public override string ToString()
            {
                return $"{Rank} {Suit}";
            }
            public Card (int i)
            {
                I = i;
            }
        }
        public class Player
        {
            public string Name { get; }
            public int Wins { get; set; }
            public List Deck { get; set; } = new List();
            public Player(string name)
            {
                Name = name;
                for (int i = 0; i < 52; i++)
                {
                    Card card = new Card(i);
                    Deck.Add(card);
                    //Debug.WriteLine(card.ToString());
                }
                Deck = Shuffle(Deck);
                //Debug.WriteLine("");
                //for (int i = 0; i < 52; i++)
                //{
               //   Debug.WriteLine(Deck[i].ToString());
                //}
            }
        }
        public static List Shuffle(List deck)
        {
            List shuffled = new List(deck);
            for (int i = shuffled.Count - 1; i > 0; i--)
            {
                int random = rand.Next(i + 1);
                if(random != i)
                {
                    Card cardR = shuffled[random];
                    shuffled[random] = shuffled[i];
                    shuffled[i] = cardR;
                }
            }
            return shuffled;
        }          
    }  
}
0
追加された