首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >终端式Blackjack

终端式Blackjack
EN

Code Review用户
提问于 2020-01-17 05:40:31
回答 3查看 537关注 0票数 4

通常情况下,我用Java编写代码,但最近我一直试图为下学期的大学课程教自己C。(我即将进入第三年级)。我不会认为自己是最好的程序员,甚至是一个好的程序员。

现在,我只是想知道是否有人能快速查看我的尝试,在C制作一个基于终端的Blackjack游戏,并告诉我是否有任何明显的漏洞,我如何做到这一点。为此,我不习惯编写C或更大的过程代码项目,所以我确信我做了很多愚蠢的事情。我已经在下面包含了main.c文件,但其余部分都包含了这是GitHub链接。

代码语言:javascript
复制
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "data.h"
#include "deck.h"

#define MAX_CARDS 25

Hand deal_player_hand(Deck deck);
Card* get_random_card(Deck deck);
Hand deal_dealer_hand(Deck deck);
void print_card(Card* card);
void print_hand(Hand player_hand);
char* suite_to_string(int suite);
void hit(Hand player_hand, Deck deck);
char get_user_response();
bool check_valid(char input);
Card* deal_card(Hand player_hand, Deck deck);
bool check_bust(Hand hand);
void dealer_hit(Hand dealer_hand, Deck deck);
int sum_hand(Hand hand);
void check_winner(Hand player_hand, Hand dealer_hand);


int main() {

    Deck deck = create_deck();
    Hand player_hand = deal_player_hand(deck);
    Hand dealer_hand = deal_dealer_hand(deck);

    printf("\nPlayer Hand:\n");
    print_hand(player_hand);
    printf("\nDealer Hand:\n");
    print_hand(dealer_hand);

    hit(player_hand, deck);
    dealer_hit(dealer_hand, deck);
    check_winner(player_hand, dealer_hand);

    return 0;
}

void dealer_hit(Hand dealer_hand, Deck deck) {

    int sum;

    do {
        printf("\nThe dealer deals themself a card\n");
        printf("Dealer Hand:\n");
        deal_card(dealer_hand, deck);
        print_hand(dealer_hand);
        sum = sum_hand(dealer_hand);
    } while(sum < 16);

    if (sum <= 21) {
        printf("\nThe dealer's hand is above 16, they must sit.");
    } else if (sum > 21) {
        printf("\nThe dealer has gone bust!\n");
    }
}

Hand deal_player_hand(Deck deck) {

    Hand player_hand = malloc(sizeof(Card*) * MAX_CARDS); // not possible to draw more than 25 cards

    for (int i = 0; i < MAX_CARDS; i++) {
        player_hand[i] = NULL;
    }

    player_hand[0] = get_random_card(deck);
    player_hand[1] = get_random_card(deck);

    return player_hand;
}

Hand deal_dealer_hand(Deck deck) {

    Hand dealer_hand = malloc(sizeof(Card*) * MAX_CARDS); // not possible to draw more than 25 cards

    for (int i = 0; i < MAX_CARDS; i++) {
        dealer_hand[i] = NULL;
    }

    dealer_hand[0] = get_random_card(deck);

    return dealer_hand;
}

Card* get_random_card(Deck deck) {

    srand(time(NULL));

    int random_index;

    do {
        random_index = rand() % DECK_SIZE;
    } while (deck[random_index]->dealt == true);

    Card* random_card = deck[random_index];
    random_card->dealt = true;
    return random_card;
}

// deals a random card to the next available slot in player or dealer's hand
Card* deal_card(Hand hand, Deck deck) {

    Card* card = NULL;

    for (int i = 0; i < MAX_CARDS; i++) {
        if (hand[i] == NULL) {
            card = get_random_card(deck);
            hand[i] = card;
            break;
        }
    }

    return card;
}

void hit(Hand player_hand, Deck deck) {

    bool stop_hitting = false;

    while (!stop_hitting && !check_bust(player_hand)) {
        printf("\nHit? (y/n)\n");
        char input = get_user_response();
        if (input == 'n') {
            stop_hitting = true;
        } else {
            Card* card = deal_card(player_hand, deck);
            print_card(card);
        }
    }

    if (check_bust(player_hand)) {
        printf("You've gone bust\n");
        printf("Game Over\n");
        exit(0);
    }

    printf("Player Hand:\n");
    print_hand(player_hand);
}

void check_winner(Hand player_hand, Hand dealer_hand) {

    int player_sum = sum_hand(player_hand);
    int dealer_sum = sum_hand(dealer_hand);

    printf("\nSum of Player's hand: %d\n", player_sum);
    printf("Sum of Dealer's hand: %d\n", dealer_sum);

    if (player_sum == dealer_sum) {
        printf("It's a draw!\n");
    } else if (player_sum < dealer_sum) {
        printf("The house wins!\n");
    } else if (player_sum > dealer_sum) {
        printf("You win!\n");
    }
}

//###########################
// GRAPHICS FUNCTIONS
//###########################

void print_card(Card* card) {
    char* suite = suite_to_string(card->suite);
    printf("%s of %s\n", card->name, suite);
}

void print_hand(Hand player_hand) {

    int value = 0;

    for (int i = 0; i < MAX_CARDS; i++) {
        Card* card = player_hand[i];

        if (card == NULL) {
            break;
        }

        value += card->value;
        print_card(card);
    }
    printf("Sum: %d\n", value);
}

//###########################
// UTILITY FUNCTIONS
//###########################
char* suite_to_string(int suite) {

    char *suite_name = malloc(sizeof(char) * 10);

    switch (suite) {
        case DIAMONDS:
            strcpy(suite_name, "Diamonds");
            break;
        case CLUBS:
            strcpy(suite_name, "Clubs");
            break;
        case SPADES:
            strcpy(suite_name, "Spades");
            break;
        case HEARTS:
            strcpy(suite_name, "Hearts");
            break;
        default:
            strcpy(suite_name, "ERROR");
    }

    return suite_name;
}

bool check_valid(char input) {

    bool valid = false;

    if (input == 'y' || input == 'n') {
        valid = true;
    }

    return valid;
}

// determines if this hand has gone over 21.
bool check_bust(Hand hand) {

    bool bust = false;
    int sum = sum_hand(hand);

    if (sum > 21) {
        bust = true;
    }

    return bust;
}

int sum_hand(Hand hand) {

    int sum = 0;

    for (int i = 0; i < MAX_CARDS; i++) {
        if (hand[i] != NULL) {
            sum += hand[i]->value;
        }
    }

    return sum;
}

//###########################
// File IO Functions
//###########################

// gets first char entered by user on command line
char get_user_response() {

    char input;
    scanf(" %c", &input);
    while(!check_valid(input)) {
        printf("Invalid input. Please enter y/n\n");
        scanf(" %c", &input);
    }

    return input;
}

谢谢您的任何建议,您可能有!

EN

回答 3

Code Review用户

发布于 2020-01-17 08:44:44

很好的第一次努力。来自Java,您会发现C中的内存管理是新的和令人沮丧的:

内存管理

您不能只调用malloc并直接使用这样的结果:

Hand player_hand = malloc(sizeof(Card*) * MAX_CARDS);for (int i= 0;i< MAX_CARDS;i++) { player_hand我 = NULL;}

如果malloc()失败,它将返回一个空指针,当我们到达player_hand[i] = NULL;时,这将导致未定义的行为。重要的是,我们在使用结果之前检查其结果,可能简单到:

代码语言:javascript
复制
Hand player_hand = malloc(sizeof(Card*) * MAX_CARDS);
if (!player_hand)
    fputs("Memory allocation error!\n", stderr);
    exit(EXIT_FAILURE);
}

对于Java程序员来说,另一件新的事情是需要free()我们分配的内存。对于这样的小程序,我们不用释放内存,因为我们的操作系统会在程序退出时回收内存,但我们希望为运行更长时间的程序开发良好的实践(例如,我们可能希望开发一个围绕这段代码构建的游戏服务器)。

有一条简单的规则:每一次分配都必须与程序中某个地方的取消分配配对。C编程的很大一部分涉及管理分配,并确保在发布之前每个分配都有明确的所有权。

错误检查

除了malloc()之外,还有其他函数的返回值表示错误,因此必须进行检查。scanf()就是一个这样的例子,如下所示:

char输入;scanf(“%c”& input );while(!check_valid(input)) {printf(“无效输入。请输入y/n\n");

scanf()返回执行的转换数,如果I/O失败,则返回EOF。如果失败了会发生什么呢?(我们可以通过关闭输入流来强制失败。) input没有被分配,因此它的值没有初始化。除非它碰巧包含有效的响应,否则我们将无限期地循环,每次都无法读取输入。要解决这个问题,我们需要检查返回值(我将重新排序循环,因此我们只需要编写一次代码):

代码语言:javascript
复制
char get_user_response(void)
{
    char input;
    while (scanf(" %c", &input) == 1) {
        if (check_valid(input)) {
            return input;
        }
        printf("Invalid input. Please enter y or n\n");
    }
    fputs("Input error!\n", stderr);
    exit(EXIT_FAILURE);
}

选择哪些字符串是可写

我们有

代码语言:javascript
复制
char* suite_to_string(int suite);

如果我们查看使用它的位置,就不会需要写入返回的内存--我们只是将它用于打印。这意味着我们可以返回一个const char*,这反过来意味着我们不需要分配内存:我们只需要返回指向字符串文本的指针:

代码语言:javascript
复制
#include <assert.h>
#define NOTREACHED(message) 0

const char *suite_to_string(int suite)
{
    switch (suite) {
    case DIAMONDS: return "Diamonds";
    case CLUBS: return "Clubs";
    case SPADES: return "Spades";
    case HEARTS: return "Hearts";
    }
    assert(NOTREACHED("Invalid suit name"));
    return "ERROR";
}

如果我们要为这个程序声明一个enum,那么一个好的编译器将检查我们是否包含了switch中的所有值(使用default可以防止这种情况,这就是为什么我将错误处理放在了块之外)。

票数 6
EN

Code Review用户

发布于 2020-01-17 22:08:58

get_random_card函数中的这一行有一个微妙的问题:

代码语言:javascript
复制
srand(time(NULL));

您正在用当前时间播种随机数生成器,每次调用get_random_card,这意味着同一秒钟内的多个调用将具有相同的随机数序列。在这种特殊情况下,它不会影响任何东西(除了小的性能损失),因为do ... while (deck[random_index]->dealt == true);会循环到序列中的下一个数字为止。如果您更改了实现,或者在程序中的其他地方使用了随机数,您可能会遇到一些细微的错误,而这些错误并不像您认为的那样随机。

您应该在程序开始时调用srand(time(NULL))一次,然后将种子单独放在get_random_card中。

票数 2
EN

Code Review用户

发布于 2020-01-17 17:39:44

拼写

“套房”和“西服”不一样。一套西装是红心,棍棒,黑桃和钻石。

逻辑检查

  • 为什么MAXCARDS 25?在Blackjack中,玩家允许的牌数最多为5张。即便如此,目标是21张;如果是一副标准牌,那将是4张2张牌(8张)、3张3张牌(9张)和4张Aces牌(4张)。如果你计划玩超过1牌,那么你可以根据需要调整数字,但游戏应该停止向一名玩家一次5张牌。
  • 我看不出处理卡片的逻辑和它们的价值。我想这是在你的Github链接中(对不起,我没有遵循它)。确保Aces被视为11和1 -当我进行21点时,我将它们作为11对待,直到玩家崩溃,然后转换为1的值,并重新运行检查。

码样式

  • void print_hand(Hand player_hand);会向我指出,这里只传递player_hand,但您也可以通过这个函数传递经销商的手。很好,您没有重复--但我建议将其更改为Hand hand,以表明它将运行对传递给它的任何有效Hand的检查。
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/235761

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档