C# BlackJack - 发给玩家的同一张牌 & cpu?

C# BlackJack - same card dealt to player & cpu?

我无缘无故地在做一个小黑杰克游戏,我 运行 遇到了一个问题,我不知道我哪里出错了,我唯一能做的就是可以想象的是 'new card' 方法被调用了两次,太快了...

问题是它给两个玩家 同一张牌 :/

这是我的代码

谢谢! :)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace BlackJack_Reworked
{
    public partial class BlackJack : Form
    {
        public BlackJack()
        {
            InitializeComponent();
        }
        class myVars
        {
            public static int cardsDrawn = 0;

            public static int playerX = 230;
            public static int playerY = 160;

            public static int cpuX = 230;
            public static int cpuY = 60;

            public static int playerCardValue = 0;
            public static int cpuCardValue = 0;
        }
        private PictureBox[] card = new PictureBox[100];
        private void makeCard(string pickedCard, int x)
        {
            card[myVars.cardsDrawn] = new PictureBox();

            if (x == 0)
            {
                card[myVars.cardsDrawn].Location = new Point(myVars.playerX, myVars.playerY);
                myVars.playerX += 15;
            }
            if (x == 1)
            {
                card[myVars.cardsDrawn].Location = new Point(myVars.cpuX, myVars.cpuY);
                myVars.cpuX += 15;
            }

            card[myVars.cardsDrawn].Image = (Image)Properties.Resources.ResourceManager.GetObject(pickedCard);
            card[myVars.cardsDrawn].Size = new Size(72, 96);
            card[myVars.cardsDrawn].Parent = this;
            card[myVars.cardsDrawn].BringToFront();
            card[myVars.cardsDrawn].Update();

            myVars.cardsDrawn++;

            checkScores(false);
        }
        private void newCard(int x)
        {
            Random cardPicker = new Random();

            int cardChoice = cardPicker.Next(1, 13);
            int houseChoice = cardPicker.Next(1, 4);

            string house = null;

            switch (houseChoice)
            {
                case 1:
                    house = "Hearts";
                    break;
                case 2:
                    house = "Diamonds";
                    break;
                case 3:
                    house = "Spades";
                    break;
                case 4:
                    house = "Clubs";
                    break;
            }

            if (x == 0)
            {
                makeCard(house + Convert.ToString(cardChoice), 0);
                myVars.playerCardValue += cardChoice;
            }
            if (x == 1)
            {
                makeCard(house + Convert.ToString(cardChoice), 1);
                myVars.cpuCardValue += cardChoice;
            }
        }
        private bool feelingLucky()
        {
            Random Dice = new Random();
            if (myVars.cpuCardValue >= 20) { return false; }
            if (myVars.cpuCardValue <= 16) { return true; }
            if (myVars.cpuCardValue >= 17 && myVars.cpuCardValue <= 18) if (Dice.Next(1, 5) == 1) { return true; }
            if (myVars.cpuCardValue == 19) if (Dice.Next(1, 10) == 1) { return true; }
            return false;
        }
        private void updateHandValues()
        {
            lblPlayerHand.Text = "Player: " + myVars.playerCardValue.ToString();
            lblCPUhand.Text = "CPU: " + myVars.cpuCardValue.ToString();
        }
        private void checkScores(bool stand)
        {
            if (stand == true)
            {
                if (myVars.playerCardValue <= 21 && myVars.playerCardValue > myVars.cpuCardValue)
                {
                    MessageBox.Show("Win!");
                    btnNewGame.Visible = true;
                }
                else if (myVars.cpuCardValue <= 21 && myVars.cpuCardValue > myVars.playerCardValue)
                {
                    btnNewGame.Visible = true;
                    MessageBox.Show("Lose!");
                }
           }
            else
            {
                if (myVars.playerCardValue > 21)
                {
                    MessageBox.Show("Bust!");
                    btnNewGame.Visible = true;
                }
                if (myVars.cpuCardValue > 21)
                {
                    MessageBox.Show("Win!");
                    btnNewGame.Visible = true;
                }
            }
        }
        private void newGame()
        {
            for(int x = 0; x < myVars.cardsDrawn; x++) { card[x].Dispose(); }

            myVars.cpuCardValue = 0;
            myVars.playerCardValue = 0;
            myVars.cpuX = 230;
            myVars.playerX = 230;

            btnNewGame.Visible = false;

            newCard(0); newCard(1);
        }
        private void btnNewGame_Click(object sender, EventArgs e)
        {
            newGame();
        }
        private void btnHit_Click(object sender, EventArgs e)
        {
            newCard(0); newCard(1);
            updateHandValues();
        }
        private void btnStand_Click(object sender, EventArgs e)
        {
            if (feelingLucky() == true) newCard(1);
            else checkScores(true);
        }
    }
}

编辑 这是我的新版本和工作版本的代码,在下面这些好人的帮助下,以防万一有人发现它有用,谢谢大家!

要使此代码正常工作,您需要将这些卡片图片文件添加到项目资源中。

我知道我的逻辑可能不太好,但我觉得我已经从这个小项目中学到了东西,希望其他人也能,现在,是时候想出一些新的东西了。感谢 Whosebug。

Playing Card Pictures Download

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace BlackJack_Reworked
{
    public partial class BlackJack : Form
    {
        public BlackJack()
        {
            InitializeComponent();
        }
        private PictureBox[] Card = new PictureBox[52];
        static List<string> Deck = new List<string>();
        class myVars
        {
            public static int playerX = 230;
            public static int cpuX = 230;
            public static int playerCardValue = 0;
            public static int cpuCardValue = 0;
            public static int cardsDrawn = 0;
        }
        private void newDeck()
        {
            Deck.Clear();

            for (int x = 0; x < myVars.cardsDrawn; x++)
            {
                Card[x].Dispose();
            }

            for (int x = 0; x < 52; x++)
            {
                int cardSuite = (x / 13) + 1;
                int faceValue = (x % 13) + 1;

                string Suite = null;

                switch (cardSuite)
                {
                    case 1:
                        Suite = "Hearts";
                        break;
                    case 2:
                        Suite = "Diamonds";
                        break;
                    case 3:
                        Suite = "Spades";
                        break;
                    case 4:
                        Suite = "Clubs";
                        break;
                }
                Deck.Add(Suite + Convert.ToString(faceValue));
            }
            Extensions.Shuffle(Deck);

            myVars.cardsDrawn = myVars.cpuCardValue = myVars.playerCardValue = 0;
            myVars.cpuX = myVars.playerX = 230;
        }
        private void handCard(string recipient)
        {
            Random Random = new Random(); Extensions.Shuffle(Deck);

            string pickedCard = Deck[Random.Next(Deck.Count)];
            int cardvalue = Convert.ToInt32(Regex.Replace(pickedCard, "[^0-9]", ""));

            Card[myVars.cardsDrawn] = new PictureBox();

            if (recipient == "player") {
                Card[myVars.cardsDrawn].Location = new Point(myVars.playerX, 160); myVars.playerX += 15;
                myVars.playerCardValue += cardvalue;
            }
            if (recipient == "cpu") {
                Card[myVars.cardsDrawn].Location = new Point(myVars.cpuX, 60); myVars.cpuX += 15;
                myVars.cpuCardValue += cardvalue;
            }

            Card[myVars.cardsDrawn].Image = (Image)Properties.Resources.ResourceManager.GetObject(pickedCard);
            Card[myVars.cardsDrawn].Size = new Size(72, 96);
            Card[myVars.cardsDrawn].Parent = this;
            Card[myVars.cardsDrawn].BringToFront();
            Card[myVars.cardsDrawn].Update();

            Deck.Remove(pickedCard); myVars.cardsDrawn++; updateHandValues();
        }
        private void updateHandValues()
        {
            lblPlayerHand.Text = "Player: " + myVars.playerCardValue.ToString();
            lblCPUhand.Text = "CPU: " + myVars.cpuCardValue.ToString();
        }
        private void newGame()
        {
            lblBlackJack.Text = "♠ Blackjack ♥";
            btnNewGame.Visible = false;
            newDeck(); handCard("player"); handCard("cpu");
        }
        private void checkCards()
        {
            if (playerWins() == true)
            {
                lblBlackJack.Text = "♠ You Win! ♥";
                btnNewGame.Visible = true;
            }
            else if (playerWins() == false)
            {
                lblBlackJack.Text = "♠ Dealer Wins! ♥";
                btnNewGame.Visible = true;
            }
        }
        private void tieBreak()
        {
            if (myVars.cpuCardValue == myVars.playerCardValue && myVars.cpuCardValue >= 17)
            {
                lblBlackJack.Text = "♠ Tie! ♥";
                btnNewGame.Visible = true;
            }
            else { checkCards(); }
        }
        private bool? playerWins()
        {
            if(myVars.cpuCardValue == 21 || myVars.playerCardValue > 21) { return false; }
            if(myVars.playerCardValue == 21 || myVars.cpuCardValue > 21) { return true; }
            else { return null; }
        }
        private bool cpuShouldPlay(bool stand)
        {
            Random Dice = new Random();

            if (stand == false)
            {
                if (myVars.cpuCardValue <= 15) { return true; }
                if (myVars.cpuCardValue >= 20 && myVars.cpuCardValue <= 21 && myVars.cpuCardValue > myVars.playerCardValue) { return false; }
                if (myVars.cpuCardValue == 19 && myVars.cpuCardValue < myVars.playerCardValue) { return true; } else { return false; }
            }
            else
            {
                if(myVars.cpuCardValue < myVars.playerCardValue)
                {
                    return true;
                }
                else { return false; }
            }
        }
        private void btnNewGame_Click(object sender, EventArgs e)
        {
            newGame();
        }
        private void btnHit_Click(object sender, EventArgs e)
        {
            handCard("player"); if(cpuShouldPlay(false) == true) handCard("cpu"); checkCards();
        }
        private void btnStand_Click(object sender, EventArgs e)
        {
            if (cpuShouldPlay(true) == true) handCard("cpu"); tieBreak();
        }
    }
    public static class Extensions
    {
        public static void Shuffle<T>(this IList<T> list)
        {
            Random rng = new Random();
            int n = list.Count;
            while (n > 1)
            {
                n--;
                int k = rng.Next(n + 1);
                T value = list[k];
                list[k] = list[n];
                list[n] = value;
            }
        }
    }
}

您正在新卡片方法中声明一个新的 Random()。

如果该方法被调用两次,而且速度非常快,自动生成的种子(我相信是基于时间)将非常接近,以至于它们将生成几乎相同的数字。

最好的办法是在方法之外创建 random 的实例,并每次都传递 random,这样每次调用都不会得到相同的数字。

Random.Next 方法根据当前时间戳和先前生成的数字(称为 'seed')生成随机数。当您每次创建 Random 的新对象时,它都会使用相同的种子进行初始化。在您的情况下,时间戳和种子不会更改为随机的 'Next' 方法。

如果您对所有操作使用单个 Random 对象,则每次调用 'next' 时种子都会改变。

其次:您需要跟踪已经抽到的牌,所以我创建了一个牌鞋清单。我会删除已经使用过的卡,就像真正的一样table。

注意:创建一个新鞋,假设您的卡片数量为 20。

 List<string> aShoe = new List<string>(); //shoe contains 4 to 8 decks. Depending upon blackjack version.
 int numberOfDeckPerShoe = 4;

private void CreateNewDeck()
 {
     for(int i =0;i <numberOfDeckPerShoe;i++)
 for(int j=0;j<52;j++)
 {
            int cardFace = (j%13)+1;
        int cardSuite = (j/13) + 1;

        string Suite = null;

        switch (cardSuite)
        {
        case 1:
            Suite = "Hearts";
            break;
        case 2:
            Suite = "Diamonds";
            break;
        case 3:
            Suite = "Spades";
            break;
        case 4:
            Suite = "Clubs";
            break;
        }
        aShoe.add(Suite + Convert.ToString(cardFace));
 }

 }

 Random cardPicker = new Random(); //This is change

 private void newCard(int x)
    {
        int cardChoice = 0;
        int houseChoice = 0;
    string cardDrawn = "";

    int cardToDraow = cardPicker.Next(0,aShoe.length);

    cardDrawn = card aShoe[cardToDraow];
    card.removeAt(cardToDraow);

        if (x == 0)
        {
            makeCard(cardDrawn, 0);
            myVars.playerCardValue += cardChoice;
        }
        if (x == 1)
        {
            makeCard(cardDrawn, 1);
            myVars.cpuCardValue += cardChoice;
        }
    }

如果您按照单副牌规则进行游戏,则必须使用 'bag' 或 'no replacement' 随机值源模型。最初,用所有 52 张可能的牌填满袋子。然后每次迭代,从包中剩余的卡片中随机挑选一张卡片,将其从包中取出。当袋子空了,再装满。

请注意,当从有 n 个剩余物品的袋子中随机抽取一张卡片时,您的随机值,即要拿走的袋子中物品的索引,必须不大于 n-1(假设索引 运行 从 0 到 n-1).

但是,有些二十一点模型是将多副牌一起洗牌;许多赌场都是这样玩的。来自维基百科 Blackjack, rules of play at casinos:

At a casino blackjack table, the dealer faces five to seven playing positions from behind a semicircular table. Between one and eight standard 52-card decks are shuffled together.

在上述模型中,同一张牌连续发两次当然是可能的;但是,根据一起洗牌的套牌数量,这种情况只会发生有限的次数。

如果我对你的代码的理解正确的话,你目前正在为一副 21 点鞋建模,其中有无限多副牌一起洗牌。