C语言 控制执行流以实现更好的代码设计

u91tlkcl  于 2022-12-03  发布在  其他
关注(0)|答案(1)|浏览(103)

我不知道该如何解决这个问题,希望你们能帮助我。
我用C语言创建了一个名为truco的纸牌游戏(在终端窗口上运行的纯文本游戏)。这是一个三局两胜的游戏,直到一个队得到12分或更多,所以我们有三墩牌(我们称之为一轮),每墩牌值2分,游戏最多进行三轮,直到一个队得到12分。
当玩truco时,一个人可以“要求truco”来提高比赛的赌注(我称之为一场比赛的三墩三胜),另一个玩家可以接受、拒绝或再次提高赌注(本质上是接受,然后要求truco)。
我目前正在尝试实现这个功能,但我的代码可能不是最好的设计,因为我正在忍受添加“拒绝”选项,当一个球员拒绝truco,其他应该立即作为赢家(比赛).
我的问题来了,假设用户在第一个戏法上要求truco,而CPU拒绝了(现在游戏只是你对CPU),我如何“跳过”与其他戏法相关的所有其他代码,并将用户设置为这场比赛的赢家?
我不知道如何设计代码来允许这种功能,在正常情况下,两个或三个技巧应该发挥,但如果一些球员要求truco和其他拒绝游戏应该忽略其余的技巧和所有其他功能与之相关,去比赛结束,更新分数,并开始另一场比赛。
下面我创建了一个小的可复制代码来澄清事情(只是注意,我删除和修改了很多代码,以保持它的简单):

#include <stdio.h>
#include <stdbool.h>

// variable to store the result of the trick and also the final result of the match
int current_result = 0;

void ask_truco()
{
    printf("asking truco...\ntruco refused\n");
    // game should verify current_result and skip to reset_deck
    current_result = 1;
}

void play_trick()
{
    printf("playing trick\n");

    // simulating that match should be interrupted and finished
    ask_truco();

    // this should be skipped, but not in normal situations
    printf("do something...\n");
}

int check_winner()
{
    printf("checking winner\n");
    // hard-coded CPU wins
    return 2;
}

void reset_deck()
{
    printf("resetting deck\n");
}

int play_match(void)
{
    // asking for truco here, second and third tricks should not be played
    play_trick();

    while (true)
    {
        play_trick(); // while loop helps play second and third trick
        current_result = check_winner();

        // from ask_truco, I should go back here
        if (current_result == 1)
        {
            printf("increasing user score\n");
            break;
        }
        else if (current_result == 2)
        {
            printf("increasing cpu score\n");
            break;
        }
    }

    // resetting deck of cards
    reset_deck();

    return 0;
}

int main(void)
{
    play_match();
}

在实际的项目中,我有一些文件和函数,如果我只使用if和标志,我将结束与混乱,有没有更好的方法来做到这一点?我想知道如果事件驱动的设计将帮助我...

rt4zxlrg

rt4zxlrg1#

好的,在阅读了你们中的一些人提供给我的评论之后,特别是@Fe2O3,在对自己进行了一点自我训练之后,我实施了提供给我的方法,并设法解决了问题。我想借此机会解释一下我所做的事情。
首先,让我解释一下我的代码是什么,在我上面的问题中,我犯了一个错误(正如你们中的一些人所指出的),删除了 * 太多 * 我的代码,这阻止了你完全理解我的代码在做什么。
我将开始向您展示我的项目的三个文件,再次一些东西被删除或更改,但这一次我认为这是相当简洁,仍然可以理解。
1.我的类型定义

// types.h
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define TOTAL_CARDS_NUMBER 40
#define TOTAL_HAND_CARDS_NUMBER 3

enum suits
{
    spades = 1, // (♠)
    hearts,     // (♥)
    diamonds,   // (♦)
    clubs       // (♣)
};

enum rank
{
    four = 1,
    five,
    six,
    seven,
    queen,
    jack,
    king,
    ace,
    two,
    three
};

enum trumpcards
{
    facedown = 0,
    fourclubs = 14,
    sevenhearts = 13,
    acespades = 12,
    sevendiamonds = 11
};

typedef struct card
{
    enum suits suit;
    enum rank rank;
    bool available;
    int value;
} card;

typedef struct player
{
    card cards[TOTAL_HAND_CARDS_NUMBER];
    int *player_tentos;
} player;

typedef struct player_action
{
    int choice;
    bool ask_truco;
    bool hide_card;
} player_action;

enum round_result
{
    TIE,
    WIN,
    LOSE
};

typedef struct trick
{
    enum round_result result;
    bool is_tied_by_user;
} trick;

/*
prototypes
*/

void play_hand(card *cards, player *user, player *cpu);
void reset_deck(card *cards);
card ask_cpu_for_card(card *cpu_cards);
card ask_user_for_card(card *user_cards);

1.我的主文件

// truco.c
#include "./types.h"

int main(void)
{
    printf("Truco\n\n");

    card cards[TOTAL_CARDS_NUMBER];
    // prepare deck, generating the cards
    /* set_deck(cards); */

    player user, cpu;
    int user_tentos = 0;
    int cpu_tentos = 0;

    user.player_tentos = &user_tentos;
    cpu.player_tentos = &cpu_tentos;
    while (user_tentos < 12 && cpu_tentos < 12)
    {
        play_hand(cards, &user, &cpu);
    }

    /* show_final_victor(user_tentos, cpu_tentos); */

    return 0;
}

1.匹配相关文件
特鲁科是每场比赛三局两胜制,在比赛结束前有很多比赛要打

// match.c
#include "./types.h"

void play_hand(card *cards, player *user_ptr, player *cpu_ptr)
{
    int stake = 2;
    int *user_tentos = (*user_ptr).player_tentos;
    int *cpu_tentos = (*cpu_ptr).player_tentos;

    // distributing cards
    /* draw_cards(cards, user_ptr, cpu_ptr); */

    trick tricks[3];
    int trick = 0;
    enum round_result current_result = TIE;

    while (true)
    {
        tricks[trick] = play_trick(user_ptr, cpu_ptr);
        current_result = check_winner(tricks);

        if (current_result == WIN)
        {
            // user wins
            (*user_tentos) += stake;
            break;
        }
        else if (current_result == LOSE)
        {
            // cpu wins
            (*cpu_tentos) += stake;
            break;
        }
        else if (trick == 2)
        {
            // third trick in case of tie-tie-tie
            break;
        }

        trick++;
    }

    // cards should be made available again here
    reset_deck(cards);
}

trick play_trick(player *user_ptr, player *cpu_ptr)
{
    card *user_cards = (*user_ptr).cards;
    card *cpu_cards = (*cpu_ptr).cards;

    card user_card, cpu_card;
    trick trick;

    // ask players for card, possibily asking truco
    ask_cards_from_players(user_cards, cpu_cards,
                        &user_card, &cpu_card);

    // populate trick structure with result of current round
    /* set_trick_result(user_card, cpu_card, &trick); */

    // visual only - show user played cards and round result
    /* show_played_cards(user_card, cpu_card); */

    return trick;
}

void ask_cards_from_players(
    card *user_cards, card *cpu_cards,
    card *user_card, card *cpu_card)
{
    // ask each player for a card in correct order
    // I removed this part to keep it concise and simple

    // user can possibily ask truco
    *user_card = ask_user_for_card(user_cards);

    // cpu could possibily ask truco
    // but let's focus on the user for now
    *cpu_card = ask_cpu_for_card(cpu_cards);
}

card ask_user_for_card(card *user_cards)
{
    // show user cards
    /* show_player_cards(user_cards); */

    player_action action = {
        .choice = 0,
        .ask_truco = false};

    card card;

    // get user choice, including they called truco or not
    /* get_choice(&action); */

    // get card using choice
    /* card = get_card_from_hand(action.choice); */

    if (action.ask_truco)
    {
        // ask cpu if it accepts truco
        // if so, maybe the cpu wants to call truco (retruco)
        // if retruco, we should ask the user, if they accept truco
        // again the user may call retruco now, and so on
        // otherwise, the player who called truco should be considered the winner
        // if there is a winner in this way, we should set `current_result` accordingly
        // the tentos should be updated and the game should skip to line `43` on `play_hand` function
        // that is, the `reset_deck` function call

        // if truco is just accepted, but retruco is not called
        // the game should continue
        // and stake for this match should be increased
    }

    return card;
}

card ask_cpu_for_card(card *cpu_cards)
{
    // show cpu cards
    /* show_player_cards(cpu_cards); */

    card card;
    // get a random card for cpu
    /* card = get_random_card(cpu_cards); */

    return card;
}

就是这样,这里我们有大部分与问题相关的代码,还有一些额外的东西,我保留了这些东西来维护上下文。
正如你在上面看到的,my是在truco被拒绝的情况下,假设cpu拒绝truco,我如何从ask_user_for_card返回到play_hand函数的while循环中更新user_tentos,并立即转到第43行和reset_deck(),完成匹配?
当然,我可以使用很多if语句,可能更改一些函数的返回类型,甚至创建一些新的结构 *,但我认为您使代码有点混乱,请记住,这仍然是一个简化的示例。

**说点切线,我想知道创建结构的唯一目的是在函数中返回多个内容,即使这些属性可能并不相关,这是否被认为是一种糟糕的做法... *

为了解决这个问题,@Fe2O3给我的想法是一个关键因素,为什么不使用枚举来分隔游戏的每个状态,并使用一个循环和一个开关来从一个阶段转换到另一个阶段呢?
为了更好地组织事情,我决定创建另一个文件state-manager.c连同它的.h夫妇。这个文件负责管理比赛的状态,它将存储和更新比赛的状态。
第一个
在我的match.c中,几乎所有的东西都被改变了

// match.c new version
void play_hand(card *cards, player *user_ptr, player *cpu_ptr)
{
  int stake = 2;

  state match_state = get_state();
  trick tricks[3];
  int trick = 0;
  enum round_result current_result = TIE;
  card user_card, cpu_card;

  while (match_state.current_state != END_OF_MATCH)
  {
    switch (match_state.current_state)
    {
    case IDLE:
      printf("IDLE\n");
      // checks and populate the next state
      match_state = idle(match_state, trick);
      update_state(match_state.next_state);
      break;

    case CHECK_USER_TURN:
      printf("CHECK_USER_TURN\n");
      is_user_turn = check_user_turn(tricks[trick - 1]);
      update_state(SET_ASK_PLAYER_CARD);
      break;

    case SET_ASK_PLAYER_CARD:
      printf("SET_ASK_PLAYER_CARD\n");
      update_state((is_user_turn) ? ASK_USER_CARD : ASK_CPU_CARD);
      break;

    case ASK_USER_CARD:
      printf("ASK_USER_CARD\n");
      // now I can just return out of the function it truco is refused
      match_state = ask_user_card(match_state,
                                  user_cards,
                                  &user_card,
                                  &current_result,
                                  &stake);

      // and update the state accordingly
      update_state(match_state.next_state);
      break;

    case ASK_CPU_CARD:
      printf("ASK_CPU_CARD\n");
      // do more stuff...
      break;

    case SET_TRICK_RESULT:
      printf("SET_TRICK_RESULT\n");
      // do more stuff...
      break;

    case SHOW_PLAYED_CARDS:
      printf("SHOW_PLAYED_CARDS\n");
      // do more stuff...
      break;

    case CHECK_WINNER:
      printf("CHECK_WINNER\n");
      // do more stuff...
      break;

    case UPDATE_WINNER_TENTOS:
      printf("UPDATE_WINNER_TENTOS\n");
      // whatever it does, I can go here anytime I want and break out of the loop
      break;

    default:
      printf("default\n");
      update_state(END_OF_MATCH);
      break;
    }

    match_state = get_state();
  }

  reset_state();

  // cards should be made available again here
  reset_deck(cards);
}

当然,我只是展示了相关的东西,但我认为还是可以看到变化的,现在我有一个循环,用switch语句从游戏的每个状态改变到另一个状态,我控制从我在哪里到我将去哪里.
我唯一担心的是,这种方法虽然可以说是作品伟大更复杂的阅读理解和维护,说实在的我很高兴我设法做到了这一点;任何其他建议或意见都将受到欢迎。
谢谢你,谢谢你

相关问题