I'm making a terminal-like program (to calculate currency) with custom commands as input but I have a problem.
Every time I implement a new command, I have to add a new else if statement. This wouldn't be a problem but for a terminal-like program there can be a lot of commands.
Here is my code:
#include <iostream>
#include <string>
#include <Windows.h>
#include <math.h>
float user_balance = 0.0f;
float eur_in_czk = 24.0f; //ammount of czk in single euro
std::string currency = "czk";
bool czk_to_eur_enabled = true;
bool eur_to_czk_enabled = false;
//------------------START method definition---------------------------------------------------------
void czk_to_eur()
{
if (czk_to_eur_enabled) //to prevent using twice in a row
{
user_balance /= eur_in_czk;
user_balance = floorf(user_balance * 100) / 100; //limit to two decimal numbers
currency = "eur";
czk_to_eur_enabled = false;
eur_to_czk_enabled = true;
}
else
{
std::cout << "Your savings are already converted to " << currency << "!" << std::endl;
}
}
void eur_to_czk()
{
if (eur_to_czk_enabled) //to prevent using twice in a row
{
user_balance *= eur_in_czk;
user_balance = floorf(user_balance * 100) / 100; //limit to two decimal numbers
currency = "czk";
eur_to_czk_enabled = false;
czk_to_eur_enabled = true;
}
else
{
std::cout << "Your savings are already converted to " << currency << "!" << std::endl;
}
}
void set_balance(float new_balance)
{
user_balance = new_balance;
}
void add_balance(float new_balance)
{
user_balance += new_balance;
}
//------------------END method definition-----------------------------------------------------------
int main()
{
bool main_loop = true; //main loop enabler
float input_money;
std::string user_command = "";
std::cout << "This is currency converter v1.0 (czk to eur and back)\n\n\n" << std::endl;
while (main_loop) //main loop for currency converter
{
std::cout << "Input: ";
std::cin >> user_command;
std::cout << std::endl;
if ((user_command == "setbal") || (user_command == "SETBAL"))
{
std::cout << "Your balance is " << user_balance << " " << currency << ".\n";
std::cout << "Please enter desired value (" << currency << "): ";
std::cin >> input_money;
set_balance(input_money);
std::cout << "\n" << std::endl;
}
else if ((user_command == "addbal") || (user_command == "ADDBAL"))
{
std::cout << "Your balance is " << user_balance << " " << currency << ".\n";
std::cout << "Please enter desired value (" << currency << "): ";
std::cin >> input_money;
add_balance(input_money);
std::cout << "\n" << std::endl;
}
else if ((user_command == "balance") || (user_command == "BALANCE"))
{
std::cout << "Your balance is " << user_balance << " " << currency << "." << std::endl;
}
else if ((user_command == "curstat") || (user_command == "CURSTAT"))
{
std::cout << "Currency status is " << eur_in_czk << " czk in 1 eur." << std::endl;
}
else if ((user_command == "toeur") || (user_command == "TOEUR"))
{
czk_to_eur();
}
else if ((user_command == "toczk") || (user_command == "TOCZK"))
{
eur_to_czk();
}
else if ((user_command == "cheuv") || (user_command == "CHEUV"))
{
std::cout << "Change eur value (" << eur_in_czk << "): ";
std::cin >> eur_in_czk;
std::cout << std::endl;
}
else if ((user_command == "help") || (user_command == "HELP"))
{
std::cout << "SETBAL Sets balance.\n"
<< "ADDBAL Adds balance.\n"
<< "BALANCE Shows current balance.\n"
<< "CURSTAT Shows currency status.\n"
<< "TOEUR Converts czk to eur.\n"
<< "TOCZK Converts eur to czk.\n"
<< "CHEUV Changes eur currency value.\n"
<< "CLS Cleans terminal history.\n"
<< "EXIT Exits program.\n" << std::endl;
}
else if ((user_command == "cls") || (user_command == "CLS"))
{
system("CLS"); //funtion from Windows.h library
}
else if ((user_command == "exit") || (user_command == "EXIT"))
{
main_loop = false;
}
else
{
std::cout << "'" << user_command << "'"
<< "is not recognized as an internal or external command!\n";
std::cout << "Type 'HELP' to see available commands.\n" << std::endl;
}
}
return 0;
}
The bottom part of the code in while cycle is where the problem is.
Everything works fine but I would like to know, if there is any other way. And switch to my knowledge does not support string values as condition/dependency. (also I'm currently not using any custom classes and/or custom header files because this is just experiment.)
Is there any other way to do it?
Normally I would suggest using a std::map with a string as the key and a function as the value so that you could search the map for a command and then invoke the function associated with it. However, since that's already been mentioned in the comments I figured I'd get all fancy and provide a totally wack solution you probably shouldn't use.
This wack solution allows you to use string literals in a switch/case statement. This is possible by taking advantage of a feature of modern C++ called user defined literals that allow you to produce objects of user-defined type by defining a user-defined suffix much in the same way you append U to a integer literal to specify an unsigned value.
The first thing we'll do is define a user defined literal that produces a hash value that is calculated at compile time. Since this generates a hash value from the string it is possible to encounter collisions but that's dependant on the quality of the hash algorithm used. For our example we're going to use something simple. This following snippet defines a string literal with the suffix _C that generates our hash.
constexpr uint32_t djb2Hash(const char* str, int index = 0)
{
return !str[index]
? 0x1505
: (djb2Hash(str, index + 1) * 0x21) ^ str[index];
}
// Create a literal type for short-hand case strings
constexpr uint32_t operator"" _C(const char str[], size_t /*size*/)
{
return djb2Hash(str);
}
Now every time the compiler sees a string literal in the format of "Hello World"_C it will produce a hash value and use that in place of the string.
Now we'll apply this to your existing code. First we'll separate the code that takes the user command from cin and make the given command all lower case.
std::string get_command()
{
std::cout << "Input: ";
std::string user_command;
std::cin >> user_command;
std::cout << std::endl;
std::transform(
user_command.begin(),
user_command.end(),
user_command.begin(),
[](char ch) { return static_cast<char>(std::tolower(ch)); });
return user_command;
}
There now that we can get an all lowercase command from the user we need to process that so we'll take your original set of if/else statements and turn them into a simple switch/case statement instead. Now since we can't actually use string literals in the switch/case statement we'll have to fudge a little bit and generate the hash value of the users command for the switch part of the code. We'll also take all of your commands and add the _C suffix to them so that the compiler automatically generates our hash values for us.
int main()
{
bool main_loop = true; //main loop enabler
std::cout << "This is currency converter v1.0 (czk to eur and back)\n\n\n" << std::endl;
while (main_loop) //main loop for currency converter
{
const auto user_command(get_command());
switch(djb2Hash(user_command.c_str()))
{
case "setbal"_C:
std::cout << "Set balance command\n";
break;
case "addbal"_C:
std::cout << "Add balance command\n";
break;
case "balance"_C:
std::cout << "Get balance command\n";
break;
case "curstat"_C:
std::cout << "Get current status command\n";
break;
case "help"_C:
std::cout << "Get help command\n";
break;
case "exit"_C:
main_loop = false;
break;
default:
std::cout
<< "'" << user_command << "'"
<< "is not recognized as an internal or external command!\n"
<< "Type 'HELP' to see available commands.\n" << std::endl;
}
}
}
And there you have it. A totally wack solution! Now keep in mind that we're not really using strings in the switch/case statement, we're just hiding most of the details of generating hash values which are then used.
Related
I made up a game called password hacker in C++, purpose is to guess the password through given hints, when I input the correct password, it works correct, and moves to the new level as well.
But it does the same even when I input wrong password as well.
#include <iostream>
void Intro(int Level) {
std::cout << "SUP, this PC is locked\n\n" << "well, sure why not give it a try.... it will all be over soon when you type the password incorrectly.\n" << "type your best code here to break security of server number " << Level;
}
bool PlayGame(int Diff) {
Intro(Diff);
int CodeA = 0;
int CodeB = 1;
int CodeC = 2;
int CodeProduct = CodeA * CodeB * CodeC;
int CodeSum = CodeA + CodeB + CodeC;
std::cout << std::endl;
//Instructions
std::cout << "+ 3 number password" << "\n+The numbers adds up to " << CodeSum << "\n+The numbers multiply up to " << CodeProduct << std::endl;
int PlayerGuessA;
int PlayerGuessB;
int PlayerGuessC;
std::cin >> PlayerGuessA >> PlayerGuessB >> PlayerGuessC;
int PlayerSum = PlayerGuessA + PlayerGuessB + PlayerGuessC;
int PlayerProduct = PlayerGuessA * PlayerGuessB * PlayerGuessC;
std::cout << "You entered:\n" << PlayerGuessA << " " << PlayerGuessB << " " << PlayerGuessC;
std::cout << "\n \n Your numbers multiply up to: " << PlayerProduct;
std::cout << "\n Your numbers add up to: " << PlayerSum;
if (PlayerSum != CodeSum && PlayerProduct != CodeProduct) {
std::cout << std::endl << "Like i said earlier, PATHETIC" << std::endl;
return false;
} else {
std::cout << std::endl << "Well, No shit Sherlock " << std::endl;
return true;
}
}
int main() {
int Lev = 1;
while (true) {
bool bLevelComplete = PlayGame(Lev);
std::cin.clear(); //clears any errors
std::cin.ignore(); //discards buffer
++Lev;
}
return 0;
}
Seems to me like you need to put your level up code within your win condition statement, otherwise you're telling the game to keep going regardless of the outcome.
So make Lev global, take the ++Lev out of the main function and put it in the else statement of PlayGame.
OR
Have an if statement wrapped around the ++Lev that takes the return value of PlayGame as it condition. So,
if(bLevelComplete){
++Lev;
}
I'm trying to follow a C++ class to get more acquainted with the language and Unreal Engine. However, now whenever I start the program, the console window immediately closes. I've already tried fixing the subsystem stuff and I've seen some dirty code that wouldn't close the window, but I'd like to know how to get the problem fixed without trying to find a weird circumnavigation.
/* This is the console executable, that makes use of the BullCowClass
This acts as the view in a MVC pattern, and is responsible for all
user interaction. For game logic see the FBullCow`enter code here`Game class.
*/
#include <iostream>
#include <string>
#include "FBullCowGame.h"
using FText = std::string;
using int32 = int;
void PrintIntro();
void PlayGame();
FText GetValidGuess();
bool AskToPlayAgain();
FBullCowGame BCGame; //instantiatiate a new game
// entry point for our application
int main()
{
bool bPlayAgain = false;
do
{
PrintIntro();
PlayGame();
bPlayAgain = AskToPlayAgain();
} while (bPlayAgain == true);
return 0;
}
void PrintIntro()
{
//introduce to game
std::cout << "Welcome to Bulls and Cows" << std::endl;
std::cout << "Can you guess the " << BCGame.GetHiddenWordLength() << " letter isogram I'm thinking of?\n";
std::cout << std::endl;
return;
}
void PlayGame()
{
BCGame.Reset();
int32 MaxTries = BCGame.GetMaxTries();
//looping for the numbert of turns asking for guesses
for (int32 i = 1; i <= MaxTries; i++) {// TODO change from FOR to WHILE
FText Guess = GetValidGuess();
// submit valid guess to the game and receive counts
FBullCowCount BullCowCount = BCGame.SubmitGuess(Guess);
std::cout << "Bulls = " << BullCowCount.Bulls;
std::cout << ". Cows = " << BullCowCount.Cows << "." << "\n\n";
}
// TODO summarize game here
}
//loop continually until the use gives a valid guess
FText GetValidGuess() // TODO change to GetValidGuess
{
EGuessStatus Status = EGuessStatus::Invalid_Status;
do {
//get a guess from the player
int32 CurrentTry = BCGame.GetCurrentTry();
std::cout << "Try " << CurrentTry << " What is your word guess: ";
FText Guess = "";
std::getline(std::cin, Guess);
Status = BCGame.CheckGuessValidity(Guess);
switch (Status)
{
case EGuessStatus::Wrong_Length:
std::cout << "Please enter a " << BCGame.GetHiddenWordLength() << " letter word.\n";
break;
case EGuessStatus::Not_Isogram:
std::cout << "Please enter an isogram.\n";
break;
case EGuessStatus::Not_Lowercase:
std::cout << "Please enter your guess in lowercase letters.\n";
break;
default:
return Guess;
}
std::cout << std::endl;
{
};
} while (Status != EGuessStatus::OK); //keep looping until we get no errors
}
bool AskToPlayAgain()
{
std::cout << "Do you want to play again(y/n)? ";
FText Response = "";
std::getline(std::cin, Response);
if (Response[0] == 'y')
{
return true;
}
if (Response[0] == 'n')
{
return false;
}
return false;
}
IF you are redirecting your output to a text file then it wont stop after execution. You can check the projectsettings under Debugging Section.
I've been working on a simple console application and was stopped when, upon compiling my latest code, it began outputting strings of text and integers which did not match what I have entered.
The purpose of the program thus far is simple: to input a string of data and for it to output correctly multiple times in the console application. Below I have linked the pseudocode.
Thanks in advance.
#include <iostream>
#include <string>
void printIntro();
void RunApp();
bool RequestRestart();
std::string GetAttempt();
int main() // entry point of the application
{
printIntro();
RunApp();
RequestRestart();
return 0;
}
void printIntro() {
// introduce the program
constexpr int WORD_LENGTH = 8; // constant expression
std::cout << "Welcome to the Bull and Cow guessing game\n";
std::cout << "Can you guess the " << WORD_LENGTH;
std::cout << " letter isogram I am thinking of?\n\n";
return;
}
void RunApp()
{
// loop for number of attempts
constexpr int ATTEMPTS = 5;
for (int count = 1; count <= ATTEMPTS; count++)
{
std::string Attempt = GetAttempt();
std::cout << "You have entered " << GetAttempt << "\n";
std::cout << std::endl;
}
}
std::string GetAttempt()
{
// receive input by player
std::cout << "Enter your guess: \n";
std::string InputAttempt = "";
std::getline(std::cin, InputAttempt);
return InputAttempt;
}
bool RequestRestart()
{
std::cout << "Would you like to play again?\n";
std::string Response = "";
std::getline(std::cin, Response);
std::cout << "Is it y?: \n" << (Response[0] == 'y'); //response must be in brackets
return false;
}
You have to change this line
std::cout << "You have entered " << GetAttempt << "\n";
instd::cout << "You have entered " << Attempt << "\n";
In this way you do not print the address of the function, just like you did before, but the variable in which you stored the return value of the GetAttempt function.
You are printing a pointer to GetAttempt. Instead print Attempt:-
std::cout << "You have entered " << Attempt << "\n";
The boost units library provides useful compile time "units of measure" type checking. It also provides stream io operations to serialize units. However, I am struggling with the string parsing bit.
For example, the following lines:
boost::units::quantity<boost::units::si::force> f(2.0 * boost::units::si::newton);
std::cout << "Force = " << f << std::endl;
produce the output:
Force = 2.0 N
Can someone point me to an example that parses these standard serialization back to boost units?
// f.parse_string("2.0 N"); or using stream operators??
Thanks!
It's not directly supported by the library.
There's an example that could give some inspiration: http://www.boost.org/doc/libs/1_56_0/doc/html/boost_units/Examples.html#boost_units.Examples.RuntimeUnits
This example shows how to implement an interface that allow different
units at runtime while still maintaining type safety for internal
calculations.
namespace {
using namespace boost::units;
using imperial::foot_base_unit;
std::map<std::string, quantity<si::length> > known_units;
}
quantity<si::length> calculate(const quantity<si::length>& t)
{
return(boost::units::hypot(t, 2.0 * si::meters));
}
int main()
{
known_units["meter"] = 1.0 * si::meters;
known_units["centimeter"] = .01 * si::meters;
known_units["foot"] =
conversion_factor(foot_base_unit::unit_type(), si::meter) * si::meter;
std::string output_type("meter");
std::string input;
while((std::cout << "> ") && (std::cin >> input))
{
if(!input.empty() && input[0] == '#')
{
std::getline(std::cin, input);
}
else if(input == "exit")
{
break;
}
else if(input == "help")
{
std::cout << "type \"exit\" to exit\n"
"type \"return 'unit'\" to set the return units\n"
"type \"'number' 'unit'\" to do a simple calculation"
<< std::endl;
}
else if(input == "return")
{
if(std::cin >> input)
{
if(known_units.find(input) != known_units.end())
{
output_type = input;
std::cout << "Done." << std::endl;
}
else
{
std::cout << "Unknown unit \"" << input << "\""
<< std::endl;
}
}
else
{
break;
}
}
else
{
try
{
double value = boost::lexical_cast<double>(input);
if(std::cin >> input)
{
if(known_units.find(input) != known_units.end())
{
std::cout << static_cast<double>(
calculate(value * known_units[input]) /
known_units[output_type])
<< ' ' << output_type << std::endl;
}
else
{
std::cout << "Unknown unit \"" << input << "\""
<< std::endl;
}
}
else
{
break;
}
}
catch(...)
{
std::cout << "Input error" << std::endl;
}
}
}
}
Im new to programming and C++ and I started making a little string type game for fun, which gives the user two options through out the program, but in the final part of the program i cant get it to output a unique option for the final input(makeCure) - which i only want to output at the end not through out the program. Hope Im making sense :/ .Iv tried and tried and tried and the more i try the more probloms I create. Iv shown below in my code where Im sure the problom lies. Any advice would much appreciated.
#include<iostream>
#include<string>
using std::string;
bool intro(void);
void room(bool enemy, bool data, bool cure, string description);
//player stats
string Name = "";
//enemy states
string enemyName = "";
//data stats
string dataName = "";
//Cure - Option in room7 only
string makeCure = "";
//room descriptions(string constructs)
const string room1 = "You enter the first town of infected Zombies.";
const string room2 = "You are overwelmed by Zombies, and plunder into the sewers to escape.";
const string room3 = "You make your way to safety and find yourself in the Central Town Hall.";
const string room4 = "You decide to venture into the local forest to find the finalingrediants";
const string room5 = "You venture further for the final ingrediant, into a nearby Cave.";
const string room6 = "Its time for you to face the Zombie General!!!";
const string room7 = "You work day and Night in the Labs to make the Cure.";
int main(void)
{
if(intro())
return 0;
dataName = "First Ingrediant- Zombie Rags with infected DNA";
enemyName = "Zombie Soldior";
room(true, true, false, room1);
enemyName = "Massive Zombie Rat";
room(true, false, false, room2);
dataName = "Seconed Ingrediant- StemCells";
enemyName = "Mutated Scientists";
room(true, true, false, room3);
dataName = "Third Magic-Mushrooms";
room(false, true, false, room4);
dataName = "Fourth Final Ingrediant - Coffee Beans";
enemyName = "Commander Zombie";
room(true, true, false, room5);
enemyName = "Zombie General";
room(false, true, false, room6);
return 0;
makeCure = "Elixier to Save the World";
room(false, false, true, room7);
return 0;
}
bool intro(void)
{
using std::cout;
using std::cin;
cout << "Brave Soul!!! What is your name?\n";
cin >> Name;
cout << "Ahh... " << Name << " You say.." << "How about Zombie Slayer?.. Good, glad we agree!\n";
cout << "Humanity is in need of your Help, "
<< "The world is being infected by the\n"
<< "ZD1678 ZOMBIE VIRUS \n"
<< "And we need to send you to Cape Town to stop the central spread.\n"
<< "Your task will be tough, but we know you can do it \n"
<< "Will you accept the challenge?\n\n";
cout << "1)Yes. \n"
<< "2)No. \n\n";
int response;
cin >> response;
return !(response ==1);
}
void room(bool enemy, bool data, bool cure, string description)
{
using std::cout;
using std:: cin;
while(true)
{
cout << description.c_str() << "\n\n";
int response = 0;
do
{
cout << "Shall Our Hero continue his Quest?\n";
if(enemy)
cout << "1) Attack the "
<< enemyName.c_str() << "\n";
else if(!enemy)
cout << "1) venture further....";
if(data)
cout << "2)Pick up the "
<< dataName.c_str() << "\n";
cin >> response;
/* Trying to create the last if that only kicks in at room7( string makeCure )
* that displays the option to make the cure
* This is where my Problem is.
* Iv tried anouther if
* and else
* and while and nothing works, its just messes up everything..
* badly
*/
} while(response < 1 || response > 2);
switch(response)
{
case 1:
if(enemy)
{
enemy = !enemy;
cout << "You slay the deadly "
<< enemyName.c_str() << "\n";
}
else if(!enemy)
return;
break;
case 2:
data = !data;
cout << "You pick up the "
<< dataName.c_str() << "\n";
break;
}
}
}
what you probably want to do is dynamically generate a list of possible events each time you write out the list and present it to the user, then you can match the response to the list to get what the user wants to do. like this:
enum EventType
{
ET_Enemy,
ET_Item,
ET_Cure,
ET_Continue,
ET_MAX
};
void room(bool enemy, bool data, bool cure, string description)
{
using std::cout;
using std:: cin;
int currentEventChoices[ET_MAX];
int numEventChoices;
while(true)
{
cout << description.c_str() << "\n\n";
int response = 0;
do
{
numEventChoices = 0;
cout << "Shall Our Hero continue his Quest?\n";
if(enemy)
{
cout << (numEventChoices+1) << ") Attack the "
<< enemyName.c_str() << "\n";
currentEventChoices[numEventChoices] = ET_Enemy;
numEventChoices++;
}
if(data)
{
cout << (numEventChoices+1) << ") Pick up the "
<< dataName.c_str() << "\n";
currentEventChoices[numEventChoices] = ET_Item;
numEventChoices++;
}
if(cure)
{
cout << (numEventChoices+1) << ") cure related string "
<< makeCure.c_str() << "\n";
currentEventChoices[numEventChoices] = ET_Cure;
numEventChoices++;
}
cout << (numEventChoices+1) << ") venture further....\n"; // note if this is only meant to be an option if there is no enemy, put it in an else after the if(enemy)
numEventChoices++;
cin >> response;
} while(response < 1 || response > numEventChoices);
switch(currentEventChoices[response-1])
{
case ET_Enemy:
enemy = !enemy;
cout << "You slay the deadly "
<< enemyName.c_str() << "\n";
break;
case ET_Item:
data = !data;
cout << "You pick up the "
<< dataName.c_str() << "\n";
break;
case ET_Cure:
//do cure stuff
break;
case ET_Continue:
return;
}
}
}
the trouble you are having is that by just using a very static next of if/else statements each time you want to match the option number to the event, it gets very complex and messy, it was fine when there was just the 4 cases of there being an enemy or not, or data or not. but now you are adding another branch with cure, its just got really complex to do it that way.
It's a bit hard to understand what you need, so tell me if it's not what you wanted.
Using braces and indenting consistently can really help with this:
do {
cout << "Shall Our Hero continue his Quest?\n";
if (enemy) {
cout << "1) Attack the " << enemyName << "\n";
} else {
cout << "1) venture further....";
}
if (data) {
cout << "2) Pick up the " << dataName << "\n";
}
if (cure) {
cout << "2) take the " << makeCure << "\n";
}
cin >> response;
} while (response < 1 || response > 2);
and fix "case 2" in the switch part:
case 2:
if (data) {
data = false;
cout << "You pick up the " << dataName << "\n";
} else if (cure) {
// fill in ...
}
break;
Notes:
You can use endl (from std) instead of '\n'. cout << "hello" << endl;
You can pass many of your global variables as arguments, so you won't need them to be global (global is bad, in general).
Much of your game can be be squeeszed into arrays and structs - being "data driven" and "table driven". I don't know if you got there already, but you can try and identify these parts.
if(enemy) ... else if(!enemy) you don't need the !enemy part. it is implied by the else.