Простая пошаговая игра CMD, сделанная на C

Я изучал c последние несколько недель, я сделал простую пошаговую игру, чтобы показать то, что я знаю. Могу ли я что-нибудь сделать, чтобы улучшить код или улучшить работу игры?

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <windows.h>
#include <time.h>
#include <stdbool.h>

int main(){
    srand(time(NULL));
    bool play = true;
 
    while(play){
        int choose = -2;
        int P_h = 25;
        int E_h = 30;
        int SP_u = 3;
        puts("Do you wanna play this game. [Press 0 to exit or press any other number to play]");
        scanf("%d", &choose);
 
        if(choose == 0){
            play = false;
            puts("Goodbye");
            break;
        }
        else{
            puts("Okay");
            Sleep(300);
 
            while(E_h > 0){
                int P_at = (rand()%8);
                int E_at = (rand()%10);
                int SP = 100;
                int at_p = -5;
 
                if(P_h <= 0){
                    puts("You have died");
                    break;
                }
             if(E_h <= 0){
                puts("you win!");
                break;
             }
 
                printf("You have %d uses left of the super attack n", SP_u);
                puts("Would you like to attack or do a super attack [Press 0 to do a super attack, Press 1 to attack]");
                scanf("%d", &at_p);
 
                if(at_p == 0){
                    if(SP_u <= 0){
                    puts("Sorry, you have 0 uses left, you cannot use this n");
                }
                else{
                    E_h -= SP;
                    SP_u--;
                    printf("nThe enemy took a damage of %d! It now has a health of %d!", SP, E_h);
 
                    P_h -= E_at;
                    printf("nThe enemy attacks! Your health went down by %d it is now %d n n", E_at, P_h);
                }
 
            }
            else if(at_p == 1){
                 E_h -= P_at;
                printf("nThe enemy took a damage of %d! It now has a health of %d!", P_at, E_h);
 
                P_h -= E_at;
                printf("nThe enemy attacks! Your health went down by %d it is now %d n n", E_at, P_h);
            }
            else{
                puts("Thats not an option, idiot n");
            }
 
        }
 
 
    }
    if(E_h <= 0){
            puts("You killed the enemy!");
            puts("Would you like to play again? [0 to exit, Press anything else to play again]");
            scanf("%d", &choose);
 
            if(choose == 0){
                play = false;
                break;
            }
            else{
                play = true;
            }
        }
        else if(P_h <= 0){
            puts("You died. Would you like to play again to try to defeat the enemy. [0 to exit, anything else to play again]");
            scanf("%d", &choose);
 
            if(choose == 0){
                play = false;
                break;
            }
            else{
                play = true;
            }
        }
    }
 
 
 
    return 0;
}

Я чувствую, что есть лучший способ запрограммировать решения, потому что сейчас они просто вложены в операторы if.

Я думаю

1 ответ
1

Форматирование и условные обозначения

Всегда запускайте свой код через автоформатор. В Linux я использую clang-format, а в Win10 я использую функции автоформатирования intellisense VSCode. Я не уверен в ваших точных настройках, но форматирование велико.

Затем имена переменных. Переменная choose чудесно информативен. Я сразу знаю, для чего это нужно. Переменная P_h с другой стороны, расплывчатый и непонятный. Не беспокойтесь о длине ваших имен переменных. Компилятору все равно. Вместо этого сосредоточьтесь на создании описательных имен переменных, например Player_health так что любой, кто читает ваш код, сразу его понимает.

Ход программы

Игнорируя всю неряшливую специфику, вот что я читаю вашу программу как:

while playing:
    setup variables.
    get player input
    if player doesn't want to play
        exit
    otherwise:
        main game loop
    if lose:
        ask to replay
    if win:
        ask to replay

Я намеренно пока игнорирую основной игровой цикл. Сразу же я вижу одну большую проблему: вы делаете одно и то же дважды внизу. Все это choose==0 вещь может быть перемещена за пределы if заявления, потому что в любом случае вы столкнетесь с этим. (В случае, если E_h>0 && P_h>0, вы не выйдете из основного игрового цикла, поэтому мы можем это игнорировать.)

Это выглядело бы так:

if (E_h <= 0) {
    puts("You killed the enemy!nWould you like to play again? [0 to exit, Press anything else to play again]");
}
else if (P_h <= 0) {
    puts("You died. Would you like to play again to try to defeat the enemy. [0 to exit, anything else to play again]");
}
scanf("%d", &choose);
if (choose == 0) {
    play = false;
    break;
}
else {
    play = true;
}

Даже здесь мы можем продолжить оптимизацию. Наблюдая за ходом программы, каждый раз, когда мы нажимаем scanf в этом коде мы можем утверждать, что play==true и поэтому мы можем удалить весь раздел, который его устанавливает. Кроме того, поскольку следующее, что мы делаем, это запускаем while (play) мы можем вырезать break, поскольку он выйдет из цикла естественным образом. Наконец, условные выражения, которые запускают только один оператор, не нуждаются в скобках. (Это зависит от вашего личного выбора и на самом деле не влияет на ваш код. Мне нравится убирать их оттуда.) Таким образом, мы можем сократить 25 строк кода до

if (E_h <= 0)
    puts("You killed the enemy!nWould you like to play again? [0 to exit, Press anything else to play again]");
else if (P_h <= 0)
    puts("You died. Would you like to play again to try to defeat the enemy. [0 to exit, anything else to play again]");
scanf("%d", &choose);
if (choose == 0)
    play = false;

всего семь.


Затем давайте посмотрим на ту часть кода, где вы изначально спрашиваете, хочет ли игрок играть. По логике вещей, если игрок запускает ваш код, вы можете предположить, что они это сделали. Весь раздел первоначального выбора может быть удален. (Если вы решите оставить его там, подумайте о том, что может случиться, если игрок решит переиграть игру. Прямо сейчас они должны сказать, что хотят (пере) сыграть в игру дважды!)

Основной игровой цикл

Хорошо, вот суть дела. Первое, что вы можете сделать, это переместить это маленькое число в конец:

if (P_h <= 0) {
    puts("You have died");
    break;
}
if (E_h <= 0) {
    puts("you win!");
    break;
}

и убираем второй разрыв. Это работает так же, но вам не нужно рассчитывать значения атаки для раунда, который вы не собираетесь использовать.

Что касается вашего фактического раздела «атаки», похоже, вы также можете значительно его уменьшить. У вас есть два варианта: либо использовать switch операторы или изменить ход программы. switch будет выглядеть так:

switch at_p
{
    case 0: 
        super_attack code;
    break;
    case 1: 
        regular_attack code;       
    break;
    default:
        idiot_code;
}

Что касается потока программы: обратите внимание, что в обеих атаках у вас очень похожий / идентичный код. Если вы правильно кодируете граничные условия, представьте, что вы могли бы сделать с P_at = SP. Вы можете значительно реорганизовать свой основной цикл.

Я лично выбрал бы оператор switch просто потому, что он более удобен для будущих обновлений.

Другие предложения

Не использовать stdbool.h. Намного проще работать с int. В целом, false сопоставляется с 0 и true сопоставляется со всем, что не является ложным. (Плюс, ints обычно имеют размер 4 байта и bools имеют размер 1 байт. У вас есть много места для использования.) Я никогда не использую bools, и если мне нужно их использовать, я объявляю union и создайте служебную переменную, которая содержит логические флаги.

Следите за ходом программы. Хороший принцип — иметь только одно место возврата. Будь то return, break, exit() или же continueсведение к минимуму количества способов, которыми программа может избежать определенного участка кода, упрощает отладку. Часто вы можете переместить элементы из начала циклов в конец, отрицать условное выражение внутри оператора if или иным образом упростить свой код, просто немного повозившись с ним.

Следите за возможными значениями переменных во всем вашем коде. Если что-то имеет чтобы быть определенным значением в определенной точке кода, тогда нет смысла проверять это значение или устанавливать то, что оно уже есть. Это может немного усложниться с большим кодом, но это мощный навык.

Предложения по будущим функциям

Возможно, посмотрите на реализацию некоторых дополнительных опций в основной игре, таких как

  • защита и доспехи
  • выход из основного игрового цикла
  • разбиение частей атаки / защиты на их собственные функции

Также взгляните на

  • циклы do-while
  • Операторы переключения
  • константы

В целом, ваш код хорош, но есть несколько модификаций в потоке программы, которые могут быть сделаны, чтобы сократить операционную сложность.

  • 1

    Спасибо! Я изучу эти функции. Не могу поверить, что я не видел, что мог использовать операторы switch. Я постараюсь лучше отформатировать / оптимизировать свой код.

    — дерзко

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *