diff options
| author | Joseph Williams <[email protected]> | 2022-11-08 14:16:56 -0800 |
|---|---|---|
| committer | Joseph Williams <[email protected]> | 2022-11-08 14:16:56 -0800 |
| commit | c0683aa5f4667ef6fe6f8deeebd5004b1f7989a7 (patch) | |
| tree | 4e5cc1b5fa5937e9ece3c24e5e6dcd58ae0a99be | |
| parent | Setting up GitHub Classroom Feedback (diff) | |
| download | cst116-lab2-allthenamesaretaken3141-c0683aa5f4667ef6fe6f8deeebd5004b1f7989a7.tar.xz cst116-lab2-allthenamesaretaken3141-c0683aa5f4667ef6fe6f8deeebd5004b1f7989a7.zip | |
finished user input functions
| -rw-r--r-- | BlankConsoleLab/BlankConsoleLab.cpp | 16 | ||||
| -rw-r--r-- | BlankConsoleLab/BlankConsoleLab.vcxproj | 13 | ||||
| -rw-r--r-- | BlankConsoleLab/BlankConsoleLab.vcxproj.filters | 7 | ||||
| -rw-r--r-- | BlankConsoleLab/CST116_Lab2_Williams.cpp | 223 | ||||
| -rw-r--r-- | BlankConsoleLab/CST116_Lab2_Williams_Pseudocode.txt | 8 |
5 files changed, 245 insertions, 22 deletions
diff --git a/BlankConsoleLab/BlankConsoleLab.cpp b/BlankConsoleLab/BlankConsoleLab.cpp deleted file mode 100644 index ed5f807..0000000 --- a/BlankConsoleLab/BlankConsoleLab.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// BlankConsoleLab.cpp : This file contains the 'main' function. Program execution begins and ends there. -// - -#include <iostream> - -using namespace std; - -using std::cout; -using std::cin; -using std::endl; - -int main() -{ - cout << "Hello World!\n"; -} - diff --git a/BlankConsoleLab/BlankConsoleLab.vcxproj b/BlankConsoleLab/BlankConsoleLab.vcxproj index db2e734..6ee166d 100644 --- a/BlankConsoleLab/BlankConsoleLab.vcxproj +++ b/BlankConsoleLab/BlankConsoleLab.vcxproj @@ -29,26 +29,26 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v142</PlatformToolset> + <PlatformToolset>v143</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v142</PlatformToolset> + <PlatformToolset>v143</PlatformToolset> <WholeProgramOptimization>true</WholeProgramOptimization> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v142</PlatformToolset> + <PlatformToolset>v143</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v142</PlatformToolset> + <PlatformToolset>v143</PlatformToolset> <WholeProgramOptimization>true</WholeProgramOptimization> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> @@ -139,7 +139,10 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="BlankConsoleLab.cpp" /> + <ClCompile Include="CST116_Lab2_Williams.cpp" /> + </ItemGroup> + <ItemGroup> + <Text Include="CST116_Lab2_Williams_Pseudocode.txt" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/BlankConsoleLab/BlankConsoleLab.vcxproj.filters b/BlankConsoleLab/BlankConsoleLab.vcxproj.filters index aca1dd9..ca64071 100644 --- a/BlankConsoleLab/BlankConsoleLab.vcxproj.filters +++ b/BlankConsoleLab/BlankConsoleLab.vcxproj.filters @@ -15,8 +15,13 @@ </Filter> </ItemGroup> <ItemGroup> - <ClCompile Include="BlankConsoleLab.cpp"> + <ClCompile Include="CST116_Lab2_Williams.cpp"> <Filter>Source Files</Filter> </ClCompile> </ItemGroup> + <ItemGroup> + <Text Include="CST116_Lab2_Williams_Pseudocode.txt"> + <Filter>Source Files</Filter> + </Text> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/BlankConsoleLab/CST116_Lab2_Williams.cpp b/BlankConsoleLab/CST116_Lab2_Williams.cpp new file mode 100644 index 0000000..4a39cfc --- /dev/null +++ b/BlankConsoleLab/CST116_Lab2_Williams.cpp @@ -0,0 +1,223 @@ +#include <iostream> +#include <windows.h> +#include <vector> +#include <tuple> +#include <algorithm> +#include <string> +#include <sstream> + +using namespace std; + +// Printing/input functions that I shamelessly stole from myself. Nothing new here. The pragma command isn't c++ code and is only there so that visual studio collapses all these functions down to one line. +#pragma region colorprints +const HANDLE HCONSOLE = GetStdHandle(STD_OUTPUT_HANDLE); + +// Uses black magic to print in full color with (optional) automatic line breaks. Requires <windows.h>. Returns nothing and takes up to 4 parameters: +// text (string): The text to be printed. Don't put a newline at the end. +// color (int): The color code of the text. Optional, defaults to white. +// linebreak (bool): Whether to end the line after printing. Optional, defaults to true. +// console (HANDLE) the console the function is printing to, used for changing color. Defaults to HCONSOLE, so as long as you define the console as HCONSOLE somewhere, you can probably ignore it. +void colorPrint(string text, int color = 15, bool linebreak = true, HANDLE console = HCONSOLE) +{ + SetConsoleTextAttribute(console, color); // Change the output color to whatever is chosen for the text (defaults to 15, which is white). + cout << text; + SetConsoleTextAttribute(console, 15); // Use moret the color back to white so that we don't start randomly printing other colors. + if (linebreak) // Add a line break to the end of the text unless told not to + { + cout << endl; + } +} + +//I made this to avoid having to write a dozen lines of couts and colorPrints just to print multiple colors in a line. It basically just loops through a list of text blocks and colorPrints each of them. Requires <windows.h> and colorPrint. Returns nothing and takes up to 2 parameters: +//text (vector): All the text fragments. Made up of a series of tuples (a different type of list that can contain multiple data types) containing the following elements: +// string: The block of text to be printed. Don't put a newline or endl at the end. +// int: The color of the block of text. 15 is white. +// bool: Whether to add a line break at the end of the block. +//console (HANDLE) the console the function is printing to, used for changing color. Defaults to HCONSOLE, so as long as you define the console as HCONSOLE somewhere, you can probably ignore it. +void multiColorPrint(vector<tuple<string, int, bool> > text, HANDLE console = HCONSOLE) +{ + string fragmentText; + int fragmentColor; + bool fragmentBreak; + + for (auto i : text) { //Loop through the text data + tie(fragmentText, fragmentColor, fragmentBreak) = i; //"Unpack" the current block tuple and pass its elements to their respective variables. + colorPrint(fragmentText, fragmentColor, fragmentBreak, console); //Print the current block. + } +} +#pragma endregion colorprints + +// It turns out that cin will usually break when you input a string that has whitespace in it, so I wrote this to fix it. Feel free to stick with cin if you're not trying to get input with whitespace, though. +string whitespaceInput() +{ + string input; + string afterWhitespace; + cin >> input; // cin stops reading input when it reaches any whitespace, but it doesn't get rid of what's left. Not only does that mean you lose anything the user typed after that whitespace, but it also breaks cin the next time you call it. cin (as far as I can tell) doesn't actually wait for user input, it just waits until there's something in the "input buffer". Since there's still input in the buffer, the next cin you call will see that there's something in the buffer and immediately read it instead of letting the user input something new. + + getline(cin, afterWhitespace); // getline gets input from the buffer like cin does, except that it ignores whitespace, and it doesn't wait for something to be in the buffer (if there's nothing there when you call it, it just returns an empty string). I'm using it here as a sort of "cleanup" function to grab anything that's left in the buffer after the cin. + + input += afterWhitespace; // Combine the input from the cin with anything that getline picked up. + + return input; +} + +// Splits a string into segments based on a list of delimiters. Returns a vector containing the segments and takes up to 3 parameters: +// str (string): The string to be split. +// delimiters (vector <char>): A vector containing the characters to split at. Optional, defaults to any whitespace. +// includeSplit (bool): Whether to include the delimiter when splitting. Optional, defaults to false. +vector <string> splitString(string str, vector <char> delimiters = { ' ', '\f', '\n', '\r', '\t', '\v' }, bool includeSplit = false) +{ + string temp = ""; // Staging variable for the current segment. + vector <string> segments = {}; + + for (char s : str) + { + if (find(delimiters.begin(), delimiters.end(), s) == delimiters.end()) + { // If the current character isn't a split character, add it to the end of the temp segment. + temp.push_back(s); + } + else + { // If it is a split character, push temp into the segment list and reset temp to an empty string. + if (includeSplit) { temp.push_back(s); } + segments.push_back(temp); + temp = ""; + } + } + + if (temp != "") { segments.push_back(temp); } // If a segment is still staged after the loop ends, add it to the list of segments. Prevents dropping the final segment if the string doesn't end with whitespace. + + return segments; +} + +// Returns true if the given string is a valid number, and false if it isn't. +bool isNumber(string str) { + istringstream iss(str); // Stringstreams are great + float f; + iss >> noskipws >> f; // Attempts to convert the string to a float. This won't throw an error if it fails, but it will set the stream's failbit to true. noskipws causes a failure if there's any leading whitespace. + return (iss.eof() && !iss.fail()); // If the conversion consumed the entire string (meaning there was no trailing whitespace), and it didn't fail, return true; otherwise return false. +} + +// Gets the temperature from the user, and the unit. Returns the temperature value and sets the unit by reference. Sets unit to one of 2 exit codes: +// 0: The user entered the temperature in F +// 1: The user entered the temperature in C +float getTemp(int& unit) { + vector <tuple<string, int, bool> > asktext = {make_tuple("Please enter the current temperature followed by its unit (", 15, 0), make_tuple("[F]ahrenheit", 9, 0), make_tuple(" or ", 15, 0), make_tuple("[C]elcius", 9, 0), make_tuple("): ", 15, 0)}; + float temp; + while (true) + { + multiColorPrint(asktext); + string input = whitespaceInput(); // Getting input that has whitespace in it is painful, so it's easier to just call getStringInput with an empty string as it's printout. + transform(input.begin(), input.end(), input.begin(), tolower); // Make every character in the string lowercase, because there's no reason for something like this to be case sensitive. I only have a vague idea how transform works, but doing this with a for loop causes some funky errors. + vector <string> iseg = splitString(input); + + int size = iseg.size(); + + if (size < 2) { + colorPrint("Oh no! Something went wrong. Make sure you enter a number and a unit.", 12); + continue; + } + + iseg = { iseg[0], iseg[1] }; // Discard anything after the first two segments + + if (isNumber(iseg[0]) && (iseg[1] == "f" || iseg[1] == "c" || iseg[1] == "fahrenheit" || iseg[1] == "celcius")) + { + // Representing the unit with a number probably seems weird, but it comes in handy later on. + iseg[1][0] == 'f' ? unit = 0 : unit = 1; // I can skip having to write out conditions for both "f" and "fahrenheit", because either way the first letter is f. Also why did it take me so long to learn about ternary operators? I missed lambdas from python and now I finally have them back. + + temp = stof(iseg[0]); + if ((unit == 0 && temp >= -80 && temp <= 121) || (unit == 1 && temp >= -62 && temp <= 49.5)) + { + return temp; + } + colorPrint("Oh no! Something went wrong. Make sure you enter a temperature between -80 and 121 Fahrenheit (-62 and 49.5 Celcius).", 12); + } else + { + colorPrint("Oh no! Something went wrong. Make sure you enter a number and a unit.", 12); + } + } +} + +// Gets the wind speed from the user, and the unit (yes, I know the assignment only says to use mph, but if you can enter temperature in celcius, you should be able to enter wind speed in kph). Returns the wind speed value and sets the unit by reference. Sets unit to one of 2 exit codes: +// 0: The user entered the wind speed in mph +// 1: The user entered the wind speed in kph +float getWind(int& unit) { + vector <tuple<string, int, bool> > asktext = { make_tuple("Please enter the current wind speed followed by its unit (", 15, 0), make_tuple("miles per hour", 9, 0), make_tuple(" or ", 15, 0), make_tuple("kilometers per hour", 9, 0), make_tuple("): ", 15, 0) }; + float wind; + string input; + while (true) + { + multiColorPrint(asktext); + input = whitespaceInput(); // I'm so happy I made this function. + + transform(input.begin(), input.end(), input.begin(), tolower); // Make every character in the string lowercase, because there's no reason for something like this to be case sensitive. I only have a vague idea how transform works, but doing this with a for loop causes some funky errors. + vector <string> iseg = splitString(input); + + int size = iseg.size(); + + //The checks for this are a fair bit more complex than in getTemp, since I wanted both "mph" and "miles per hour" to be valid inputs for the unit. I also can't just use a whole bunch of && operators and test everything at once because if some of the conditions aren't true, trying to check some of the other ones will crash the program. My current solution is to use all these ifs to create sort of a "stack" of conditions, where each check only gets made if the previous ones are true. + if (size < 2) + { + colorPrint("Oh no! Something went wrong. Make sure you enter a number and a unit.", 12); + continue; + } + if (!isNumber(iseg[0])) + { + colorPrint("Oh no! Something went wrong. Make sure you enter a number and a unit.", 12); + continue; + } + else + { + wind = stof(iseg[0]); + } + + if (size < 4) { + if (iseg[1] == "mph" || iseg[1] == "kph") + { + iseg[1] == "mph" ? unit = 0 : unit = 1; + } + else + { + colorPrint("Oh no! Something went wrong. Make sure you enter a number and a unit.", 12); + continue; + } + + } + else + { + string combined = iseg[1] + " " + iseg[2] + " " + iseg[3]; + if (combined == "miles per hour" || combined == "kilometers per hour") + { + combined == "miles per hour" ? unit = 0 : unit = 1; + } + else + { + colorPrint("Oh no! Something went wrong. Make sure you enter a number and a unit.", 12); + continue; + } + } + + if ((unit == 0 && wind >= 0 && wind <= 231) || (unit == 1 && wind >= 0 && wind <= 370)) + { + return wind; + } + else + { + colorPrint("Oh no! Something went wrong. Make sure you enter a wind speed between 0 and 231 miles per hour (0 and 370 kilometers per hour).", 12); + } + } +} + +int main() +{ + int tempUnit, windUnit; + float temp = getTemp(tempUnit); + string tempName; tempUnit == 0 ? tempName = "Fahrenheit" : tempName = "Celcius"; + cout << temp << " " << tempName << endl; + + float wind = getWind(windUnit); + string windName; windUnit == 0 ? windName = "miles per hour" : windName = "kilometers per hour"; + cout << wind << " " << windName << endl; + + return 0; +} + diff --git a/BlankConsoleLab/CST116_Lab2_Williams_Pseudocode.txt b/BlankConsoleLab/CST116_Lab2_Williams_Pseudocode.txt new file mode 100644 index 0000000..0fa87bf --- /dev/null +++ b/BlankConsoleLab/CST116_Lab2_Williams_Pseudocode.txt @@ -0,0 +1,8 @@ +program start: +get temperature and windspeed from the user with the following conditions: + - temperature is enterable in F or C + - windspeed is enterable in mph or kph + - -80 <= temp in F <= 121; -62 <= temp in C <= 49.5 + - 0 <= windspeed in mph <= 231; 0 <= windspeed in kph <= 378 +determine and print windchill +program end:
\ No newline at end of file |