2
\$\begingroup\$

I recently started studying C++ programming; while attending the course I'm writing a program on the side as a personal project to familiarize with what I'm learning. It is still very basic and I'm sure that as I begin learning OOP there will be a lot of room for improving it. But for now I would like your feedback / suggestion to understand if the logic / coding I wrote is correct and where I could have done it better. Thanks a lot!

#include <iostream>
#include <vector>

using namespace std;

const char* choose_race();
const char* choose_character_class();
bool define_strength(int& strength, int& unassigned_points);
bool define_dexterity(int& dexterity, int& unassigned_points);
bool define_constitution(int& constitution, int& unassigned_points);
bool define_intellect(int& intellect, int& unassigned_points);
bool define_wisdom(int& wisdom, int& unassigned_points);
bool define_charisma(int& charisma, int& unassigned_points);
bool race_stats_mod(int race, int& strength, int& dexterity, int& constitution, int& intellect, int& wisdom, int& charisma);

int main()
{
    cout << "***** Fantasy Character Creation Tool *****" << endl;
    cout << endl;

    // ABILITY SCORE MODIFIERS TABLE INITIALIZATION

    vector<vector<int>> ability_score_modifier(45, vector<int>(2));
    
    ability_score_modifier.at(0).at(0) = 1;
    ability_score_modifier.at(0).at(1) = -5;

    int counter{ 2 };
    int modifier{ -4 };
    
    do
    {
        for (int i = 0; i < 2; i++)
        {
            ability_score_modifier.at(counter - 1).at(0) = counter;
            ability_score_modifier.at(counter - 1).at(1) = modifier;
            counter++;
        }
        modifier++;
    } while (counter<=45);

    // GENERAL VARIABLE INIZIALIZATION

    int level{ 1 };
    int experience{ 0 };
    int strength{ 10 }, dexterity{ 10 }, constitution{ 10 }, intellect{ 10 }, wisdom{ 10 }, charisma{ 10 };

    // CHOOSE RACE

    string race;
    race = choose_race();
    system("cls");

    // CHOOSE CLASS

    string character_class;
    character_class = choose_character_class();
    system("cls");

    // CHOOSE NAME

    string name;
    cout << "Enter your character name: ";
    cin >> name;
    system("cls");

    // MODIFY ATTRIBUTES VALUES BASED ON CHOOSEN RACE

    int race_to_integer{};
    if (race == "Human")
    {
        race_to_integer = 1;
    }
    else
    {
        if (race == "Dwarf")
        {
            race_to_integer = 2;
        }
        else
        {
            if (race == "Elf")
            {
                race_to_integer = 3;
            }
        }
    }

    bool check_if_selection_success{ false };
    
    do
    {
        check_if_selection_success = race_stats_mod(race_to_integer, strength, dexterity, constitution, intellect, wisdom, charisma);
    } while (check_if_selection_success == false);

    system("cls");

    // ASSIGN ATTRIBUTE STATS

    cout << "SELECT YOUR CHARACTER ATTRIBUTES" << endl;
    cout << endl;
    cout << "Current stats:" << endl;
    cout << endl;
    cout << "STRENGTH: " << strength << endl;
    cout << "DEXTERITY: " << dexterity << endl;
    cout << "CONSTITUTION: " << constitution << endl;
    cout << "INTELLECT: " << intellect << endl;
    cout << "WISDOM: " << wisdom << endl;
    cout << "CHARISMA: " << charisma << endl;
    cout << endl;
    
    int unassigned_points{ 15 };

    cout << "Available points: " << unassigned_points << endl;
    cout << endl;
    cout << "Points gained or spent based on attribute values:" << endl;
    cout << endl;
    cout << "7 / gain 4 point" << endl;
    cout << "8 / gain 2 point" << endl;
    cout << "9 / gain 1 point" << endl;
    cout << "11 --> 13 / 1 point each" << endl;
    cout << "14 --> 15 / 2 points each" << endl;
    cout << "16 --> 17 / 3 points each" << endl;
    cout << "18 / 4 points" << endl;
    cout << endl;
    cout << "------------------------------------------" << endl;
    cout << endl;
    
    do
    {
        check_if_selection_success = define_strength(strength, unassigned_points);
    } while (check_if_selection_success == false); 
    
    cout << "Remaining available points: " << unassigned_points << endl;
    cout << endl;
    cout << "------------------------------------------" << endl;
    cout << endl;
    check_if_selection_success = false;
    
    do
    {
        check_if_selection_success = define_dexterity(dexterity, unassigned_points);
    } while (check_if_selection_success == false);

    cout << "Remaining available points: " << unassigned_points << endl;
    cout << endl;
    cout << "------------------------------------------" << endl;
    cout << endl;
    check_if_selection_success = false;

    do
    {
        check_if_selection_success = define_constitution(constitution, unassigned_points);
    } while (check_if_selection_success == false);

    cout << "Remaining available points: " << unassigned_points << endl;
    cout << endl;
    cout << "------------------------------------------" << endl;
    cout << endl;
    check_if_selection_success = false;

    do
    {
        check_if_selection_success = define_intellect(intellect, unassigned_points);
    } while (check_if_selection_success == false);

    cout << "Remaining available points: " << unassigned_points << endl;
    cout << endl;
    cout << "------------------------------------------" << endl;
    cout << endl;
    check_if_selection_success = false;

    do
    {
        check_if_selection_success = define_wisdom(wisdom, unassigned_points);
    } while (check_if_selection_success == false);

    cout << "Remaining available points: " << unassigned_points << endl;
    cout << endl;
    cout << "------------------------------------------" << endl;
    cout << endl;
    check_if_selection_success = false;

    do
    {
        check_if_selection_success = define_charisma(charisma, unassigned_points);
    } while (check_if_selection_success == false);
    
    // CHECK IF ALL ATTRIBUTE POINTS HAVE BEEN ASSIGNED OR NOT
    // IN CASE THERE ARE REMAINING POINTS IT WILL PROMPT THE USER TO RECONSIDER THE PREVIOUS SELECTION

    if (unassigned_points>0)
    {
        cout << "You did not assign all available points." << endl;
        cout << endl;
        while (unassigned_points>0)
        {
        cout << "Remaining available points: " << unassigned_points << endl;
        cout << endl;
        cout << "Witch attribute would you like to change:" << endl;
        cout << endl;
        cout << "[1] Strength = " << strength << endl;
        cout << "[2] Dexterity = " << dexterity << endl;
        cout << "[3] Constitution = " << constitution << endl;
        cout << "[4] Intellect = " << intellect << endl;
        cout << "[5] Wisdom = " << wisdom << endl;
        cout << "[6] Charisma = " << charisma << endl;
        cout << endl;
        cout << "Selection: ";
        int change_attribute{};
        cin >> change_attribute;
        switch (change_attribute)
        {
        case 1:
            check_if_selection_success = false;
            do
            {
                check_if_selection_success = define_strength(strength, unassigned_points);
            } while (check_if_selection_success == false);
            cout << endl;
            break;
        case 2:
            check_if_selection_success = false;
            do
            {
                check_if_selection_success = define_dexterity(dexterity, unassigned_points);
            } while (check_if_selection_success == false);
            cout << endl;
            break;
        case 3:
            check_if_selection_success = false;
            do
            {
                check_if_selection_success = define_constitution(constitution, unassigned_points);
            } while (check_if_selection_success == false);
            cout << endl;
            break;
        case 4:
            check_if_selection_success = false;
            do
            {
                check_if_selection_success = define_intellect(intellect, unassigned_points);
            } while (check_if_selection_success == false);
            cout << endl;
            break;
        case 5:
            check_if_selection_success = false;
            do
            {
                check_if_selection_success = define_wisdom(wisdom, unassigned_points);
            } while (check_if_selection_success == false);
            cout << endl;
            break;
        case 6:
            check_if_selection_success = false;
            do
            {
                check_if_selection_success = define_charisma(charisma, unassigned_points);
            } while (check_if_selection_success == false);
            cout << endl;
            break;
        default:
            cout << "Wrong selection. Try again." << endl;
            cout << endl;
            break;
        }
        }
    }
    
    // DEFINE THE ATTRIBUTES MOD VALUES BASED ON FINAL ATTRIBUTES AND ABILITY SCORES VECTOR

    int strength_mod{}, dexterity_mod{}, constitution_mod{}, intellect_mod{}, wisdom_mod{}, charisma_mod{};

    for (int i = 0; i < 45; i++)
    {
        if (strength == ability_score_modifier.at(i).at(0))
        {
            strength_mod = ability_score_modifier.at(i).at(1);
        }
    }

    for (int i = 0; i < 45; i++)
    {
        if (dexterity == ability_score_modifier.at(i).at(0))
        {
            dexterity_mod = ability_score_modifier.at(i).at(1);
        }
    }

    for (int i = 0; i < 45; i++)
    {
        if (constitution == ability_score_modifier.at(i).at(0))
        {
            constitution_mod = ability_score_modifier.at(i).at(1);
        }
    }

    for (int i = 0; i < 45; i++)
    {
        if (intellect == ability_score_modifier.at(i).at(0))
        {
            intellect_mod = ability_score_modifier.at(i).at(1);
        }
    }

    for (int i = 0; i < 45; i++)
    {
        if (wisdom == ability_score_modifier.at(i).at(0))
        {
            wisdom_mod = ability_score_modifier.at(i).at(1);
        }
    }

    for (int i = 0; i < 45; i++)
    {
        if (charisma == ability_score_modifier.at(i).at(0))
        {
            charisma_mod = ability_score_modifier.at(i).at(1);
        }
    }

    system("cls");

    // PRINT OUT THE CHARACTER SHEET

    cout << "***** YOUR CHARACTER *****" << endl;
    cout << endl;
    cout << name << " - LVL " << level << endl;
    cout << race << " " << character_class << endl;
    cout << endl;
    cout << "PRIMARY STATS: " << endl;
    cout << endl;
    cout << "STRENGTH: " << strength << " - mod: " << strength_mod << endl;
    cout << "DEXTERITY: " << dexterity << " - mod: " << dexterity_mod << endl;
    cout << "CONSTITUTION: " << constitution << " - mod: " << constitution_mod << endl;
    cout << "INTELLECT: " << intellect << " - mod: " << intellect_mod << endl;
    cout << "WISDOM: " << wisdom << " - mod: " << wisdom_mod << endl;
    cout << "CHARISMA: " << charisma << " - mod: " << charisma_mod << endl;

    return 0;
}

const char* choose_race()
{
    int race{};
    char confirm_choice{ 'n' };

    cout << "Select your character race: " << endl;
    cout << endl;
    cout << "[1] Human" << endl;
    cout << "[2] Dwarf" << endl;
    cout << "[3] Elf" << endl;
    cout << endl;
    cout << "Selection: ";
    cin >> race;
    switch (race)
    {
    case 1:
        cout << endl;
        cout << "In the reckonings of most worlds, humans are the youngest of the common races," << endl; 
        cout << "late to arrive on the world sceneand short - lived in comparison to dwarves," << endl;
        cout << "elves, and dragons. Perhaps it is because of their shorter lives that they strive" << endl;
        cout << "to achieve as much as they can in the years they are given. Or maybe they feel" << endl; 
        cout << "they have something to prove to the elder races, and that’s why they build their" << endl; 
        cout << "mighty empires on the foundation of conquestand trade. Whatever drives them," << endl; 
        cout << "humans are the innovators, the achievers, and the pioneers of the worlds." << endl;
        cout << endl;
        cout << "Do you confirm your choice? (y/n) --> ";
        cin >> confirm_choice;
        if (confirm_choice == 'n')
        {
            system("cls");
            choose_race();
        }
        else
        {
            return "Human";
        }
        break;
    case 2:
        cout << endl;
        cout << "Kingdoms rich in ancient grandeur, halls carved into the roots of mountains," << endl;
        cout << "the echoing of picks and hammers in deep mines and blazing forges, a commitment" << endl;
        cout << "to clan and tradition, and a burning hatred of goblins and orcs — these common" << endl;
        cout << "threads unite all dwarves." << endl;
        cout << "Bold and hardy, dwarves are known as skilled warriors, miners, and workers" << endl;
        cout << "of stone and metal. Though they stand well under 5 feet tall, dwarves are so broad" << endl;
        cout << "and compact that they can weigh as much as a human standing nearly two feet taller." << endl;
        cout << "Their courage and endurance are also easily a match for any of the larger folk." << endl;
        cout << endl;
        cout << "Do you confirm your choice? (y/n) --> ";
        cin >> confirm_choice;
        if (confirm_choice == 'n')
        {
            system("cls");
            choose_race();
        }
        else
        {
            return "Dwarf";
        }
        break;
    case 3:
        cout << endl;
        cout << "Elves are a magical people of otherworldly grace, living in the world but not entirely" << endl;
        cout << "part of it. They live in places of ethereal beauty, in the midst of ancient forests" << endl;
        cout << "or in silvery spires glittering with faerie light, where soft music drifts through" << endl;
        cout << "the air and gentle fragrances waft on the breeze. Elves love nature and magic, art" << endl;
        cout << "and artistry, music and poetry, and the good things of the world." << endl;
        cout << "With their unearthly grace and fine features, elves appear hauntingly beautiful to" << endl;
        cout << "humans and members of many other races. They are slightly shorter than humans on average," << endl;
        cout << "ranging from well under 5 feet tall to just over 6 feet. They are more slender than humans," << endl;
        cout << "weighing only 100 to 145 pounds. Males and females are about the same height, and males" << endl;
        cout << "are only marginally heavier than females." << endl;
        cout << endl;
        cout << "Do you confirm your choice? (y/n) --> ";
        cin >> confirm_choice;
        if (confirm_choice == 'n')
        {
            system("cls");
            choose_race();
        }
        else
        {
            return "Elf";
        }
        break;
    default:
        cout << "Wrong input. Try again!" << endl; 
        system("cls"); 
        choose_race();
    }
}

const char* choose_character_class()
{
    int character_class{};
    char confirm_choice{ 'n' };

    cout << "Select your character class: " << endl;
    cout << endl;
    cout << "[1] Fighter" << endl;
    cout << "[2] Mage" << endl;
    cout << "[3] Rogue" << endl;
    cout << endl;
    cout << "Selection: ";
    cin >> character_class;
    switch (character_class)
    {
    case 1:
        cout << endl;
        cout << "Some take up arms for glory, wealth, or revenge. Others do battle to prove themselves, to" << endl;
        cout << "protect others, or because they know nothing else. Still others learn the ways of weaponcraft to" << endl;
        cout << "hone their bodies in battle and prove their mettle in the forge of war. Lords of the battlefield," << endl;
        cout << "fighters are a disparate lot, training with many weapons or just one, perfecting the uses of" << endl;
        cout << "armor, learning the fighting techniques of exotic masters, and studying the art of combat, all to" << endl;
        cout << "shape themselves into living weapons. Far more than mere thugs, these skilled warriors reveal" << endl;
        cout << "the true deadliness of their weapons, turning hunks of metal into arms capable of taming" << endl;
        cout << "kingdoms, slaughtering monsters, and rousing the hearts of armies. Soldiers, knights, hunters," << endl;
        cout << "and artists of war, fighters are unparalleled champions, and woe to those who dare stand" << endl;
        cout << "against them." << endl;
        cout << endl;
        cout << "ROLE: Fighters excel at combat—defeating their enemies, controlling the flow of battle, and" << endl;
        cout << "surviving such sorties themselves. While their specific weapons and methods grant them a wide" << endl;
        cout << "variety of tactics, few can match fighters for sheer battle prowess." << endl;
        cout << endl;
        cout << "PREFERRED ATTRIBUTES: Strength / Constitution" << endl;
        cout << endl;
        cout << "Do you confirm your choice? (y/n) --> ";
        cin >> confirm_choice;
        if (confirm_choice == 'n')
        {
            system("cls");
            choose_character_class();
        }
        else
        {
            return "Fighter";
        }
        break;
    case 2:
        cout << endl;
        cout << "Beyond the veil of the mundane hide the secrets of absolute power. The works of beings beyond" << endl;
        cout << "mortals, the legends of realms where gods and spirits tread, the lore of creations both wondrous" << endl;
        cout << "and terrible—such mysteries call to those with the ambition and the intellect to rise above the" << endl;
        cout << "common folk to grasp true might. Such is the path of the wizard. These shrewd magic-users" << endl;
        cout << "seek, collect, and covet esoteric knowledge, drawing on cultic arts to work wonders beyond the" << endl;
        cout << "abilities of mere mortals. While some might choose a particular field of magical study and" << endl;
        cout << "become masters of such powers, others embrace versatility, reveling in the unbounded wonders" << endl;
        cout << "of all magic. In either case, wizards prove a cunning and potent lot, capable of smiting their foes," << endl;
        cout << "empowering their allies, and shaping the world to their every desire." << endl;
        cout << endl;
        cout << "ROLE: While universalist wizards might study to prepare themselves for any manner of danger," << endl;
        cout << "specialist wizards research schools of magic that make them exceptionally skilled within a" << endl;
        cout << "specific focus. Yet no matter their specialty, all wizards are masters of the impossible and can aid" << endl;
        cout << "their allies in overcoming any danger." << endl;
        cout << endl;
        cout << "PREFERRED ATTRIBUTES: Intellect" << endl;
        cout << endl;
        cout << "Do you confirm your choice? (y/n) --> ";
        cin >> confirm_choice;
        if (confirm_choice == 'n')
        {
            system("cls");
            choose_character_class();
        }
        else
        {
            return "Mage";
        }
        break;
    case 3:
        cout << endl;
        cout << "Life is an endless adventure for those who live by their wits. Ever just one step ahead of danger," << endl;
        cout << "rogues bank on their cunning, skill, and charm to bend fate to their favor. Never knowing what to" << endl;
        cout << "expect, they prepare for everything, becoming masters of a wide variety of skills, training" << endl;
        cout << "themselves to be adept manipulators, agile acrobats, shadowy stalkers, or masters of any of" << endl;
        cout << "dozens of other professions or talents. Thieves and gamblers, fast talkers and diplomats, bandits" << endl;
        cout << "and bounty hunters, and explorers and investigators all might be considered rogues, as well as" << endl;
        cout << "countless other professions that rely upon wits, prowess, or luck. Although many rogues favor" << endl;
        cout << "cities and the innumerable opportunities of civilization, some embrace lives on the road," << endl;
        cout << "journeying far, meeting exotic people, and facing fantastic danger in pursuit of equally fantastic" << endl;
        cout << "riches. In the end, any who desire to shape their fates and live life on their own terms might" << endl;
        cout << "come to be called rogues." << endl;
        cout << endl;
        cout << "ROLE: Rogues excel at moving about unseen and catching foes unaware, and tend to avoid head-" << endl;
        cout << "to-head combat. Their varied skills and abilities allow them to be highly versatile, with great" << endl;
        cout << "variations in expertise existing between different rogues. Most, however, excel in overcoming" << endl;
        cout << "hindrances of all types, from unlocking doors and disarming traps to outwitting magical hazards" << endl;
        cout << "and conning dull-witted opponents." << endl;
        cout << endl;
        cout << "PREFERRED ATTRIBUTES: Dexterity / Wisdom" << endl;
        cout << endl;
        cout << "Do you confirm your choice? (y/n) --> ";
        cin >> confirm_choice;
        if (confirm_choice == 'n')
        {
            system("cls");
            choose_character_class();
        }
        else
        {
            return "Rogue";
        }
        break;
    default:
        cout << "Wrong input. Try again!" << endl;
        system("cls");
        choose_character_class();
    }
}

bool define_strength(int& strength, int& unassigned_points)
{
    int temp_strength{};
    char confirm_new_value{};
    int required_points{ 0 };

    cout << "Target strength (Min value is 7 / Max value is 18): ";
    cin >> temp_strength;
    cout << endl;

    if (temp_strength < 7 || temp_strength > 18)
    {
        return false;
    }

    while (confirm_new_value == 0)
    {
        if (temp_strength > strength)
        {
            for (int i = strength; i < temp_strength; i++)
            {
                if (i < 13)
                {
                    required_points++;
                }
                else
                {
                    if (i >= 13 && i < 15)
                    {
                        required_points += 2;
                    }
                    else
                    {
                        if (i >= 15 && i < 17)
                        {
                            required_points += 3;
                        }
                        else
                        {
                            required_points += 4;
                        }
                    }
                }
            }
        }
        else
        {
            if (temp_strength < strength)
            {
                for (int i = strength; i > temp_strength; i--)
                {
                    if (i==18)
                    {
                        required_points -= 4;
                    }
                    else
                    {
                        if (i<18 && i>=16)
                        {
                            required_points -= 3;
                        }
                        else
                        {
                            if (i<16 && i>=14)
                            {
                                required_points -= 2;
                            }
                            else
                            {
                                if (i<14 && i>10)
                                {
                                    required_points--;
                                }
                                else
                                {
                                    if (i==10)
                                    {
                                        required_points--;
                                    }
                                    else
                                    {
                                        if (i==9)
                                        {
                                            required_points -= 2;
                                        }
                                        else
                                        {
                                            required_points -= 4;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                return true;
            }
        }
        if (required_points > unassigned_points)
        {
            cout << "Not enough free points. Try again." << endl;
            return false;
        }
        else
        {
            cout << "Assigning this value requires " << required_points << " points." << endl;
            cout << "Do you want to proceed? Y/N --> ";
            cin >> confirm_new_value;
            cout << endl;
            switch (confirm_new_value)
            {
            case 'Y':
                unassigned_points -= required_points;
                strength = temp_strength;
                return true;
            case 'y':
                unassigned_points -= required_points;
                strength = temp_strength;
                return true;
            case 'N':
                cout << "Try again." << endl;
                return false;
            case 'n':
                cout << "Try again." << endl;
                return false;
            default:
                cout << "Wrong input. Try again." << endl;
                return false;
                break;
            }
        }
    }
}

bool define_dexterity(int& dexterity, int& unassigned_points)
{
    int temp_dexterity{};
    char confirm_new_value{};
    int required_points{ 0 };

    cout << "Target dexterity (Min value is 7 / Max value is 18): ";
    cin >> temp_dexterity;
    cout << endl;

    if (temp_dexterity < 7 || temp_dexterity > 18)
    {
        return false;
    }

    while (confirm_new_value == 0)
    {
        if (temp_dexterity > dexterity)
        {
            for (int i = dexterity; i < temp_dexterity; i++)
            {
                if (i < 13)
                {
                    required_points++;
                }
                else
                {
                    if (i >= 13 && i < 15)
                    {
                        required_points += 2;
                    }
                    else
                    {
                        if (i >= 15 && i < 17)
                        {
                            required_points += 3;
                        }
                        else
                        {
                            required_points += 4;
                        }
                    }
                }
            }
        }
        else
        {
            if (temp_dexterity < dexterity)
            {
                for (int i = dexterity; i > temp_dexterity; i--)
                {
                    if (i == 18)
                    {
                        required_points -= 4;
                    }
                    else
                    {
                        if (i < 18 && i >= 16)
                        {
                            required_points -= 3;
                        }
                        else
                        {
                            if (i < 16 && i >= 14)
                            {
                                required_points -= 2;
                            }
                            else
                            {
                                if (i < 14 && i>10)
                                {
                                    required_points--;
                                }
                                else
                                {
                                    if (i == 10)
                                    {
                                        required_points--;
                                    }
                                    else
                                    {
                                        if (i == 9)
                                        {
                                            required_points -= 2;
                                        }
                                        else
                                        {
                                            required_points -= 4;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                return true;
            }
        }
        if (required_points > unassigned_points)
        {
            cout << "Not enough free points. Try again." << endl;
            return false;
        }
        else
        {
            cout << "Assigning this value requires " << required_points << " points." << endl;
            cout << "Do you want to proceed? Y/N --> ";
            cin >> confirm_new_value;
            cout << endl;
            switch (confirm_new_value)
            {
            case 'Y':
                unassigned_points -= required_points;
                dexterity = temp_dexterity;
                return true;
            case 'y':
                unassigned_points -= required_points;
                dexterity = temp_dexterity;
                return true;
            case 'N':
                cout << "Try again." << endl;
                return false;
            case 'n':
                cout << "Try again." << endl;
                return false;
            default:
                cout << "Wrong input. Try again." << endl;
                return false;
                break;
            }
        }
    }
}

bool define_constitution(int& constitution, int& unassigned_points)
{
    int temp_constitution{};
    char confirm_new_value{};
    int required_points{ 0 };

    cout << "Target constitution (Min value is 7 / Max value is 18): ";
    cin >> temp_constitution;
    cout << endl;

    if (temp_constitution < 7 || temp_constitution > 18)
    {
        return false;
    }

    while (confirm_new_value == 0)
    {
        if (temp_constitution > constitution)
        {
            for (int i = constitution; i < temp_constitution; i++)
            {
                if (i < 13)
                {
                    required_points++;
                }
                else
                {
                    if (i >= 13 && i < 15)
                    {
                        required_points += 2;
                    }
                    else
                    {
                        if (i >= 15 && i < 17)
                        {
                            required_points += 3;
                        }
                        else
                        {
                            required_points += 4;
                        }
                    }
                }
            }
        }
        else
        {
            if (temp_constitution < constitution)
            {
                for (int i = constitution; i > temp_constitution; i--)
                {
                    if (i == 18)
                    {
                        required_points -= 4;
                    }
                    else
                    {
                        if (i < 18 && i >= 16)
                        {
                            required_points -= 3;
                        }
                        else
                        {
                            if (i < 16 && i >= 14)
                            {
                                required_points -= 2;
                            }
                            else
                            {
                                if (i < 14 && i>10)
                                {
                                    required_points--;
                                }
                                else
                                {
                                    if (i == 10)
                                    {
                                        required_points--;
                                    }
                                    else
                                    {
                                        if (i == 9)
                                        {
                                            required_points -= 2;
                                        }
                                        else
                                        {
                                            required_points -= 4;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                return true;
            }
        }
        if (required_points > unassigned_points)
        {
            cout << "Not enough free points. Try again." << endl;
            return false;
        }
        else
        {
            cout << "Assigning this value requires " << required_points << " points." << endl;
            cout << "Do you want to proceed? Y/N --> ";
            cin >> confirm_new_value;
            cout << endl;
            switch (confirm_new_value)
            {
            case 'Y':
                unassigned_points -= required_points;
                constitution = temp_constitution;
                return true;
            case 'y':
                unassigned_points -= required_points;
                constitution = temp_constitution;
                return true;
            case 'N':
                cout << "Try again." << endl;
                return false;
            case 'n':
                cout << "Try again." << endl;
                return false;
            default:
                cout << "Wrong input. Try again." << endl;
                return false;
                break;
            }
        }
    }
}

bool define_intellect(int& intellect, int& unassigned_points)
{
    int temp_intellect{};
    char confirm_new_value{};
    int required_points{ 0 };

    cout << "Target intellect (Min value is 7 / Max value is 18): ";
    cin >> temp_intellect;
    cout << endl;

    if (temp_intellect < 7 || temp_intellect > 18)
    {
        return false;
    }

    while (confirm_new_value == 0)
    {
        if (temp_intellect > intellect)
        {
            for (int i = intellect; i < temp_intellect; i++)
            {
                if (i < 13)
                {
                    required_points++;
                }
                else
                {
                    if (i >= 13 && i < 15)
                    {
                        required_points += 2;
                    }
                    else
                    {
                        if (i >= 15 && i < 17)
                        {
                            required_points += 3;
                        }
                        else
                        {
                            required_points += 4;
                        }
                    }
                }
            }
        }
        else
        {
            if (temp_intellect < intellect)
            {
                for (int i = intellect; i > temp_intellect; i--)
                {
                    if (i == 18)
                    {
                        required_points -= 4;
                    }
                    else
                    {
                        if (i < 18 && i >= 16)
                        {
                            required_points -= 3;
                        }
                        else
                        {
                            if (i < 16 && i >= 14)
                            {
                                required_points -= 2;
                            }
                            else
                            {
                                if (i < 14 && i>10)
                                {
                                    required_points--;
                                }
                                else
                                {
                                    if (i == 10)
                                    {
                                        required_points--;
                                    }
                                    else
                                    {
                                        if (i == 9)
                                        {
                                            required_points -= 2;
                                        }
                                        else
                                        {
                                            required_points -= 4;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                return true;
            }
        }
        if (required_points > unassigned_points)
        {
            cout << "Not enough free points. Try again." << endl;
            return false;
        }
        else
        {
            cout << "Assigning this value requires " << required_points << " points." << endl;
            cout << "Do you want to proceed? Y/N --> ";
            cin >> confirm_new_value;
            cout << endl;
            switch (confirm_new_value)
            {
            case 'Y':
                unassigned_points -= required_points;
                intellect = temp_intellect;
                return true;
            case 'y':
                unassigned_points -= required_points;
                intellect = temp_intellect;
                return true;
            case 'N':
                cout << "Try again." << endl;
                return false;
            case 'n':
                cout << "Try again." << endl;
                return false;
            default:
                cout << "Wrong input. Try again." << endl;
                return false;
                break;
            }
        }
    }
}

bool define_wisdom(int& wisdom, int& unassigned_points)
{
    int temp_wisdom{};
    char confirm_new_value{};
    int required_points{ 0 };

    cout << "Target wisdom (Min value is 7 / Max value is 18): ";
    cin >> temp_wisdom;
    cout << endl;

    if (temp_wisdom < 7 || temp_wisdom > 18)
    {
        return false;
    }

    while (confirm_new_value == 0)
    {
        if (temp_wisdom > wisdom)
        {
            for (int i = wisdom; i < temp_wisdom; i++)
            {
                if (i < 13)
                {
                    required_points++;
                }
                else
                {
                    if (i >= 13 && i < 15)
                    {
                        required_points += 2;
                    }
                    else
                    {
                        if (i >= 15 && i < 17)
                        {
                            required_points += 3;
                        }
                        else
                        {
                            required_points += 4;
                        }
                    }
                }
            }
        }
        else
        {
            if (temp_wisdom < wisdom)
            {
                for (int i = wisdom; i > temp_wisdom; i--)
                {
                    if (i == 18)
                    {
                        required_points -= 4;
                    }
                    else
                    {
                        if (i < 18 && i >= 16)
                        {
                            required_points -= 3;
                        }
                        else
                        {
                            if (i < 16 && i >= 14)
                            {
                                required_points -= 2;
                            }
                            else
                            {
                                if (i < 14 && i>10)
                                {
                                    required_points--;
                                }
                                else
                                {
                                    if (i == 10)
                                    {
                                        required_points--;
                                    }
                                    else
                                    {
                                        if (i == 9)
                                        {
                                            required_points -= 2;
                                        }
                                        else
                                        {
                                            required_points -= 4;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                return true;
            }
        }
        if (required_points > unassigned_points)
        {
            cout << "Not enough free points. Try again." << endl;
            return false;
        }
        else
        {
            cout << "Assigning this value requires " << required_points << " points." << endl;
            cout << "Do you want to proceed? Y/N --> ";
            cin >> confirm_new_value;
            cout << endl;
            switch (confirm_new_value)
            {
            case 'Y':
                unassigned_points -= required_points;
                wisdom = temp_wisdom;
                return true;
            case 'y':
                unassigned_points -= required_points;
                wisdom = temp_wisdom;
                return true;
            case 'N':
                cout << "Try again." << endl;
                return false;
            case 'n':
                cout << "Try again." << endl;
                return false;
            default:
                cout << "Wrong input. Try again." << endl;
                return false;
                break;
            }
        }
    }
}

bool define_charisma(int& charisma, int& unassigned_points)
{
    int temp_charisma{};
    char confirm_new_value{};
    int required_points{ 0 };

    cout << "Target charisma (Min value is 7 / Max value is 18): ";
    cin >> temp_charisma;
    cout << endl;
    
    if (temp_charisma < 7 || temp_charisma > 18)
    {
        return false;
    }

    while (confirm_new_value == 0)
    {
        if (temp_charisma > charisma)
        {
            for (int i = charisma; i < temp_charisma; i++)
            {
                if (i < 13)
                {
                    required_points++;
                }
                else
                {
                    if (i >= 13 && i < 15)
                    {
                        required_points += 2;
                    }
                    else
                    {
                        if (i >= 15 && i < 17)
                        {
                            required_points += 3;
                        }
                        else
                        {
                            required_points += 4;
                        }
                    }
                }
            }
        }
        else
        {
            if (temp_charisma < charisma)
            {
                for (int i = charisma; i > temp_charisma; i--)
                {
                    if (i == 18)
                    {
                        required_points -= 4;
                    }
                    else
                    {
                        if (i < 18 && i >= 16)
                        {
                            required_points -= 3;
                        }
                        else
                        {
                            if (i < 16 && i >= 14)
                            {
                                required_points -= 2;
                            }
                            else
                            {
                                if (i < 14 && i>10)
                                {
                                    required_points--;
                                }
                                else
                                {
                                    if (i == 10)
                                    {
                                        required_points--;
                                    }
                                    else
                                    {
                                        if (i == 9)
                                        {
                                            required_points -= 2;
                                        }
                                        else
                                        {
                                            required_points -= 4;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                return true;
            }
        }
        if (required_points > unassigned_points)
        {
            cout << "Not enough free points. Try again." << endl;
            return false;
        }
        else
        {
            cout << "Assigning this value requires " << required_points << " points." << endl;
            cout << "Do you want to proceed? Y/N --> ";
            cin >> confirm_new_value;
            cout << endl;
            switch (confirm_new_value)
            {
            case 'Y':
                unassigned_points -= required_points;
                charisma = temp_charisma;
                return true;
            case 'y':
                unassigned_points -= required_points;
                charisma = temp_charisma;
                return true;
            case 'N':
                cout << "Try again." << endl;
                return false;
            case 'n':
                cout << "Try again." << endl;
                return false;
            default:
                cout << "Wrong input. Try again." << endl;
                return false;
                break;
            }
        }
    }
}

bool race_stats_mod(int race, int& strength, int& dexterity, int& constitution, int& intellect, int& wisdom, int& charisma)
{
    int selection{};
    switch (race)
    {
    case 1:
        cout << "As a human you can choose witch attribute will benefit from a +2 bonus." << endl;
        cout << endl;
        cout << "Select witch attribute you want to improve:" << endl;
        cout << endl;
        cout << "[1] Strength" << endl;
        cout << "[2] Dexterity" << endl;
        cout << "[3] Constitution" << endl;
        cout << "[4] Intellect" << endl;
        cout << "[5] Wisdom" << endl;
        cout << "[6] Charisma" << endl;
        cout << endl;
        cout << "Selection: ";
        cin >> selection;
        switch (selection)
        {
        case 1: strength += 2; return true;
        case 2: dexterity += 2; return true;
        case 3: constitution += 2; return true;
        case 4: intellect += 2; return true;
        case 5: wisdom += 2; return true;
        case 6: charisma += 2; return true;
        default:
            cout << "Wrong selection. Try again." << endl;
            cout << endl;
            return false;
        }
    case 2:
        constitution = wisdom += 2;
        charisma -= 2;
        return true;
    case 3:
        dexterity = intellect += 2;
        constitution -= 2;
        return true;
    default:
        cout << "Wrong selection. Try again." << endl;
        cout << endl;
        return false;
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Welcome to Code Review! To the best of your knowledge- does the code function as expected? And it would benefit reviewers to have a bit more information about the code in the description. From the help center page How to ask: "You will get more insightful reviews if you not only provide your code, but also give an explanation of what it does. The more detail, the better." \$\endgroup\$ Sep 28, 2021 at 15:16
  • \$\begingroup\$ Hi Sam, thanks for your reply :) Yes up to now the code works as intended, at least this is what I can say based on many different case testing I did \$\endgroup\$ Sep 28, 2021 at 15:26

4 Answers 4

5
\$\begingroup\$

use else if

          if (i < 13)
          {
              required_points++;
          }
          else
          {
              if (i >= 13 && i < 15)
              {
                  required_points += 2;
              }
              else
              {
                  if (i >= 15 && i < 17)
                  {
                      required_points += 3;
                  }
                  else
                  {
                      required_points += 4;
                  }
              }
          }

Chains of logic like this can be simplified using else if. Each condition is checked in turn, so we can do the following:

            if (i < 13)
            {
                required_points++;
            }
            else if (i < 15)
            {
                required_points += 2;
            }
            else if (i < 17)
            {
                required_points += 3;
            }
            else
            {
                required_points += 4;
            }

remove duplicate code

There seems to be very little difference between the various define_[stat] functions (at a glance, it looks like only the stat name is different?). Consider passing the stat name into the function as a std::string. That way we could define a single function, and call it for every stat:

bool define_stat(std::string const& name, int& stat, int& unassigned_points);

...

selection_success = define_stat("strength", strength, unsassigned_points);
selection_success = define_stat("wisdom", wisdom, unsassigned_points);

It's much easier to maintain 1 function, instead of 6. :)

\$\endgroup\$
5
\$\begingroup\$

Don’t write using namespace std;.

You can, however, in a CPP file (not H file) or inside a function put individual using std::string; etc. (See SF.7.)


⧺SL.io.50 Don't use endl.


My first impression is that you have a bunch of free functions, not a class. And those parameters are all primitive types, and passed by non-const reference. I'm guessing those similarly-named parameters should be data members instead of in/out parameters.

// GENERAL VARIABLE INIZIALIZATION

    int level{ 1 };
    int experience{ 0 };
    int strength{ 10 }, dexterity{ 10 }, constitution{ 10 }, intellect{ 10 }, wisdom{ 10 }, charisma{ 10 };

Those should probably be the various data members of your class.

Don't put more than one variable (or class data member) declaration in a statement.

So, put a class around this, making these the data members. Then remove all the similarly-named parameters from the functions, as they will all be able to access the members of the class directly. This is basic Object Oriented principles.


vector<vector<int>> ability_score_modifier(45, vector<int>(2));
You never call push_back but simply pre-define the vector of vectors to a fixed size, and then access those elements. This is inefficient and is not making use of the main feature of vector, but you suffer additional indirection and heap memory allocation.

Normally you have objects or complex types and don't have empty elements ready to be changed; rather, an element doesn't exist at all until you add it. Here you just have int values so that is not important. So, just use a 2D primitive built-in array.

ability_score_modifier.at(0).at(0) = 1;
It's strange that you are using at rather than the more usual [] notation. Since you are using fixed bounds, even constant subscripts in this example, the difference in error checking is not a reason for it.


string race;
race = choose_race();

You almost had it! You didn't declare race until you were ready to use it, but then you broke it up into two statements. initialize your variables when you declare them. And, use const whenever you can. Try:

const string race = choose_race();


system("cls");
This is non-portable and slow since it launches another program to clear the screen! Generally, don't do that as part of a TTY-style program; the user can clear the screen before launching the program if/when he wants.


    int race_to_integer{};
    if (race == "Human")
    {
        race_to_integer = 1;
    }
    else
    {
        if (race == "Dwarf")
        {
            race_to_integer = 2;
        }
        else
        {
            if (race == "Elf")
            {
                race_to_integer = 3;
            }
        }
    }

This block should be in its own function at the very least.
It should be part of the get-race stuff, not farther down in the main code.
The races should be an enumeration type, not an int.

Something like:

enum race_type { Human, Dwarf, Elf };
   ⋮
race_type parse_race (string_view in)
{
    if (in=="Human")  return Human;
    if (in=="Dwarf")  return Dwarf;
    if (in=="Elf")  return Elf;
    throw /* something */ ... ;
}
   ⋮
    /* in another function */
    const string race_name= choose_race();
    const auto race = parse_race(race_name);

Note that the actual code doesn't have nested nested nested comparisons. It is flat: code to the left.

Learn to recognise a cohesive block of code that should be put into its own function. You can draw a circle around this and it doesn't interact with anything else.


case 1:
            check_if_selection_success = false;
            do
            {
                check_if_selection_success = define_strength(strength, unassigned_points);
            } while (check_if_selection_success == false);
            cout << endl;
            break;

These cases are almost all the same. Hoist the looping out to a higher level, and have the menu only call one of the functions.

Since the functions, as previously mentioned, should be member functions, you don't need to pass parameters and the menu code becomes simple and compact.


for (int i = 0; i < 45; i++)
    {
        if (strength == ability_score_modifier.at(i).at(0))
        {
            strength_mod = ability_score_modifier.at(i).at(1);
        }
    }

You have this nearly identical block 6 times in a row! Don't Repeat Yourself.

You are also indexing the same place twice, which is wasteful and hard to read. In a generic sense, you have:

for (const auto& row : ability_score_modifier) {
    if (THING == row[0])  THING_mod= row[1];
}

This can be put into a function with the marked placeholders (the only part that changes) becoming parameters. This is the real purpose of parameters in functions: you have a one-to-one correspondence between the final home of the data and the function parameters, which is not very useful.

But in this case, I think you can just put all 6 THINGs inside the same loop.

I'm really not understanding what your data structure is all about here. There might be a better way to represent it, based on an understanding of the problem.

\$\endgroup\$
1
\$\begingroup\$

There is really just too much to go through here. I would suggest you first create classes for the basics. Consider the following code:

class Race
{
public:
    Race(std::string name, std::string message)
        : name{ name }, 
          message{ message }
    {
    }

public:
    std::string name;
    std::string message;
};

class Game
{
private:
    std::vector<Race> races;

public:
    Game() {
        races.emplace_back("Human", "In the reckonings of most worlds, humans...");
        races.emplace_back("Dwarf", "Kingdoms rich in ancient grandeur, halls carved...");
        races.emplace_back("Elf", "Elves are a magical people of otherworldly...");
    }

    int chooseRace() {
        std::string errorMessage;
        int raceNumber = -1;
        while (raceNumber == -1)
        {
            if (errorMessage.empty() == false) {
                std::cout << errorMessage << "\n\n";
                errorMessage.clear();
            }

            std::cout << "Select your character race: \n\n";
            int index = 0;
            for (auto race : races)
                std::cout << "[" << ++index << "] " << race.name << "\n";
            std::cout << "\n";

            int number;
            std::cin >> number;
            --number;
            if (number < 0 || number >= races.size())
            {
                errorMessage = "Invalid number";
                continue;
            }
            Race& race = races[number];
            std::cout << race.message << "\n\n";

            std::cout << "Do you confirm your choice? (y/n) --> ";
            char confirm_choice;
            std::cin >> confirm_choice;
            if (confirm_choice == 'y' || confirm_choice == 'Y') {
                raceNumber = number;
            }
            else {
                continue;
            }
        }
        return raceNumber;
    }
};

int main() {
    Game game;
    int number = game.chooseRace();
}

Adding a new type of 'Race' will be as simple as:

    races.emplace_back("Pigmen", "Pigmen belong to Minceraft...");
\$\endgroup\$
2
  • \$\begingroup\$ Good point about making a class, since OP mentioned wanting to learn OOP. However, I would avoid starting member variables with an underscore (see this post). I would also write races.emplace_back(1, "Human, "...") instead of using push_back(), or even better: static const std::array<Race> races = {{1, "Human", "..."}, {2, "Dwarf", "..."}, ...}; If they are in a vector or array anyway, then _number seems a bit superfluous as well. \$\endgroup\$
    – G. Sliepen
    Sep 28, 2021 at 19:35
  • \$\begingroup\$ @G.Sliepen, I have updated my code based on your comments. Do you have any other suggestions, especially about the Race constructor? \$\endgroup\$
    – jdt
    Sep 29, 2021 at 12:25
0
\$\begingroup\$

Few words from me.

  1. I would suggest to extract large texts into separate text files or resources. In you code you just need to load them and simply write on standard output. Here is an examle (codegrepper.com). I would also suggest to extract smaller text messages into constants.

  2. Somebody already mentioned about duplicated code -please do refactor it first. After that, the switch/case conditions could be simplified too. Consider converting a character from user input into lowercase first.

  3. main function -please split into smaller subroutines with meaningful names informing what this step really does (their names usually can replace comments in code).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.