diff --git a/Makefile b/Makefile index fb630b7..455fcc9 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,69 @@ TARGET = A1 SRC_FILES = main.cpp hands.cpp -# NO EDITS BELOW THIS LINE -CXX = g++ -g +# Tyler's custom makefile extensions +.DEFAULT_GOAL := all # Necessary so `make` doesn't run the "pack" target, as it is declared before "all" +.PHONY: pack clean-run c run + +## Adds only the necessary files for build into a .tar.gz file, named appropriately +ARCHIVED_FILES = Makefile $(SRC_FILES) $(SRC_FILES:.cpp=.h) $(SRC_FILES:.cpp=.hpp) +pack: + tar --ignore-failed-read -czvf $(TARGET).tar.gz $(shell echo $(ARCHIVED_FILES) | xargs ls -d 2>/dev/null) + +## Runs the pack target and then attempts to build & run the program to make sure it functions correctly +pack-test: pack + $(eval TMP := $(shell mktemp -d)) + tar -xvzf $(TARGET).tar.gz --directory $(TMP) + make -C $(TMP) + $(TMP)/$(TARGET) + rm -rf $(TMP) + +## An extension of the clean command that is shorter to type and removes a potential .tar.gz file +c: clean + $(DEL) -f $(TARGET).tar.gz + +## Simply builds and then executes the program +run: all + ./$(TARGET) + +# NO EDITS NEEDED BELOW THIS LINE + +CXX = g++ +CXXFLAGS = -O2 +CXXFLAGS_DEBUG = -g +CXXFLAGS_WARN = -Wall -Wextra -Wunreachable-code -Wshadow -Wpedantic +CPPVERSION = -std=c++17 + OBJECTS = $(SRC_FILES:.cpp=.o) ifeq ($(shell echo "Windows"), "Windows") TARGET := $(TARGET).exe DEL = del + Q = else DEL = rm -f + Q = " endif all: $(TARGET) $(TARGET): $(OBJECTS) - $(CXX) -std=c++17 -o $@ $^ + $(CXX) -o $@ $^ -%.o: %.cpp - $(CXX) -std=c++17 -o $@ -c $< +.cpp.o: + $(CXX) $(CXXFLAGS) $(CPPVERSION) $(CXXFLAGS_DEBUG) $(CXXFLAGS_WARN) -o $@ -c $< clean: - $(DEL) $(TARGET) $(OBJECTS) \ No newline at end of file + $(DEL) -f $(TARGET) $(OBJECTS) Makefile.bak + +depend: + @sed -i.bak '/^# DEPENDENCIES/,$$d' Makefile + @$(DEL) sed* + @echo $(Q)# DEPENDENCIES$(Q) >> Makefile + @$(CXX) -MM $(SRC_FILES) >> Makefile + +.PHONY: all clean depend + +# DEPENDENCIES +main.o: main.cpp hands.h +hands.o: hands.cpp hands.h diff --git a/hands.cpp b/hands.cpp index f32d3a4..6cc865e 100644 --- a/hands.cpp +++ b/hands.cpp @@ -1,6 +1,5 @@ #include "hands.h" -#include #include #include #include @@ -8,110 +7,118 @@ #include std::unique_ptr Hand::fromChar(const char letter) { - switch (letter) { - case 'R': - case 'r': - return std::make_unique(); - case 'P': - case 'p': - return std::make_unique(); - case 'S': - case 's': - return std::make_unique(); - case 'L': - case 'l': - return std::make_unique(); - case 'O': - case 'o': - return std::make_unique(); - default: - std::cout << "Invalid choice" << std::endl; - std::exit(1); - } + switch (letter) { + case 'R': + case 'r': + return std::make_unique(); + case 'P': + case 'p': + return std::make_unique(); + case 'S': + case 's': + return std::make_unique(); + case 'L': + case 'l': + return std::make_unique(); + case 'O': + case 'o': + return std::make_unique(); + default: + std::cout << "Invalid choice" << std::endl; + std::exit(1); + } } -std::unique_ptr Hand::generateRandom() { - std::mt19937 mt( - std::chrono::steady_clock::now().time_since_epoch().count()); - std::uniform_int_distribution intDist(0, 4); - - switch (intDist(mt)) { - case 0: - return std::make_unique(); - case 1: - return std::make_unique(); - case 2: - return std::make_unique(); - case 3: - return std::make_unique(); - case 4: - return std::make_unique(); - default: - std::cerr << "An invalid random number was generated, this should " - "be impossible" - << std::endl; - std::exit(1); - } +std::unique_ptr +Hand::generateRandom(std::mt19937 &mt, + std::uniform_int_distribution &intDist) { + switch (intDist(mt)) { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(); + case 2: + return std::make_unique(); + case 3: + return std::make_unique(); + case 4: + return std::make_unique(); + default: + std::cerr << "An invalid random number was generated, this should " + "be impossible" + << std::endl; + std::exit(1); + } } // List of function implementations for all of the different types of Hand std::string Rock::getHandName() const { return "rock"; } GameResult Rock::compareAgainst(const Hand &other) { - const std::string otherName = other.getHandName(); + const std::string otherName = other.getHandName(); - if (otherName == "rock") - return GameResult::Tie; - if (otherName == "scissors" || otherName == "lizard") - return GameResult::Win; - else - return GameResult::Loss; + if (otherName == "rock") { + return GameResult::Tie; + } + if (otherName == "scissors" || otherName == "lizard") { + return GameResult::Win; + } else { + return GameResult::Loss; + } } std::string Paper::getHandName() const { return "paper"; } GameResult Paper::compareAgainst(const Hand &other) { - const std::string otherName = other.getHandName(); + const std::string otherName = other.getHandName(); - if (otherName == "paper") - return GameResult::Tie; - if (otherName == "rock" || otherName == "spock") - return GameResult::Win; - else - return GameResult::Loss; + if (otherName == "paper") { + return GameResult::Tie; + } + if (otherName == "rock" || otherName == "spock") { + return GameResult::Win; + } else { + return GameResult::Loss; + } } std::string Scissors::getHandName() const { return "scissors"; } GameResult Scissors::compareAgainst(const Hand &other) { - const std::string otherName = other.getHandName(); + const std::string otherName = other.getHandName(); - if (otherName == "scissors") - return GameResult::Tie; - if (otherName == "paper" || otherName == "lizard") - return GameResult::Win; - else - return GameResult::Loss; + if (otherName == "scissors") { + return GameResult::Tie; + } + if (otherName == "paper" || otherName == "lizard") { + return GameResult::Win; + } else { + return GameResult::Loss; + } } std::string Lizard::getHandName() const { return "lizard"; } GameResult Lizard::compareAgainst(const Hand &other) { - const std::string otherName = other.getHandName(); + const std::string otherName = other.getHandName(); - if (otherName == "lizard") - return GameResult::Tie; - if (otherName == "spock" || otherName == "paper") - return GameResult::Win; - else - return GameResult::Loss; + if (otherName == "lizard") { + return GameResult::Tie; + } + if (otherName == "spock" || otherName == "paper") { + return GameResult::Win; + } else { + return GameResult::Loss; + } } std::string Spock::getHandName() const { return "spock"; } GameResult Spock::compareAgainst(const Hand &other) { - const std::string otherName = other.getHandName(); + const std::string otherName = other.getHandName(); - if (otherName == "spock") - return GameResult::Tie; - if (otherName == "scissors" || otherName == "rock") - return GameResult::Win; - else - return GameResult::Loss; + if (otherName == "spock") { + return GameResult::Tie; + } + if (otherName == "scissors" || otherName == "rock") { + return GameResult::Win; + } else { + return GameResult::Loss; + } } \ No newline at end of file diff --git a/hands.h b/hands.h index 97b5803..4cc2be8 100644 --- a/hands.h +++ b/hands.h @@ -1,5 +1,8 @@ +#ifndef HANDS_H +#define HANDS_H #include #include +#include enum GameResult { Win, Tie, Loss }; @@ -7,8 +10,9 @@ class Hand { public: // Converts a user-inputted character to the correct instance of Hand. static std::unique_ptr fromChar(char); - // Randomly returns one of the variants of Hand. - static std::unique_ptr generateRandom(); + // Randomly returns one of the variants of Hand, as generated by a Mersenne + // Twister function seeded by the current unix time + static std::unique_ptr generateRandom(std::mt19937&, std::uniform_int_distribution&); // Returns the lowercase name of this type of hand, for example "rock" or // "scissors". @@ -17,35 +21,39 @@ class Hand { // returned value is from the perspective of this instance of hand. For // example, if GameResult::Win is returned, the instance of Hand wins // against the passed argument Hand. - virtual GameResult compareAgainst(const Hand&) = 0; + virtual GameResult compareAgainst(const Hand &) = 0; }; +// List of all types of RPSLS hands that can be played + class Rock : public Hand { public: std::string getHandName() const override; - GameResult compareAgainst(const Hand&) override; + GameResult compareAgainst(const Hand &) override; }; class Paper : public Hand { public: std::string getHandName() const override; - GameResult compareAgainst(const Hand&) override; + GameResult compareAgainst(const Hand &) override; }; class Scissors : public Hand { public: std::string getHandName() const override; - GameResult compareAgainst(const Hand&) override; + GameResult compareAgainst(const Hand &) override; }; class Lizard : public Hand { public: std::string getHandName() const override; - GameResult compareAgainst(const Hand&) override; + GameResult compareAgainst(const Hand &) override; }; class Spock : public Hand { public: std::string getHandName() const override; - GameResult compareAgainst(const Hand&) override; -}; \ No newline at end of file + GameResult compareAgainst(const Hand &) override; +}; + +#endif // HANDS_H diff --git a/main.cpp b/main.cpp index 603f0d2..ba4c67a 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,13 @@ /* CSCI 200: Assignment 1 (Rock Paper Scissors): Tyler Beckman * * Author: Tyler Beckman + * Resources used: + * While learning how to use classes & polymorphism in the way I wanted to, + * student Maxwell Gross helped me debug why the way I was trying to use classes + * didn't work (virtual fields not existing, where I needed to put class + * definitions vs method definitions, etc). No code was copied or written for + * me, he just helped me understand how all of these structures I was used to in + * other langs like Java and JS worked in cpp :) * * Description: * A C++ program to play rock paper scissors (lizard spock) against @@ -15,99 +22,83 @@ * multiple other languages, so I felt it would be fun to use this as an excuse * to learn how the concepts work in cpp. I apologize if this is more * complicated than we were supposed to make it, but it was fun and it should - * still be a perfectly functional and point-scoring program :) - * - * Academic integrity: - * 1. Because I did go overboard on making an object-oriented program when we - * haven't even learned how classes yet, I did use a lot of online cpp syntax - * reference and explainers to learn how classes and inheritance work in cpp. - * This includes mainly cppreference.com, but also geeksforgeeks.org and - * tutorialspoint to figure out the basics. Nowhere did I actually copy the - * exact structures, I simply used it to figure out how to do what I wanted and - * what I knew how to do in other languages such as Java and JavaScript (have a - * "which one wins" function on each sibling class of "Hand"). I am not entirely - * sure if this needs cited, but given we have learned none of this yet, I just - * wanted to clarify that this is all my code - not copied from the internet, - * ChatGPT, Copilot, or anything else other than my code. - * 2. Also since I was making a program with structures that I hadn't quite - * learned in this language yet, I did ask Max[TODO FILL IN NAME] (someone with - * experience in cpp) for debugging assistance when concepts I was used to in - * other languages didn't work well in cpp. For example, I wasn't used to class - * definitions and function bodies being separate, so he helped me figure out - * the correct structure there so I could properly reference sibling classes in - * methods on the parent class. + * still be a perfectly functional and point-scoring program :). To clarify, I + * didn't copy any code but I did use multiple online tutorials and references + * to learn how classes and the such work in cpp. */ #include "hands.h" +#include #include #include #include int main(void) { - int wins = 0; - int losses = 0; - int ties = 0; + int wins = 0; + int losses = 0; + int ties = 0; - while (true) { - // Take a letter input and convert it to the correct class type for - // later use - char handLetter; - std::cout << "Welcome to a round of [R]ock [P]aper [S]cissors [L]izard " - "Sp[O]ck! Please enter " - "a hand to play (R/P/S/L/O): "; - std::cin >> handLetter; - const std::unique_ptr userHand = Hand::fromChar(handLetter); + std::mt19937 mt(std::chrono::steady_clock::now().time_since_epoch().count()); + std::uniform_int_distribution intDist(0, 4); - // Generate a random "computer" hand and display the two choices - const std::unique_ptr computerHand = Hand::generateRandom(); + while (true) { + // Take a letter input and convert it to the correct class type for + // later use + char handLetter; + std::cout << "Welcome to a round of [R]ock [P]aper [S]cissors [L]izard " + "Sp[O]ck! Please enter " + "a hand to play (R/P/S/L/O): "; + std::cin >> handLetter; + const std::unique_ptr userHand = Hand::fromChar(handLetter); - std::cout << std::endl - << "Player chose " << userHand->getHandName() << std::endl - << "Computer chose " << computerHand->getHandName() - << std::endl - << std::endl; + // Generate a random "computer" hand and display the two choices + const std::unique_ptr computerHand = + Hand::generateRandom(mt, intDist); - // Compare the two hands, add to statistics, and give the correct output - // and explanation - switch (userHand->compareAgainst(*computerHand)) { - case Win: - std::cout << "The player wins, as " << userHand->getHandName() - << " beats " << computerHand->getHandName() << "!" - << std::endl; - wins++; - break; - case Tie: - std::cout << "No one wins, as " << userHand->getHandName() - << " is the same as " << computerHand->getHandName() - << "." << std::endl; - ties++; - break; - case Loss: - std::cout << "The computer wins, as " - << computerHand->getHandName() << " beats " - << userHand->getHandName() << "." << std::endl; - losses++; - break; - } + std::cout << std::endl + << "Player chose " << userHand->getHandName() << std::endl + << "Computer chose " << computerHand->getHandName() << std::endl + << std::endl; - // Ask if the user would like to play again or exit - char playAgain; - std::cout << "Do you want to play again (Y/N)? "; - std::cin >> playAgain; - switch (playAgain) { - case 'Y': - case 'y': - std::cout << std::endl; - continue; - case 'N': - case 'n': - default: - std::cout << std::endl - << "Thanks for playing!" << std::endl - << "You won " << wins << " game(s), lost " << losses - << " game(s), and tied " << ties << " time(s)." - << std::endl; - std::exit(0); - } - } + // Compare the two hands, add to statistics, and give the correct output + // and explanation + switch (userHand->compareAgainst(*computerHand)) { + case Win: + std::cout << "The player wins, as " << userHand->getHandName() + << " beats " << computerHand->getHandName() << "!" + << std::endl; + wins++; + break; + case Tie: + std::cout << "No one wins, as " << userHand->getHandName() + << " is the same as " << computerHand->getHandName() << "." + << std::endl; + ties++; + break; + case Loss: + std::cout << "The computer wins, as " << computerHand->getHandName() + << " beats " << userHand->getHandName() << "." << std::endl; + losses++; + break; + } + + // Ask if the user would like to play again or exit + char playAgain; + std::cout << "Do you want to play again (Y/N)? "; + std::cin >> playAgain; + switch (playAgain) { + case 'Y': + case 'y': + std::cout << std::endl; + continue; + case 'N': + case 'n': + default: + std::cout << std::endl + << "Thanks for playing!" << std::endl + << "You won " << wins << " game(s), lost " << losses + << " game(s), and tied " << ties << " time(s)." << std::endl; + std::exit(0); + } + } } \ No newline at end of file