通常情况下,我用Java编写代码,但最近我一直试图为下学期的大学课程教自己C。(我即将进入第三年级)。我不会认为自己是最好的程序员,甚至是一个好的程序员。
现在,我只是想知道是否有人能快速查看我的尝试,在C制作一个基于终端的Blackjack游戏,并告诉我是否有任何明显的漏洞,我如何做到这一点。为此,我不习惯编写C或更大的过程代码项目,所以我确信我做了很多愚蠢的事情。我已经在下面包含了main.c文件,但其余部分都包含了这是GitHub链接。
#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;
}谢谢您的任何建议,您可能有!
发布于 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;时,这将导致未定义的行为。重要的是,我们在使用结果之前检查其结果,可能简单到:
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没有被分配,因此它的值没有初始化。除非它碰巧包含有效的响应,否则我们将无限期地循环,每次都无法读取输入。要解决这个问题,我们需要检查返回值(我将重新排序循环,因此我们只需要编写一次代码):
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);
}我们有
char* suite_to_string(int suite);如果我们查看使用它的位置,就不会需要写入返回的内存--我们只是将它用于打印。这意味着我们可以返回一个const char*,这反过来意味着我们不需要分配内存:我们只需要返回指向字符串文本的指针:
#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可以防止这种情况,这就是为什么我将错误处理放在了块之外)。
发布于 2020-01-17 22:08:58
get_random_card函数中的这一行有一个微妙的问题:
srand(time(NULL));您正在用当前时间播种随机数生成器,每次调用get_random_card,这意味着同一秒钟内的多个调用将具有相同的随机数序列。在这种特殊情况下,它不会影响任何东西(除了小的性能损失),因为do ... while (deck[random_index]->dealt == true);会循环到序列中的下一个数字为止。如果您更改了实现,或者在程序中的其他地方使用了随机数,您可能会遇到一些细微的错误,而这些错误并不像您认为的那样随机。
您应该在程序开始时调用srand(time(NULL))一次,然后将种子单独放在get_random_card中。
发布于 2020-01-17 17:39:44
“套房”和“西服”不一样。一套西装是红心,棍棒,黑桃和钻石。
void print_hand(Hand player_hand);会向我指出,这里只传递player_hand,但您也可以通过这个函数传递经销商的手。很好,您没有重复--但我建议将其更改为Hand hand,以表明它将运行对传递给它的任何有效Hand的检查。https://codereview.stackexchange.com/questions/235761
复制相似问题