Hello Internet, its your premiere Script Kitty Killer Kat here to share the details of my latest fun project. Although its not actually something I made on a weekend I’m going to use this moniker anyway, my ADHD means that I often get Hyperfocused on projects and then completely forget to write about them afterwords, but stay tuned for more regular updates as I am looking to change that.
Today’s project is something quick and fun I made to keep my C skills sharp, I saw Alpha Phoenix’s video on Snake AI and while I would love to make a Snake AI of my own its a little bit beyond my current Computer Science skill-set however I was inspired to make something involving at least a rudimentary AI. My C skills had been rusting somewhat after I paused work on my Unity game so I decided to write a C++ console app that would allow you to play Rock Paper Scissors against a AI opponent. Here is the Github Permalink to the project.
So to start, we need an AI that we can play against. This first AI is just random, with no logic behind their choices. To start we need a random number generator, for this use case I chose to use a pseudo random number generator seeded with the current time. In a more serious use case where security is a concern this would be a bad idea as it would be trivial to bypass the generator and get the same output, however in this instance all that would accomplish is cheating at RPS so it was not a design consideration. Next we add some text to the console to tell the player what app they are running and how to use it. So far our main() function looks like this:
//seeds the pseudo random number generator with the time, not the best choice but it works well enough for this use case.
srand((unsigned)time(0));
std::cout << "Wellcome to Rock Paper Scissors C++ Edition!\n";
std::cout << "This app made by Killerkat on 9/22/2021 find me on Github or Checkout my blog!\n";
std::cout << "0 for Rock 1 for Paper and 2 for Scissors\n";
Next we need a way for the player to choose their move and a way to have the computer generate its move, for this I create a new class called CPUData that stores variables related to the Computer Player and methods to determine how the Computer Player acts. I have made two AI’s based on the RNG from before and used switches to announce their moves, the int that stores the move is part of the class itself. The class looks like this:
class CPUData { //Creates a class so we can store the varibles we need in an object that can be passed when needed.
public:
int CPUChoice;
std::string CPUChoiceMessage;
bool debugMessageToggle;
void RandomOpponent() {
//Random mode
CPUChoice = (rand() % 3);
if (debugMessageToggle) {
std::cout << "DEBUG: CPU CHOICE (Random) IS " << CPUChoice << "\n";
}
switch (CPUChoice) {
case 0:
CPUChoiceMessage = "CPU Chose Rock";
break;
case 1:
CPUChoiceMessage = "CPU Chose Paper";
break;
case 2:
CPUChoiceMessage = "CPU Chose Scissors";
break;
default:
CPUChoiceMessage = "CPU Had an Invalid Choice\n";
}
};
void RockLoverOpponent() {
//Rock mode
CPUChoice = (rand() % 3);
if (debugMessageToggle) {
std::cout << "DEBUG: CPU (Rocky) CHOICE IS " << CPUChoice << "\n";
}
switch (CPUChoice) {
case 0:
CPUChoiceMessage = "CPU Chose Rock";
break;
case 1:
CPUChoice = (rand() % 2);
switch (CPUChoice) {
case 0:
CPUChoiceMessage = "CPU Chose Rock";
break;
case 1:
CPUChoiceMessage = "CPU Chose Paper";
break;
default:
CPUChoiceMessage = "CPU Had an Invalid Choice\n";
}
break;
case 2:
CPUChoiceMessage = "CPU Chose Scissors";
break;
default:
CPUChoiceMessage = "CPU Had an Invalid Choice\n";
}
};
};
Now that we have a way to get the Computer Players move how do we play against it? Well we stored this inside this class so that we can create an object in our main() function and pass the object to a second function PlayGame. The PlayGame function takes our object as an argument, gets the players move and then compares the data from the object to the players move and returns either true for a win or false for a tie/loss. The PlayGame() function looks like this:
bool PlayGame(CPUData Opponent) {
int PlayerChoice = 3; //3 is null 0 is Rock 1 is Paper and 2 is Scissors
std::cout << "Make Your Move!\n";
std::cin >> PlayerChoice;
switch (PlayerChoice) {
case 0:
std::cout << "You Chose Rock and " << Opponent.CPUChoiceMessage << "\n";
if (Opponent.CPUChoice == 0) {
std::cout << "The game ends in a tie!\n";
return false;
}
else if (Opponent.CPUChoice == 2) {
std::cout << "You win!\n";
return true;
}
else {
std::cout << "CPU wins!\n";
return false;
}
break;
case 1:
std::cout << "You Chose Paper and " << Opponent.CPUChoiceMessage << "\n";
if (Opponent.CPUChoice == 1) {
std::cout << "The game ends in a tie!\n";
return false;
}
else if (Opponent.CPUChoice == 0) {
std::cout << "You win!\n";
return true;
}
else {
std::cout << "CPU wins!\n";
return false;
}
break;
case 2:
std::cout << "You Chose Scissors and " << Opponent.CPUChoiceMessage << "\n";
if (Opponent.CPUChoice == 2) {
std::cout << "The game ends in a tie!\n";
return false;
}
else if (Opponent.CPUChoice == 1) {
std::cout << "You win!\n";
return true;
}
else {
std::cout << "CPU wins!\n";
return false;
}
break;
default:
std::cout << "Invalid Choice\n";
return false;
}
}
Now all we have to do is capture that return value in our main() function and use it to increment a score counter if we won! But wait we still cant change the AI we are playing against, what if we want to play against Dwayne Rocky JSONson the rock loving, paper hating AI that when it chooses paper has a 50% chance to choose rock instead? Well for that we use another switch statement at the end of the game to allow the player to either play again, quit, or open the options menu. Then we just add some dialog to inform the player of the game state and settings, tie in a clear() function we got from Stack Overflow so we don’t have to create a security risk by passing commands to the windows command interpreter and then we are done. The finished main() function looks like this:
void main()
{
//seeds the pseudo random number generator with the time, not the best choice but it works well enough for this use case.
srand((unsigned)time(0));
static int gameScore;
static int gameMode;
static bool debugMessage;
int optionsMenu; // used for the options menu switch
CPUData CPUPlayer; //Obj we use to hold the data for the CPU player for easy use between functions
CPUPlayer.debugMessageToggle = debugMessage;
std::cout << "Wellcome to Rock Paper Scissors C++ Edition!\n";
std::cout << "This app made by Killerkat on 9/22/2021 find me on Github or Checkout my blog!\n";
std::cout << "0 for Rock 1 for Paper and 2 for Scissors\n";
switch (gameMode) { //Game mode selector
case 0:
CPUPlayer.RandomOpponent();
std::cout << "Current Mode Random\n";
break;
case 1:
CPUPlayer.RockLoverOpponent();
std::cout << "Current Mode Rock Lover\n";
break;
default:
break;
}
if (gameScore > 0) { //Simple way to track game wins
std::cout << "You have won " << gameScore << " times!\n";
}
//passes the Computer player object to the game playing function so it can generate a game, if the Playgame function returns true it increases the score.
if (PlayGame(CPUPlayer)) {
gameScore++;
}
int pasta; //because its spaghetti code.
std::cout << "Play again? 1 = Yes 0 = No (Close Game) 2 = Open options menu\n";
std::cin >> pasta;
switch (pasta){
case 0 :
break;
case 1 :
clear();
main();
break;
case 2:
clear();
std::cout << "OPTIONS MENU: Select an option\n 0 : Set opponent to Random mode.\n 1 : Set opponent to Rock Lover mode.\n 2 : Toggle Debug Mode.\n";
std::cin >> optionsMenu;
switch (optionsMenu) {
case 0:
gameMode = 0;
break;
case 1:
gameMode = 1;
break;
case 2 :
debugMessage = !debugMessage;
if (debugMessage) {
std::cout << "Debug mode is on.\n";
}
else {
std::cout << "Debug mode is off.\n";
}
break;
default :
break;
}
clear();
main();
break;
default:
clear();
main();
break;
}
}
And that’s the end of the project, I also added a debug option that tells you the AI’s move before you make yours. I hope you learned something today, even if that something was that I know C++ well enough to make a small game. If you have any questions about how this code works, thoughts on my technique or general thoughts and questions please leave them in the comments down below and I’ll be sure to answer them.
If you have any suggestions for future topics/projects let me know and until next time this is your favorite (and only) Script-Kitty Killer Kat signing off.