【问题标题】:Best way to store a BlackJack hand?存放二十一点手的最佳方式?
【发布时间】:2013-06-02 01:32:54
【问题描述】:

首先,我还是 C 的新手,所以请告诉我您可以提出的任何建议(尤其是关于处理数组的建议)。

我想在 C 中存储 BlackJack 手牌。我得出的结论是手牌或卡片必须是字符串,因为卡片可以是两个字符:A, J, Q, K 或数字:1, 2.. 10,其中 10实际上应该是一个由两个字符组成的字符串。

现在,我尝试将组成手的牌存储到这样的数组中:

char* hand;
hand[1] = "A";
hand[2] = "2";

问题是10,它占用了数组的两个索引而不是一个。我可以解决这个问题的一种方法是创建一个包含 5 个字符串(21 点手牌中的最大牌数)的结构,每张牌一个。但是,如果我出于某种原因想要拥有数千张牌怎么办?那么存放手的最佳方式是什么?

【问题讨论】:

  • 你可以用"T"来表示10。对于一手牌,你可以用一个数字来表示,而不是一个字母串。
  • 我想我没有足够清楚地表达我的意图,但我正试图将我的手打印到一个嵌入式系统项目的 LCD 上。我的 LCD 屏幕非常有限,所以最好打印出 J、Q 或 K,而不是 11、12、13。
  • 打印和存储是不同的问题。显然它们不是独立的,但是在选择存储格式时,您需要说明您的要求和约束。您可以将手存储为您希望在屏幕上显示的图像,但在应用程序的其他部分可能不容易阅读和解释。
  • 了解 C 的第一件事:永远不要将字符串用于任何事情,除非你真的必须这样做。

标签: c playing-cards blackjack


【解决方案1】:

您可能会觉得这个网站很有趣。

http://www.computerpokercompetition.org/

他们每年举办一次 AI 扑克比赛。他们的服务器是用 C 语言编写的,你可以从上面的站点下载代码。

基本上,它们将卡片存储为整数。这是处理卡片最有效的方法。牌组中只有 52 种卡片。如果有小丑在场,那就更多了。因此,您可以将其映射为 0 到 51 之间的整数值。他们使用以下函数来打印卡是什么,因为整数卡号不会告诉您太多信息。请注意,他们根据等级和套件构建字符串。

int printCard( const uint8_t card, const int maxLen, char *string  )
{
  if( 3 > maxLen ) {
    return -1;
  }

  string[ 0 ] = rankChars[ rankOfCard( card ) ];
  string[ 1 ] = suitChars[ suitOfCard( card ) ];
  string[ 2 ] = 0;

  return 2;
}

【讨论】:

    【解决方案2】:

    不要将卡片存储为字符串(例如"9"),而是存储为字符(例如'9')。对于值10,您可以使用'T' 之类的替换字符。示例代码:

    char hand[MAX_HAND_LEN];
    int hand_len;
    
    get_hand(hand, hand_len);
    
    for (int i = 0; i < hand_len; i++) { 
        if (hand[i] == 'T') {
            putchar('1');
            putchar('0');
        } else {
            putchar(hand[i]);
        }
        putchar(' ');
    }
    putchar('\n');
    

    这样您既不会浪费不必要的内存(因为一张卡现在只需要一个字节的存储空间),也不会牺牲代码的简单性或可读性。

    【讨论】:

    • ITYM putchar()putc() 还需要一个流参数。
    • 是的,我在脑海中将两者混为一谈。固定。
    • 比字符串好,但仍然比简单的整数差。
    • @LeeDanielCrocker 在 C 中,字符是整数。最小的那种。
    • 是的,但是如果您使用“T”、“J”等字符,那么您必须使用类似开关的东西来获取要添加到总数的值。我的意思是有一个可以直接计算的顺序的整数表示。不,我不是在谈论将手牌压缩成单个值——我说的是使用整数的数组作为手牌。
    【解决方案3】:

    我写了一篇关于 here 的文章。使用字符串是一个非常糟糕的主意。整数更好,最好的使用顺序是把花色放在低位,即使用顺序 2c, 2d, 2h, 2s, 3c, 3d, ... Ks, Ac, Ad, Ah, As .这样,您甚至不必分开军衔和西装来进行数学计算。那么,手只是整数数组。使用这种表示,我可以在几分钟内运行 十亿 手牌。 my library 中计算二十一点手牌总数的函数如下所示(OJ_CARD 宏扩展为整数常量,因此比较快):

    int ojb_total(const oj_cardlist_t *sp) {
        int i, c, t = 0, ace = 0, soft = 0;
    
        for (i = 0; i < sp->length; ++i) {
            c = sp->cards[i];
            if (c >= OJ_CARD(OJR_ACE, OJS_CLUB)) {
                ace = 1;
                ++t;
            } else if (c >= OJ_CARD(OJR_TEN, OJS_CLUB)) {
                t += 10;
            } else {
                t += OJ_RANK(c) + 2;
            }
        }
        if (ace && t < 12) {
            t += 10;
            soft = 1;
        }
        return soft ? -t : t;
    }
    

    这是来自通用纸牌模拟库,而且速度非常快,但如果我真的想要从没有其他任何作用的 21 点模拟中获得滚球到墙的速度,我根本不会代表纸牌,而只是拥有 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10} 的多个副本的“套牌”,然后从中进行交易。

    【讨论】:

    • 您的答案在技术上是最好的,因为在处理大容量时,将卡存储在尽可能少的位中显然会给您带来最佳性能,所以 +1。也就是说,我觉得这个答案对于 “对 C 还很陌生”... 的人来说可能有点矫枉过正
    • 使用整个库可能有点矫枉过正,但整数表示是最简单的。如果你想学习 C,首先你应该学习的是永远不要使用字符串,除非你真的必须这样做。
    【解决方案4】:

    将卡片存储为整数:

    • 1 = 王牌
    • 2 = 2
    • 3 = 3
    • ...
    • 9 = 9
    • 10 = 10
    • 11 = 杰克
    • 12 = 女王
    • 13 = 国王

    出于显示目的,使用 translate 函数将整数转换为其名称:

    string GetCardNameFromNumber(int cardNumber)
    {
        switch(cardNumber)
        {
            case 1:
                return "A";
            case 11:
                return "J";
            case 12:
                return "Q";
            case 13:
                return "K";
            default:
                return cardNumber.ToString();
        }
    }
    

    【讨论】:

    • 我喜欢@Amali 使用字母“T”或“0”来表示 10 的想法。我认为最好在内部将卡片值表示为整数并将它们转换为字符以进行输出。
    • 这是标记为 C,而不是 Java :-) 没有字符串,没有类。
    • 为了提高可读性,您可以声明一个枚举来为内部整数命名
    • 整数顺序选择错误。需要除以 13 才能获得排名。请参阅my essay 关于该主题。
    • 实际上,我刚刚注意到您不是在处理西装,因此订购确实很好。但是,返回字符的函数比需要的要复杂:为什么不直接使用 "XA23456789TJQK"[rank]
    【解决方案5】:

    我想说没有单一的最佳方法。但是,char *hand; 没有定义字符串数组;您可以使用char *hand[5],并且 10 不会占用两个索引;或者您可以使用char hand[5],并将 10 存储为单个字符,例如。 G。 “0”或“T”。

    【讨论】:

      猜你喜欢
      • 2017-04-05
      • 2021-07-26
      • 1970-01-01
      • 1970-01-01
      • 2011-01-25
      • 1970-01-01
      • 1970-01-01
      • 2017-04-22
      • 1970-01-01
      相关资源
      最近更新 更多