TransWikia.com

A simple terminal-based trading game in C

Code Review Asked by Redwolf Programs on February 20, 2021

I wrote this a few months ago as my first "serious" C program (I’ve used JS for a few years). It’s a simple resource collecting and trading game (based off one I made in HTML/JS), that takes input in the form of some simple commands.

#include <stdio.h>
#include <inttypes.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

struct trade_like {
    int8_t buy_item;
    int8_t sell_item;
    uint64_t buy_amount;
    uint64_t sell_amount;
    int16_t timer;
};

struct upgrade {
    int8_t thread;
    int8_t id;
};

void shorten_number(char *dest, uint64_t number) {
    if (number >= 1000000000000) {
        sprintf(dest, "%.2fT", fmin((double) number / 1000000000000.0, 999.99));
    } else if (number >= 1000000000) {
        sprintf(dest, "%.2fB", fmin((double) number / 1000000000.0, 999.99));
    } else if (number >= 1000000) {
        sprintf(dest, "%.2fM", fmin((double) number / 1000000.0, 999.99));
    } else if (number >= 1000) {
        sprintf(dest, "%.2fK", fmin((double) number / 1000.0, 999.99));
    } else {
        sprintf(dest, "%d", number);
    }

    return;
}

void print_resources(uint64_t resources[], struct trade_like trades[], struct trade_like deals[], struct upgrade upgrades[]) {
    char amounts[9][10];
    char trade_list[5][48];
    char deal_list[3][48];
    char upgrade_list[9][36];

    char names[9][8] = {"Wood", "Stone", "Coal", "Iron", "Copper", "Silver", "Gold", "Diamond", "Emerald"};
    char upper_names[9][8] = {"WOOD", "STONE", "COAL", "IRON", "COPPER", "SILVER", "GOLD", "DIAMOND", "EMERALD"};
    char unlock_costs[8][2][16] = {
        {"Wood (200)", "Coal (40)"},
        {"Stone (400)", "Coal (80)"},
        {"Stone (200)", "Iron (100)"},
        {"Iron (300)", "Copper (50)"},
        {"Stone (1.00K)", "Iron (400)"},
        {"Copper (500)", "Coal (300)"},
        {"Iron (1.00K)", "Silver (400)"},
        {"Diamond (200)", "Silver (100)"}
    };
    char multiplier_levels[7][8] = {"+10%", "+25%", "+75%", "DOUBLE", "LOTS OF", "TONS OF"};
    int16_t base_cost[7] = {200, 500, 1500, 4000, 10000, 20000};

    memset(trade_list, '', sizeof(trade_list[0][0]) * 240);
    memset(deal_list, '', sizeof(deal_list[0][0]) * 144);
    memset(upgrade_list, '', sizeof(upgrade_list[0][0]) * 324);

    printf("33[F33[A33[A33[A33[A33[A33[A33[A33[A33[A33[A33[A");

    for (int8_t i = 0; i < 9; i++) {
        shorten_number(amounts[i], resources[i]);
    }

    for (int8_t i = 0; i < 5; i++) {
        if (trades[i].timer >= 0) {
            char sell_string[10];
            char buy_string[10];

            shorten_number(sell_string, trades[i].sell_amount);
            shorten_number(buy_string, trades[i].buy_amount);

            sprintf(trade_list[i], "[%d] %s (%s) -> %s (%s)", i + 1, names[trades[i].sell_item], sell_string, names[trades[i].buy_item], buy_string);
        }
    }

    for (int8_t i = 0; i < 3; i++) {
        if (deals[i].timer >= 0) {
            char sell_string[10];
            char buy_string[10];

            shorten_number(sell_string, deals[i].sell_amount);
            shorten_number(buy_string, deals[i].buy_amount);

            sprintf(deal_list[i], "[%d] %s (%s) -> %s (%s)", i + 1, names[deals[i].sell_item], sell_string, names[deals[i].buy_item], buy_string);
        }
    }

    if (upgrades[0].thread != -1) {
        sprintf(upgrade_list[0], "MINE %s", upper_names[upgrades[0].id + 1]);
        sprintf(upgrade_list[1], "   %s", unlock_costs[upgrades[0].id][0]);
        sprintf(upgrade_list[2], "   %s", unlock_costs[upgrades[0].id][1]);
    }

    if (upgrades[1].thread != -1) {
        sprintf(upgrade_list[4], "%s %s", multiplier_levels[(int8_t) ((double) upgrades[1].id / 9.0)], upper_names[upgrades[1].id % 9]);

        char cost_string[10];

        shorten_number(cost_string, (double) base_cost[(int8_t) upgrades[1].id / 9] * (1.0 - 0.1 * (upgrades[1].id % 9)));

        sprintf(upgrade_list[5], "   %s (%s)", names[upgrades[1].id % 9], cost_string);
    }

    if (upgrades[2].thread == 2) {
        sprintf(upgrade_list[7], "AUTO-%s", upper_names[upgrades[2].id]);
        if (upgrades[2].id == 4) {
            strcpy(upgrade_list[8], "   Gold (200)");
        }
        sprintf(upgrade_list[8], "   %s (200)", names[upgrades[2].id + 2]);
    } else if (upgrades[2].thread == 3) {
        sprintf(upgrade_list[7], "%s/%s DEALS", upper_names[upgrades[2].id * 2], upper_names[upgrades[2].id * 2 + 1]);

        char cost_string[10];

        shorten_number(cost_string, 1750 - 375 * upgrades[2].id);

        sprintf(upgrade_list[8], "   %s (%s)", names[upgrades[2].id * 2 + 1], cost_string);
    }

    printf(
        "  Wood:     %-9s%-44s%-36sn  Stone:    %-9s%-44s%-36sn  Coal:     %-9s%-44s%-36sn  Iron:     %-9s%-44s%-36sn  Copper:   %-9s%-44s%-36sn  Silver:   %-9s%-44s%-36sn  Gold:     %-9s%-44s%-36sn  Diamond:  %-9s%-44s%-36sn  Emerald:  %-9s%-44s%-36snnnn >>                         bbbbbbbbbbbbbbbbbbbbbbbb",
        amounts[0], trade_list[0], upgrade_list[0], amounts[1], trade_list[1], upgrade_list[1], amounts[2], trade_list[2], upgrade_list[2],
        amounts[3], trade_list[3], upgrade_list[3], amounts[4], trade_list[4], upgrade_list[4], amounts[5], " ", upgrade_list[5],
        amounts[6], deal_list[0], upgrade_list[6], amounts[7], deal_list[1], upgrade_list[7], amounts[8], deal_list[2], upgrade_list[8]
    );

    return;
}

int main(int argc, char** argv) {
    int8_t unlocked = 0;
    uint64_t resources[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    double multipliers[9] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    int8_t auto_level = 0;

    int16_t item_costs[9] = {1, 2, 5, 10, 25, 40, 80, 200, 875};

    char names[9][8] = {"Wood", "Stone", "Coal", "Iron", "Copper", "Silver", "Gold", "Diamond", "Emerald"};

    struct trade_like trades[5];
    struct trade_like deals[3];

    int8_t trade_tick = 0;
    int8_t deal_tick = 0;
    int8_t unlocked_deals = 0;

    for (int8_t i = 0; i < 5; i++) {
        trades[i].timer = -1;
        
        if (i < 3) {
            deals[i].timer = -1;
        }
    }

    struct upgrade upgrades[3];

    upgrades[0].thread = 0;
    upgrades[0].id = 0;

    upgrades[0].thread = -1;
    upgrades[1].thread = -1;
    upgrades[2].thread = -1;

    int64_t turn_count = 0;

    char words_redo[4][24];

    srand(time(NULL));

    strcpy(words_redo[0], "collect");
    strcpy(words_redo[1], "wood");
    memset(words_redo[2], '', sizeof(words_redo[2][0]) * 48);

    printf("MERCHANT 2.0.0 [TERMINAL EDITION]nnnnnnnnnnnn  Enter 'collect wood' to begin playing!nnn33[A");

    char input[24];

    while (strncmp(input, "quitn", 5) != 0 && strncmp(input, "exitn", 5) != 0 && strncmp(input, "qn", 2) != 0 && strncmp(input, "xn", 2) != 0) {
        print_resources(resources, trades, deals, upgrades);

        fgets(input, 24, stdin);

        printf("33[F33[A33[A                                                  bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");

        char words[4][24];
        int8_t word_count = 0;
        int8_t within_word = 0;

        memset(words[0], '', sizeof(words[0][0]) * 96);

        for (int8_t i = 0; i < 24; i++) {
            if (input[i] == 'n' || input[i] == '') {
                break;
            } else if (input[i] >= 'a' && input[i] <= 'z' || input[i] >= 'A' && input[i] <= 'Z' || input[i] >= '0' && input[i] <= '9' || input[i] == '-' || input[i] == '_') {
                words[word_count][within_word++] = input[i];
            } else {
                if (within_word != 0) {
                    word_count++;
                    within_word = 0;

                    if (word_count == 4) {
                        break;
                    }
                }
            }
        }

        if (strncmp(words[0], "redo", 5) == 0 || strncmp(words[0], "r", 2) == 0 || words[0][0] == '') {
            strcpy(words[0], words_redo[0]);
            strcpy(words[1], words_redo[1]);
            strcpy(words[2], words_redo[2]);
            strcpy(words[3], words_redo[3]);
        }

        if (strncmp(words[0], "collect", 8) == 0 || strncmp(words[0], "col", 4) == 0 || strncmp(words[0], "c", 2) == 0) {
            int16_t collect_amount;

            if (words[1][0] == '' || words[2][0] != '') {
                printf("ERR: Expected one argument (resource name)nn");
                continue;
            }

            if (strncmp(words[1], "wood", 5) == 0 || strncmp(words[1], "w", 2) == 0) {
                if (unlocked < 0) {
                    printf("ERR: You haven't unlocked this resourcenn");
                    continue;
                }

                collect_amount = (int16_t) (floorf(multipliers[0]) + (rand() < RAND_MAX * (multipliers[0] - floorf(multipliers[0]))));

                resources[0] += collect_amount;
                printf("Collected %d wood", collect_amount);
            } else if (strncmp(words[1], "stone", 6) == 0 || strncmp(words[1], "s", 2) == 0) {
                if (unlocked < 1) {
                    printf("ERR: You haven't unlocked this resourcenn");
                    continue;
                }

                collect_amount = (int16_t) (floorf(multipliers[1]) + (rand() < RAND_MAX * (multipliers[1] - floorf(multipliers[1]))));

                resources[1] += collect_amount;
                printf("Collected %d stone", collect_amount);
            } else if (strncmp(words[1], "coal", 5) == 0 || strncmp(words[1], "c", 2) == 0) {
                if (unlocked < 2) {
                    printf("ERR: You haven't unlocked this resourcenn");
                    continue;
                }

                collect_amount = (int16_t) (floorf(multipliers[2]) + (rand() < RAND_MAX * (multipliers[2] - floorf(multipliers[2]))));

                resources[2] += collect_amount;
                printf("Collected %d coal", collect_amount);
            } else if (strncmp(words[1], "iron", 5) == 0 || strncmp(words[1], "i", 2) == 0) {
                if (unlocked < 3) {
                    printf("ERR: You haven't unlocked this resourcenn");
                    continue;
                }

                collect_amount = (int16_t) (floorf(multipliers[3]) + (rand() < RAND_MAX * (multipliers[3] - floorf(multipliers[3]))));

                resources[3] += collect_amount;
                printf("Collected %d iron", collect_amount);
            } else if (strncmp(words[1], "copper", 7) == 0 || strncmp(words[1], "cp", 3) == 0 || strncmp(words[1], "cr", 3) == 0) {
                if (unlocked < 4) {
                    printf("ERR: You haven't unlocked this resourcenn");
                    continue;
                }

                collect_amount = (int16_t) (floorf(multipliers[4]) + (rand() < RAND_MAX * (multipliers[4] - floorf(multipliers[4]))));

                resources[4] += collect_amount;
                printf("Collected %d copper", collect_amount);
            } else if (strncmp(words[1], "silver", 7) == 0 || strncmp(words[1], "si", 3) == 0 || strncmp(words[1], "sv", 3) == 0) {
                if (unlocked < 5) {
                    printf("ERR: You haven't unlocked this resourcenn");
                    continue;
                }

                collect_amount = (int16_t) (floorf(multipliers[5]) + (rand() < RAND_MAX * (multipliers[5] - floorf(multipliers[5]))));

                resources[5] += collect_amount;
                printf("Collected %d silver", collect_amount);
            } else if (strncmp(words[1], "gold", 5) == 0 || strncmp(words[1], "g", 2) == 0) {
                if (unlocked < 6) {
                    printf("ERR: You haven't unlocked this resourcenn");
                    continue;
                }

                collect_amount = (int16_t) (floorf(multipliers[6]) + (rand() < RAND_MAX * (multipliers[6] - floorf(multipliers[6]))));

                resources[6] += collect_amount;
                printf("Collected %d gold", collect_amount);
            } else if (strncmp(words[1], "diamond", 8) == 0 || strncmp(words[1], "d", 2) == 0) {
                if (unlocked < 7) {
                    printf("ERR: You haven't unlocked this resourcenn");
                    continue;
                }

                collect_amount = (int16_t) (floorf(multipliers[7]) + (rand() < RAND_MAX * (multipliers[7] - floorf(multipliers[7]))));

                resources[7] += collect_amount;
                printf("Collected %d diamond", collect_amount);
            } else if (strncmp(words[1], "emerald", 8) == 0 || strncmp(words[1], "e", 2) == 0 || strncmp(words[1], "m", 2) == 0) {
                if (unlocked < 8) {
                    printf("ERR: You haven't unlocked this resourcenn");
                    continue;
                }

                collect_amount = (int16_t) (floorf(multipliers[8]) + (rand() < RAND_MAX * (multipliers[8] - floorf(multipliers[8]))));

                resources[8] += collect_amount;
                printf("Collected %d emerald", collect_amount);
            } else {
                printf("ERR: Unknown resourcenn");
                continue;
            }
        } else if (strncmp(words[0], "trade", 6) == 0 || strncmp(words[0], "tr", 3) == 0 || strncmp(words[0], "t", 2) == 0) {
            if (words[1][0] == '' || words[2][0] != '') {
                printf("ERR: Expected one argument (trade id)nn");
                continue;
            }
            
            if (words[1][1] != '' || words[1][0] < '1' || words[1][0] > '5') {
                printf("ERR: Invalid trade idnn");
                continue;
            }

            int8_t trade = words[1][0] - 49;

            if (trades[trade].timer == -1) {
                printf("ERR: There is no trade with that idnn");
                continue;
            } else if (resources[trades[trade].sell_item] < trades[trade].sell_amount) {
                printf("ERR: You can't afford that tradenn");
                continue;
            } else {
                resources[trades[trade].buy_item] += trades[trade].buy_amount;
                resources[trades[trade].sell_item] -= trades[trade].sell_amount;

                trades[trade].timer = fmax(trades[trade].timer - (int16_t) ((trades[trade].buy_amount / resources[trades[trade].buy_item]) * 4), -1);

                printf("Trade #1: %s (%d) -> %s (%d)", names[trades[trade].sell_item], trades[trade].sell_amount, names[trades[trade].buy_item], trades[trade].buy_amount);
            }
        } else if (strncmp(words[0], "deal", 5) == 0 || strncmp(words[0], "d", 2) == 0) {
            if (words[1][0] == '' || words[2][0] != '') {
                printf("ERR: Expected one argument (deal id)nn");
                continue;
            }
            
            if (words[1][1] != '' || words[1][0] < '1' || words[1][0] > '3') {
                printf("ERR: Invalid deal idnn");
                continue;
            }

            int8_t deal = words[1][0] - 49;

            if (deals[deal].timer == -1) {
                printf("ERR: There is no deal with that idnn");
                continue;
            } else if (resources[deals[deal].sell_item] < deals[deal].sell_amount) {
                printf("ERR: You can't afford that dealnn");
                continue;
            } else {
                resources[deals[deal].buy_item] += deals[deal].buy_amount;
                resources[deals[deal].sell_item] -= deals[deal].sell_amount;

                deals[deal].timer = -1;

                printf("Deal #1: %s (%d) -> %s (%d)", names[deals[deal].sell_item], deals[deal].sell_amount, names[deals[deal].buy_item], deals[deal].buy_amount);
            }
        } else if (strncmp(words[0], "unlock", 7) == 0 || strncmp(words[0], "unl", 4) == 0 || strncmp(words[0], "u", 2) == 0) {
            if (words[1][0] == '' || words[2][0] != '') {
                printf("ERR: Expected one argument (resource id)nn");
                continue;
            }

            if (strncmp(words[1], "wood", 5) == 0 || strncmp(words[1], "w", 2) == 0) {
                printf("ERR: You have already unlocked this resourcenn");
                continue;
            } else if (strncmp(words[1], "stone", 6) == 0 || strncmp(words[1], "s", 2) == 0) {
                if (unlocked >= 1) {
                    printf("ERR: You have already unlocked this resourcenn");
                    continue;
                }

                if (resources[0] < 200 || resources[2] < 40) {
                    printf("ERR: You cannot afford to unlock this resourcenn");
                    continue;
                }

                resources[0] -= 200;
                resources[2] -= 40;

                unlocked++;

                printf("Unlocked stone");
            } else if (strncmp(words[1], "coal", 5) == 0 || strncmp(words[1], "c", 2) == 0) {
                if (unlocked >= 2) {
                    printf("ERR: You have already unlocked this resourcenn");
                    continue;
                }

                if (unlocked < 1) {
                    printf("ERR: You cannot unlock this resource yetnn");
                    continue;
                }

                if (resources[1] < 400 || resources[2] < 80) {
                    printf("ERR: You cannot afford to unlock this resourcenn");
                    continue;
                }

                resources[1] -= 400;
                resources[2] -= 80;

                unlocked++;

                printf("Unlocked coal");
            } else if (strncmp(words[1], "iron", 5) == 0 || strncmp(words[1], "i", 2) == 0) {
                if (unlocked >= 3) {
                    printf("ERR: You have already unlocked this resourcenn");
                    continue;
                }

                if (unlocked < 2) {
                    printf("ERR: You cannot unlock this resource yetnn");
                    continue;
                }

                if (resources[1] < 200 || resources[3] < 100) {
                    printf("ERR: You cannot afford to unlock this resourcenn");
                    continue;
                }

                resources[1] -= 200;
                resources[3] -= 100;

                unlocked++;

                printf("Unlocked iron");
            } else if (strncmp(words[1], "copper", 7) == 0 || strncmp(words[1], "cp", 3) == 0 || strncmp(words[1], "cr", 3) == 0) {
                if (unlocked >= 4) {
                    printf("ERR: You have already unlocked this resourcenn");
                    continue;
                }

                if (unlocked < 3) {
                    printf("ERR: You cannot unlock this resource yetnn");
                    continue;
                }

                if (resources[3] < 300 || resources[4] < 50) {
                    printf("ERR: You cannot afford to unlock this resourcenn");
                    continue;
                }

                resources[3] -= 300;
                resources[4] -= 50;

                unlocked++;

                printf("Unlocked copper");
            } else if (strncmp(words[1], "silver", 7) == 0 || strncmp(words[1], "si", 3) == 0 || strncmp(words[1], "sv", 3) == 0) {
                if (unlocked >= 5) {
                    printf("ERR: You have already unlocked this resourcenn");
                    continue;
                }

                if (unlocked < 4) {
                    printf("ERR: You cannot unlock this resource yetnn");
                    continue;
                }

                if (resources[1] < 1000 || resources[3] < 400) {
                    printf("ERR: You cannot afford to unlock this resourcenn");
                    continue;
                }

                resources[1] -= 1000;
                resources[3] -= 400;

                unlocked++;

                printf("Unlocked silver");
            } else if (strncmp(words[1], "gold", 5) == 0 || strncmp(words[1], "g", 2) == 0) {
                if (unlocked >= 6) {
                    printf("ERR: You have already unlocked this resourcenn");
                    continue;
                }

                if (unlocked < 5) {
                    printf("ERR: You cannot unlock this resource yetnn");
                    continue;
                }

                if (resources[4] < 500 || resources[2] < 300) {
                    printf("ERR: You cannot afford to unlock this resourcenn");
                    continue;
                }

                resources[4] -= 500;
                resources[2] -= 300;

                unlocked++;

                printf("Unlocked gold");
            } else if (strncmp(words[1], "diamond", 8) == 0 || strncmp(words[1], "d", 2) == 0) {
                if (unlocked >= 7) {
                    printf("ERR: You have already unlocked this resourcenn");
                    continue;
                }

                if (unlocked < 6) {
                    printf("ERR: You cannot unlock this resource yetnn");
                    continue;
                }

                if (resources[3] < 1000 || resources[5] < 400) {
                    printf("ERR: You cannot afford to unlock this resourcenn");
                    continue;
                }

                resources[3] -= 1000;
                resources[5] -= 400;

                unlocked++;

                printf("Unlocked diamond");
            } else if (strncmp(words[1], "emerald", 8) == 0 || strncmp(words[1], "e", 2) == 0 || strncmp(words[1], "m", 2) == 0) {
                if (unlocked == 8) {
                    printf("ERR: You have already unlocked this resourcenn");
                    continue;
                }

                if (unlocked < 7) {
                    printf("ERR: You cannot unlock this resource yetnn");
                    continue;
                }

                if (resources[7] < 200 || resources[5] < 100) {
                    printf("ERR: You cannot afford to unlock this resourcenn");
                    continue;
                }

                resources[7] -= 200;
                resources[5] -= 100;

                unlocked++;

                printf("Unlocked emerald");
            } else {
                printf("ERR: Unknown resourcenn");
                continue;
            }

            upgrades[0].thread = -1;
        } else if (strncmp(words[0], "multiplier", 11) == 0 || strncmp(words[0], "multiply", 9) == 0 || strncmp(words[0], "multi", 6) == 0 || strncmp(words[0], "mul", 4) == 0 || strncmp(words[0], "m", 2) == 0) {
            if (words[1][0] == '' || words[2][0] != '') {
                printf("ERR: Expected one argument (resource name)nn");
                continue;
            }

            int8_t resource;

            if (strncmp(words[1], "wood", 5) == 0 || strncmp(words[1], "w", 2) == 0) {
                resource = 0;
            } else if (strncmp(words[1], "stone", 6) == 0 || strncmp(words[1], "s", 2) == 0) {
                resource = 1;
            } else if (strncmp(words[1], "coal", 5) == 0 || strncmp(words[1], "c", 2) == 0) {
                resource = 2;
            } else if (strncmp(words[1], "iron", 5) == 0 || strncmp(words[1], "i", 2) == 0) {
                resource = 3;
            } else if (strncmp(words[1], "copper", 7) == 0 || strncmp(words[1], "cp", 3) == 0 || strncmp(words[1], "cr", 3) == 0) {
                resource = 4;
            } else if (strncmp(words[1], "silver", 7) == 0 || strncmp(words[1], "si", 3) == 0 || strncmp(words[1], "sv", 3) == 0) {
                resource = 5;
            } else if (strncmp(words[1], "gold", 5) == 0 || strncmp(words[1], "g", 2) == 0) {
                resource = 6;
            } else if (strncmp(words[1], "diamond", 8) == 0 || strncmp(words[1], "d", 2) == 0) {
                resource = 7;
            } else if (strncmp(words[1], "emerald", 8) == 0 || strncmp(words[1], "e", 2) == 0 || strncmp(words[1], "m", 2) == 0) {
                resource = 8;
            } else {
                printf("ERR: Unknown resourcenn");
                continue;
            }

            if (multipliers[resource] == 1.0) {
                if (resources[resource] < 200 - 20 * resource) {
                    printf("ERR: You cannot afford this upgradenn");
                    continue;
                }

                resources[resource] -= 200 - 20 * resource;
                multipliers[resource] = 1.1;
            } else if (multipliers[resource] == 1.1) {
                if (resources[resource] < 500 - 50 * resource) {
                    printf("ERR: You cannot afford this upgradenn");
                    continue;
                }

                resources[resource] -= 500 - 50 * resource;
                multipliers[resource] = 1.25;
            } else if (multipliers[resource] == 1.25) {
                if (resources[resource] < 1500 - 150 * resource) {
                    printf("ERR: You cannot afford this upgradenn");
                    continue;
                }

                resources[resource] -= 1500 - 150 * resource;
                multipliers[resource] = 1.75;
            } else if (multipliers[resource] == 1.75) {
                if (resources[resource] < 4000 - 40 * resource) {
                    printf("ERR: You cannot afford this upgradenn");
                    continue;
                }

                resources[resource] -= 4000 - 400 * resource;
                multipliers[resource] = 2.0;
            } else if (multipliers[resource] == 2.0) {
                if (resources[resource] < 10000 - 1000 * resource) {
                    printf("ERR: You cannot afford this upgradenn");
                    continue;
                }

                resources[resource] -= 10000 - 1000 * resource;
                multipliers[resource] = 5.0;
            } else if (multipliers[resource] == 5.0) {
                if (resources[resource] < 20000 - 2000 * resource) {
                    printf("ERR: You cannot afford this upgradenn");
                    continue;
                }

                resources[resource] -= 20000 - 2000 * resource;
                multipliers[resource] = 10.0;
            } else {
                printf("ERR: This resource is already at max multipliernn");
                continue;
            }

            upgrades[1].thread = -1;
        } else if (strncmp(words[0], "autocollect", 12) == 0 || strncmp(words[0], "autocol", 8) == 0 || strncmp(words[0], "auto", 5) == 0 || strncmp(words[0], "a", 2) == 0) {
            if (words[1][0] == '' || words[2][0] != '') {
                printf("ERR: Expected one argument (resource)nn");
                continue;
            }

            int8_t level;

            if (strncmp(words[1], "wood", 5) == 0 || strncmp(words[1], "w", 2) == 0) {
                level = 1;
            } else if (strncmp(words[1], "stone", 6) == 0 || strncmp(words[1], "s", 2) == 0) {
                level = 2;
            } else if (strncmp(words[1], "coal", 5) == 0 || strncmp(words[1], "c", 2) == 0) {
                level = 3;
            } else if (strncmp(words[1], "iron", 5) == 0 || strncmp(words[1], "i", 2) == 0) {
                level = 4;
            } else if (strncmp(words[1], "metal", 6) == 0 || strncmp(words[1], "m", 2) == 0) {
                level = 5;
            } else {
                printf("ERR: Unknown or non-automatable resourcenn");
                continue;
            }

            if (auto_level > level) {
                printf("ERR: You have already automated this resourcenn");
                continue;
            }

            if (auto_level < level - 1) {
                printf("ERR: You cannot automate this resource yetnn");
                continue;
            }

            if (resources[level + 1] < 200) {
                printf("ERR: You cannot afford to automate this resourcenn");
                continue;
            }

            auto_level = level;
            resources[level + 1] -= 200;

            upgrades[2].thread = -1;
        } else if (strncmp(words[0], "unlock-deal", 12) == 0 || strncmp(words[0], "unlock_deal", 12) == 0 || strncmp(words[0], "unl-d", 6) == 0 || strncmp(words[0], "unl_d", 6) == 0 || strncmp(words[0], "ud", 3) == 0 || strncmp(words[0], "l", 2) == 0) {
            if (words[1][0] == '' || words[2][0] != '') {
                printf("ERR: Expected one argument (resource)nn");
                continue;
            }

            int8_t deal;

            if (strncmp(words[1], "wood", 5) == 0 || strncmp(words[1], "w", 2) == 0) {
                deal = 2;
            } else if (strncmp(words[1], "stone", 6) == 0 || strncmp(words[1], "s", 2) == 0) {
                deal = 2;
            } else if (strncmp(words[1], "coal", 5) == 0 || strncmp(words[1], "c", 2) == 0) {
                deal = 4;
            } else if (strncmp(words[1], "iron", 5) == 0 || strncmp(words[1], "i", 2) == 0) {
                deal = 4;
            } else if (strncmp(words[1], "copper", 7) == 0 || strncmp(words[1], "cp", 3) == 0 || strncmp(words[1], "cr", 3) == 0) {
                deal = 6;
            } else if (strncmp(words[1], "silver", 7) == 0 || strncmp(words[1], "si", 3) == 0 || strncmp(words[1], "sv", 3) == 0) {
                deal = 6;
            } else if (strncmp(words[1], "gold", 5) == 0 || strncmp(words[1], "g", 2) == 0) {
                deal = 8;
            } else if (strncmp(words[1], "diamond", 8) == 0 || strncmp(words[1], "d", 2) == 0) {
                deal = 8;
            } else {
                printf("ERR: Unknown resourcenn");
                continue;
            }

            if (deal < unlocked_deals) {
                printf("You have already unlocked these dealsnn");
                continue;
            }

            if (unlocked_deals < deal - 2) {
                printf("You cannot unlock these deals yetnn");
                continue;
            }

            if (resources[deal - 1] < 2125 - (deal / 2) * 375) {
                printf("You cannot afford to unlock these dealsnn");
                continue;
            }

            resources[deal - 1] -= 2125 - (deal / 2) * 375;
            unlocked_deals = deal;

            upgrades[2].thread = -1;
        } else {
            printf("ERR: Unknown commandnn");
            continue;
        }

        strcpy(words_redo[0], words[0]);
        strcpy(words_redo[1], words[1]);
        strcpy(words_redo[2], words[2]);
        strcpy(words_redo[3], words[3]);

        printf("nn");

        if (auto_level == 1) {
            if (rand() < (double) RAND_MAX * 0.1) {
                resources[0]++;
            }
        } else if (auto_level == 2) {
            if (rand() < (double) RAND_MAX * 0.2) {
                resources[0]++;
            }

            if (rand() < (double) RAND_MAX * 0.1) {
                resources[1]++;
            }
        } else if (auto_level == 3) {
            if (rand() < (double) RAND_MAX * 0.35) {
                resources[0]++;
            }

            if (rand() < (double) RAND_MAX * 0.2) {
                resources[1]++;
            }

            if (rand() < (double) RAND_MAX * 0.1) {
                resources[2]++;
            }
        } else if (auto_level == 4) {
            if (rand() < (double) RAND_MAX * 0.5) {
                resources[0]++;
            }

            if (rand() < (double) RAND_MAX * 0.35) {
                resources[1]++;
            }

            if (rand() < (double) RAND_MAX * 0.2) {
                resources[2]++;
            }

            if (rand() < (double) RAND_MAX * 0.1) {
                resources[3]++;
            }
        } else if (auto_level == 5) {
            if (rand() < (double) RAND_MAX * 0.75) {
                resources[0]++;
            }

            if (rand() < (double) RAND_MAX * 0.5) {
                resources[1]++;
            }

            if (rand() < (double) RAND_MAX * 0.35) {
                resources[2]++;
            }

            if (rand() < (double) RAND_MAX * 0.2) {
                resources[3]++;
            }

            if (rand() < (double) RAND_MAX * 0.1) {
                resources[4]++;
            }

            if (rand() < (double) RAND_MAX * 0.05) {
                resources[5]++;
            }

            if (rand() < (double) RAND_MAX * 0.01) {
                resources[6]++;
            }
        }

        if (trade_tick-- < 0) {
            for (int8_t i = 0; i < 5; i++) {
                if (trades[i].timer >= 0)
                    trades[i].timer--;
            }

            if (rand() < RAND_MAX * 0.15) {
                int8_t trade = -1;

                for (int8_t i = 0; i < 5; i++) {
                    if (trades[i].timer == -1) {
                        trade = i;
                        break;
                    }
                }

                if (trade != -1) {
                    trades[trade].sell_item = (int8_t) ((rand() / (double) RAND_MAX) * (unlocked + 1));
                    trades[trade].sell_amount = (uint64_t) (resources[trades[trade].sell_item] * ((rand() / (double) RAND_MAX) + 0.25) + (uint64_t) (rand() / (double) RAND_MAX) * 40 * multipliers[trades[trade].sell_item]);
                
                    do {
                        trades[trade].buy_item = (int8_t) ((rand() / (double) RAND_MAX) * (fmin(unlocked, 8) + 1));

                        double random = rand() / (double) RAND_MAX;

                        if (random < 0.02 && unlocked <= 5) {
                            trades[trade].buy_item += 3;
                        } else if (random < 0.2 && unlocked <= 6) {
                            trades[trade].buy_item += 2;
                        } else if (random < 0.6 && unlocked <= 7) {
                            trades[trade].buy_item += 1;
                        }
                    } while (trades[trade].buy_item == trades[trade].sell_item);

                    trades[trade].buy_amount = (uint64_t) (((trades[trade].sell_amount * item_costs[trades[trade].sell_item]) * ((rand() / (double) RAND_MAX) * 0.6 + 0.75)) / item_costs[trades[trade].buy_item]);
                    trades[trade].timer = (int16_t) ((rand() / (double) RAND_MAX) * 8 + 16);
                }
            }

            trade_tick = (int8_t) ((rand() / (double) RAND_MAX) * 5) + 4;
        }

        if (unlocked_deals != 0 && deal_tick-- < 0) {
            for (int8_t i = 0; i < 3; i++) {
                if (deals[i].timer >= 0)
                    deals[i].timer--;
            }

            if (rand() < RAND_MAX * 0.35) {
                int8_t deal = -1;

                for (int8_t i = 0; i < 3; i++) {
                    if (deals[i].timer == -1) {
                        deal = i;
                        break;
                    }
                }

                if (deal != -1) {
                    deals[deal].sell_item = (int8_t) ((rand() / (double) RAND_MAX) * unlocked_deals);
                    deals[deal].sell_amount = (uint64_t) (resources[deals[deal].sell_item] * ((rand() / (double) RAND_MAX) * 0.5 + 0.1) + (rand() / (double) RAND_MAX) * 20 * multipliers[deals[deal].sell_item]);
                
                    do {
                        deals[deal].buy_item = (int8_t) ((rand() / (double) RAND_MAX) * unlocked_deals);
                    } while (deals[deal].buy_item == deals[deal].sell_item);

                    deals[deal].buy_amount = (uint64_t) (((deals[deal].sell_amount * item_costs[deals[deal].sell_item]) * ((rand() / (double) RAND_MAX) + 0.6)) / item_costs[deals[deal].buy_item]);
                    deals[deal].timer = (int16_t) ((rand() / (double) RAND_MAX) * 3 + 8);
                }
            }

            deal_tick = (int8_t) ((rand() / (double) RAND_MAX) * 2) + 2;
        }

        if (upgrades[0].thread == -1) {
            if (unlocked == 0) {
                upgrades[0].thread = 0;
                upgrades[0].id = 0;
            } else if (unlocked == 1 && resources[2]) {
                upgrades[0].thread = 0;
                upgrades[0].id = 1;
            } else if (unlocked == 2 && resources[3]) {
                upgrades[0].thread = 0;
                upgrades[0].id = 2;
            } else if (unlocked == 3 && resources[4]) {
                upgrades[0].thread = 0;
                upgrades[0].id = 3;
            } else if (unlocked == 4 && resources[5]) {
                upgrades[0].thread = 0;
                upgrades[0].id = 4;
            } else if (unlocked == 5 && resources[6]) {
                upgrades[0].thread = 0;
                upgrades[0].id = 5;
            } else if (unlocked == 6 && resources[7]) {
                upgrades[0].thread = 0;
                upgrades[0].id = 6;
            } else if (unlocked == 7 && resources[8]) {
                upgrades[0].thread = 0;
                upgrades[0].id = 7;
            }
        }

        if (upgrades[1].thread == -1) {
            for (int8_t i = 0; i < 9; i++) {
                if (multipliers[i] == 5.0 && resources[i] > 2000) {
                    upgrades[1].thread = 1;
                    upgrades[1].id = i + 45;

                    break;
                }
            }

            for (int8_t i = 0; i < 9; i++) {
                if (multipliers[i] == 2.0 && resources[i] > 1000) {
                    upgrades[1].thread = 1;
                    upgrades[1].id = i + 36;

                    break;
                }
            }

            for (int8_t i = 0; i < 9; i++) {
                if (multipliers[i] == 1.75 && resources[i] > 400) {
                    upgrades[1].thread = 1;
                    upgrades[1].id = i + 27;

                    break;
                }
            }

            for (int8_t i = 0; i < 9; i++) {
                if (multipliers[i] == 1.25 && resources[i] > 150) {
                    upgrades[1].thread = 1;
                    upgrades[1].id = i + 18;

                    break;
                }
            }

            for (int8_t i = 0; i < 9; i++) {
                if (multipliers[i] == 1.1 && resources[i] > 50) {
                    upgrades[1].thread = 1;
                    upgrades[1].id = i + 9;

                    break;
                }
            }

            for (int8_t i = 0; i < 9; i++) {
                if (multipliers[i] == 1.0 && resources[i] > 20) {
                    upgrades[1].thread = 1;
                    upgrades[1].id = i;

                    break;
                }
            }
        }

        if (upgrades[2].thread == -1) {
            if (auto_level == 0 && unlocked >= 1 && resources[2]) {
                upgrades[2].thread = 2;
                upgrades[2].id = 0;
            } else if (auto_level == 1 && unlocked >= 2 && resources[3]) {
                upgrades[2].thread = 2;
                upgrades[2].id = 1;
            } else if (auto_level == 2 && unlocked >= 3 && resources[4]) {
                upgrades[2].thread = 2;
                upgrades[2].id = 2;
            } else if (auto_level == 3 && unlocked >= 4 && resources[5]) {
                upgrades[2].thread = 2;
                upgrades[2].id = 3;
            } else if (auto_level == 4 && unlocked >= 6) {
                upgrades[2].thread = 2;
                upgrades[2].id = 4;
            } else if (unlocked_deals == 0 && unlocked >= 1) {
                upgrades[2].thread = 3;
                upgrades[2].id = 0;
            } else if (unlocked_deals == 2 && unlocked >= 3) {
                upgrades[2].thread = 3;
                upgrades[2].id = 1;
            } else if (unlocked_deals == 4 && unlocked >= 5) {
                upgrades[2].thread = 3;
                upgrades[2].id = 2;
            } else if (unlocked_deals == 6 && unlocked >= 7) {
                upgrades[2].thread = 3;
                upgrades[2].id = 3;
            }
        }

        turn_count++;
    }

    printf("33[F33[A  Finished game after %d turn(s)nn", turn_count);

    return 0;
}

(Compiled on my machine (Bash, Linux VM) with gcc -g -lm -o merchant merchant.c)

Some things I was wondering about:

  • Is there a way to get rid of all those else ifs? Maybe a switch statement or for loop?
  • Is there a better way to overwrite all of the program’s previous output than just moving the cursor to overwrite it?
  • Is my code readable?

First post on code review, if there’s something I forgot to include let me know!

2 Answers

Is there a way to get rid of all those else ifs? Maybe a switch statement or for loop?

Certainly.

With if (strncmp(words[0], "redo", .... consider a table of pairs of string and function.

// Something like 
for (i=0; i<table_size; i++) {
  if (my_compare(words[0], table[i].s)) {
    table[i].f(state_data, words, ....);  // do whatever per helper function
    break;
  }
}
if (i == table_size) my_handle_no_match();

With shorten_number():

To get a correct answer is tricky mixing 64-bit integer math with double.

Example: (Note original code provides errant results with large values and near 999.995+*10^3x)

// Reduce to "1.00x" to "999.99x"
void shorten_number(char *dest, uint64_t number) {
  if (number < 1000) {
    sprintf(dest, "%d", (int) number);
    return;
  }
  static const char suffix[] = " KMBTqQ"; // or maybe metric " kMGTPEZY"
  int suffix_index = 0;
  double scale = 1.0;

  while (number/scale >= 999.995) {
    scale *= 1000.0;
    suffix_index++;
  }
  sprintf(dest, "%.2f%c", number / scale, suffix[suffix_index]);
}

Is there a better way to overwrite all of the program's previous output than just moving the cursor to overwrite it?

C has no standard way to overwrite previous output. Any solution is specific to the terminal type.

Rather than printf("33[F33[A33[A ..., consider making a helper function erase_line() that does the same, but with this abstraction allows to more easily identify and maintain implementation specific code.

Same applies for any place an ANSI escape sequence is used - abstract it.


Is my code readable?

Mostly yes.

I find the 4 space indent excessive over 2, yet such style issues are best decided by your group's style guide.

main() is long. Perhaps break it up.

Too many naked magic numbers.


Zero data cleanly

Rather than potentially miscalculated code

// memset(trade_list, '', sizeof(trade_list[0][0]) * 240);
memset(trade_list, 0, sizeof trade_list);

or initialize with zero

// char trade_list[5][48];
char trade_list[5][48] = { 0 };

Wrong format

uint64_t number
// at this point number < 1000, but still type uint64_t
// sprintf(dest, "%d", number);
sprintf(dest, "%d", (int) number);
// or 
sprintf(dest, "%" PRIu64, number);

This hints at a serious weakness in OP's efforts: not fully enabling all warnings as a good well enabled compiler will warn about this. This is the most important feedback: enable all warnings and let your compiler give your rapid feedback.

Answered by chux - Reinstate Monica on February 20, 2021

You may use a switch, but be informed that using break; to break the loop would not work (Instead you would have to use a goto to break the loop.); same applies to a nested for-loop (With additional regards to continue, which would also have to be used in a goto way.), so summa summarum, using if, else if, else is the best way in your case.

Moving the cursor is the way how to do in a terminal application, totally fine.

Your code is readable, but I would suggest to outsource some code to functions, especially all this redundant checking.

PS I would probably separate the input code from the output code. Meaning, I would write a function only for user inputs, one function for processing those inputs and another function for outputs.

Answered by paladin on February 20, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP