How did yours end?

Thanks for being such an active community member @orcmid

First of all, I wouldn’t say this is the final game.
It is for now. Since I’m still at such a noob level in programing I feel it’s better to leave it here and move on with the course. I can always come back later to modify it. :smile: struggled for so long to get the random words to work and tried so many different approaches to it ( I know you said we wouldn’t do it but I needed it ). Then I looked at Zaptruders code and found out what I had done wrong. So, big shout out to Zaptruder, hope you don’t get mad at me for using your solution. :wink:
I have not cleaned the code after my mayhems… so it might not be the best to look at. But, it’s here anyway and I’m pumped up to move on and fill my head with syntax and information!

1 Like

I’m so excited the I completed this GAME!!! I look forward to constantly working on this game. I added a difficulty chooser, although not a elegant as Ben would:sweat: , but I shall be working hard on it. Thank you Ben and fellow class mates.
My Code is over at : Kaylen’s Code over on github :grin:

WOW! The art is amazing and I totally loved your version of the game. I’m sure I’ll learn heaps from your work. Best of luck with the rest of the course:grin:

I just finished Section 2 and added the difficulty settings as suggested by @ben.

My game looks like this:

The code is on a Gist on GitHub.

I had to do a bit of exception handling to prevent the user from entering a non-numeric value and crashing my game.

I just finished Section 2 Bull Cow Game, differently from the lectures I have added:

  • words.txt -> a dictionary of Isograms to choose from

  • difficulty settings -> when the users prompts the game, they are asked for the length of the Isogram to guess

There are still a couple features I was thinking of adding in the future such as:

  • validation of isogram length
  • colours to intro screen
  • record of wins and losses
  • word hints -> keeps track of letters guessed, gives you 1 letter in exchange for a try?

Looking forward to section 3BullCowGame.zip|attachment (3.7 KB)

Mine was more of a spin off of the game and adapted to my friends that play tested it!

  1. The Details of the changes are in this post!
    My Playtesting Results! (I added a twist to the game!)
  2. The download link to my game is here if anyone wants to try it!
    FBullCowGame.zip (4.4 KB)

Thanks for making this course possible for all of us!
I wish you all a happy week!

A finished version for my game can be found here. I also added a random word picker, I downloaded the SOWPODS scrabble word dictionary and added all the isograms contained within that to the game. As a result it’s pretty difficult to win. I aim to add a difficulty selector next and refine the number of guesses, with 50330 words to choose from it’s fairly difficult so I will also change the game a bit further with a hinting system, and to just display the location of the bull’s straight away instead of the player having to figure out which letters are in what places.

I stored the list of words in a text file but to make this work so far I have to have the absolute path to the text file in a constant string in my class. This isn’t ideal as it means the player will have to manually edit this later. My code wouldn’t run in xcode when a relative path was used, why is this and what is a good solution? It seems a bit rubbish that this has to be manually set in a code file.

I encountered a bug in adding the random word picker into my code, and wondered if anyone else also had the same problem? I noticed a problem that sometimes the introduction message would say something like “Guess the 9 letter word” and I would enter nine letters and receive the following error message “please enter a 5 letter word”. The problem is that the reset method of BCGame is called twice, once when the object is created and then again in play game. I have “resolved” this issue by moving the call to printIntro into the play game method and calling it after the reset method is called. I am not really sure however if this is the best solution? It seems kind of broken to me that the reset method will be called twice but I can’t think of a better solution.

I just finished BullCowGame and learned a lot from this section. Thanks @ben :slight_smile:

I made almost no changes in the code, but I’m used to some other coding standard (variables and functions start with a small letter, and every new word starts with a capital letter. For example: bullCowGame).

I also made an array (list) with hidden words. So everytime you play, you’ll get a random word from the list. I’m now searching for a way to make my guess full lowercase by default. For example: your guess is: PLanEt, it will automatically translate to: planet. This is more user friendly than the not lowercase error.

You can find my code here.

This weekend I’m making a small C++ console project to practice a little bit with C++ and classes. Next week I start with section 3. :slight_smile:

1 Like

You’re welcome :slight_smile:

Hi,

I just finished section 2.

Everytime you start a game , it generates a random isogram from an array.

Thats all.

I’m coming section 3!

You can check my code here: https://drive.google.com/open?id=0B-7MkPbofvvoQU45N3ktMEk4cGc

Hi All!

I added a few upgrades to the BullCowGame:
-a user option for difficulty
-a random word selected from a separate .txt file
-an additional error code for spaces entered (“flower” vs. “flow er”)

The only gripe I really have is my random word selector code portion feels sloppy.

Here’s my code:
BullCowGame.zip (4.4 KB)

Thank you for the awesome lessons so far!

Hey Guys!

So this project was really fun. I really enjoyed how in-depth the Bulls and Cows series was. I never really had a chance to get to know the Visual Studio Editor until now ( I worked with the Atom Editor for a lot of my work ). So here’s what i have so far.

##Features

  • Added a feature for users to be able to choose a word length.
  • Added a feature to get a random word based on their word length.
  • Randomly adds a word if no word length is given.

##Links
Github Repository: https://github.com/Enoch2k2/bulls-and-cows

##PS
Don’t laugh too hard with my ASCII, i’m a terrible artist haha.

main.cpp

/* This file is used for the view part of the MVC structure
	See FBullCowGame.cpp for game logic.
*/
#pragma once

#include <iostream>
#include <string>
#include "FBullCowGame.h"

// Unreal standard
using FText = std::string;
using int32 = int;

void PrintIntro();
void PrintGameSummery();
void PlayGame();
FText GetValidGuess();
int32 GetNewWordLength();
bool AskToPlayAgain();

FBullCowGame BCGame; // C++ Instantiation'

//intro point for the app
int main()
{
	do
	{
		PrintIntro();
		PlayGame();
	}
	while(AskToPlayAgain());
	// will ask the user to play again and keep it in a loop if 'y'
	return 0;
}


void PrintIntro()
{
	// prints an introduction (terrible ASCII)
	std::cout << "\n\nWelcome to Bulls and Cows, a fun word game.\n";
	std::cout << std::endl;
	std::cout << "|-----, " << " ,----," << std::endl;
	std::cout << "| |__| |" << "| |---'" << std::endl;
	std::cout << "|  __  |" << "| |    " << std::endl;
	std::cout << "| |__| |" << "| |---," << std::endl;
	std::cout << "|_____/ " << " \\___/" << std::endl;
	return;
}

// Gives details of how the game ended (win / lose)
void PrintGameSummery()
{
	if (BCGame.IsGameWon()) {
		std::cout << "Congratulations, you won!\n";
	} else {
		std::cout << "Sorry, better luck yet next time!\n";
	}
	return;
}

// players a single game to completion
void PlayGame()
{
	// ask player of how long of letters, 3-7
	int32 GetWordLength = GetNewWordLength();
	// resets game back to beginning state
	BCGame.Reset(GetWordLength);
	std::cout << "Can you guess a " << BCGame.GetHiddenWordLength() << " letter word I am thinking of?" << std::endl << std::endl;
	// while the game is NOT won and there are still remaining tries
	while(!BCGame.IsGameWon() && BCGame.GetCurrentTry() <= BCGame.GetMaxTries())
	{
		FText Guess = GetValidGuess();
		FBullCowCount BullCowCount = BCGame.SubmitValidGuess(Guess);
		std::cout << "Bulls = " << BullCowCount.Bulls;
		std::cout << ". Cows = " << BullCowCount.Cows << std::endl << std::endl;
	}
	// at this point the game is over and we'll tell the user how it ended.
	PrintGameSummery();
	return;
}

// get a random number between two integers
int32 GetRandomNumber(int32 min, int32 max)
{
	int32 n = max - min + 1;
	int32 remainder = RAND_MAX % n;
	int32 x;
	do {
		x = rand();
	} while (x >= RAND_MAX - remainder);
	return min + x % n;
}

// allow player to choose length of word
int32 GetNewWordLength()
{
	std::cout << "Would you like to choose a word length?(y/n): ";
	FText Response = "";
	std::getline(std::cin, Response);
	if (Response == "y" || Response == "Y") {
		std::cout << "\n How long of word? Enter 3-7 :";
		Response = "";
		std::getline(std::cin, Response);
		if (Response == "3") {
			return 3;
		}
		else if (Response == "4") {
			return 4;
		}
		else if (Response == "5") {
			return 5;
		}
		else if (Response == "6") {
			return 6;
		}
		else if (Response == "7") {
			return 7;
		}
		else {
			std::cout << "Invalid input\n";
			GetNewWordLength();
		}
	}
	else {
		return GetRandomNumber(3, 7);
	}
}


FText GetValidGuess()
{
	EGuessStatus Status = EGuessStatus::Invalid_Status;
	FText Guess = "";
	do {
		// get guess
		std::cout << "Try " << BCGame.GetCurrentTry() << " of " << BCGame.GetMaxTries() << ". Enter Guess: ";
		std::getline(std::cin, Guess);

		// check guess validity
		Status = BCGame.CheckGuessValidity(Guess);
		
		// check for guess errors
		switch (Status)
		{
		case EGuessStatus::Wrong_Length:
			std::cout << "Please enter a " << BCGame.GetHiddenWordLength() << " letter word.\n\n";
			break;
		case EGuessStatus::Not_Isogram:
			std::cout << "Please enter a word without repeating letters.\n\n";
			break;
		case EGuessStatus::Not_Lowercase:
			std::cout << "Please use lowercase characters.\n\n";
			break;
		default:
			// assume the guess is valid
			break;
		}
	} while (Status != EGuessStatus::OK);
	// if the Guess is not valid then restart try
	return Guess;
}

bool AskToPlayAgain()
{
	std::cout << "Do you want to play again? (y/n): ";
	FText Response = "";
	std::getline(std::cin, Response);

	return (Response[0] == 'y' || Response[0] == 'Y');
}

BullCowGame.h

/*
	Header file for the FBullCowGame, this holds the
	definitions of the game logic to be used in our
	FBullCowGame class. See FBullCowGame.cpp for game
	logic.
*/
#pragma once

#include <string>

// unreal standard
using FString = std::string;
using int32 = int;

// we set up a simple struct to keep track of bulls and cows
struct FBullCowCount {
	int32 Bulls = 0;
	int32 Cows = 0;
};

// All of our guess status
enum class EGuessStatus
{
	Invalid_Status,
	OK,
	Not_Isogram,
	Wrong_Length,
	Not_Lowercase
};

// our predefined list of public and private methods / variables
class FBullCowGame {
public:
	FBullCowGame(); // constructor

	int32 GetMaxTries() const;
	int32 GetCurrentTry() const;
	int32 GetHiddenWordLength() const;
	FString GetWord(int32) const;
	FString GetRandomWord(FString LetterArray[]) const;
	// TODO Create Function to get random word based on length chosen;
	bool IsGameWon() const;

	EGuessStatus CheckGuessValidity(FString) const;

	void Reset(int32);
	FBullCowCount SubmitValidGuess(FString);

private:
	int32 MyCurrentTry;
	FString MyHiddenWord;
	bool bGameWon;
	bool IsIsogram(FString) const;
	bool IsLowerCase(FString) const;
};

BullCowGame.cpp

/*
	This file contains the logic of the game itself, it
	is the game controller. See the header file for a
	list of game definitions, or the main.cpp file for
	the view logic.
*/
#pragma once

#include "FBullCowGame.h"
#include <map>
#define TMap std::map

// unreal standard
using FString = std::string;
using int32 = int;

// instantiates the game with a fresh state
FBullCowGame::FBullCowGame(){ Reset(3); }

// getters
int32 FBullCowGame::GetCurrentTry() const { return MyCurrentTry; }
int32 FBullCowGame::GetHiddenWordLength() const { return MyHiddenWord.length(); }
bool FBullCowGame::IsGameWon() const { return bGameWon; }

// get random word to be the hidden word randomly
FString FBullCowGame::GetWord(int32 WordLength) const
{
	// create arrays of words based on length
	FString FiveLetterWords[3] = { "plane", "smile", "giant" };
	FString ThreeLetterWords[3] = { "and", "get", "sea" };
	FString FourLetterWords[3] = { "love", "grow", "tire" };
	FString SexLetterWords[3] = { "beauty", "flower", "string" };
	FString SevenLetterWords[3] = { "alchemy", "machine", "sardine" };
	if (WordLength == 3) {
		return GetRandomWord(ThreeLetterWords);
	}
	else if (WordLength == 4) {
		return GetRandomWord(FourLetterWords);
	}
	else if (WordLength == 5) {
		return GetRandomWord(FiveLetterWords);
	}
	else if (WordLength == 6) {
		return GetRandomWord(SexLetterWords);
	}
	else {
		return GetRandomWord(SevenLetterWords);
	}
}

// used to get a random word out of an array with a length of 3 elements
FString FBullCowGame::GetRandomWord(FString LetterArray[]) const
{
	int32 n = 2 - 0 + 1;
	int32 remainder = RAND_MAX % n;
	int32 x;
	do {
		x = rand();
	} while (x >= RAND_MAX - remainder);
	return LetterArray[0 + x % n];	
}

int32 FBullCowGame::GetMaxTries() const 
{
	// creates a difficulty - first difficulty in the TMap is 3 length word, 4 tries. ect...
	TMap<int32, int32> WordLengthToMaxTries{ {3,4}, {4,7}, {5, 10}, {6,15}, {7, 20} };
	return WordLengthToMaxTries[MyHiddenWord.length()];
}

// resets the game to beginning state
void FBullCowGame::Reset(int32 WordLength)
{
	const FString HIDDEN_WORD = GetWord(WordLength);
	MyHiddenWord = HIDDEN_WORD;
	MyCurrentTry = 1;
	bGameWon = false;
	return;
}

// validates the guess
FBullCowCount FBullCowGame::SubmitValidGuess(FString Guess)
{
	MyCurrentTry++;
	FBullCowCount BullCowCount;
	int32 WordLength = MyHiddenWord.length();
	for (int32 i = 0; i < WordLength; i++) {
		for (int32 j = 0; j < WordLength; j++)	{
			if (MyHiddenWord[i] == Guess[j]) {
				if (i == j) {
					BullCowCount.Bulls++;
				}
				else {
					BullCowCount.Cows++;
				}
			}
		}
	}
	// if they get the word right, set the bGameWon flag to true
	// indicating that they have won
	if (BullCowCount.Bulls == WordLength) {
		bGameWon = true;
	}
	else {
		bGameWon = false;
	}
	return BullCowCount;
}

EGuessStatus FBullCowGame::CheckGuessValidity(FString Guess) const
{
	if (!IsIsogram(Guess))// if the guess isn't an isogram
	{
		return EGuessStatus::Not_Isogram;
	}
	else if (!IsLowerCase(Guess)) // if the guess isn't all lowercase
	{
		return EGuessStatus::Not_Lowercase;
	}
	else if (Guess.length() != GetHiddenWordLength())// if the guess length is wrong
	{
		return EGuessStatus::Wrong_Length;
	}
	else
	{
		return EGuessStatus::OK;
	}
}

bool FBullCowGame::IsIsogram(FString Word) const
{
	if (Word.length() <= 1){ return true; }

	TMap<char, bool> LetterSeen;
	// check each letter if the letter has been seen.
	for (auto Letter : Word)
	{
		Letter = tolower(Letter);
		// if the letter has been seen, then it's not an Isogram
		if (LetterSeen[Letter]) {
			return false;
		}
		else {
			// indicates the letter has been seen for the first time.
			LetterSeen[Letter] = true;
		}
	}	
	return true;
}

// this method checks the string if all the characters are lowercased.
bool FBullCowGame::IsLowerCase(FString Word) const
{
	if (Word.length() <= 1) { return true; }
	// checks each letter if the letter is lowercased.
	for (auto Letter : Word)
	{
		if (!islower(Letter)) {
			return false;
		}
	}
	return true;
}

Hello,

Here’s my final code for Bulls and Cows.
It took way longer than I expected to add the changes I wanted but a least I’m satisfied with it which has to count for something.

I added a bank of isograms from three to six letters picked from this website: http://www.morewords.com/unique-letters/

In an effort to narrow down the process of figuring out the word I’ve only chosen words that are common nouns ( so something “easy to think of” like a cat, a house, a thing ). Pluralization is also excluded because that just gets unnecessarily confusing.

The player can also pick the length of the secret word or have it chosen at random.
You also get a brief explanation of the game and the amount of tries you have initially.

Here’s the code:

main.cpp

#include <iostream>
#include <string>
#include <ctime>
#include "FBullCowGame.h"

/* This is the exe that makes use of the BullCow class.
This acts as a view in a MVC pattern, and is responsible for all user interaction. For game logic see BullCow class.

*/

using FText = std::string;
using int32 = int;

// Functions
void PrintIntro();
void PlayGame();
void ChooseSecretWordLength();
FText GetValidGuess();
bool AskToPlayAgain();
void PrintGameSummary();

FBullCowGame BCGame;	// Create an instance of the Game Class

int main()				// entry point of the game
{
	PrintIntro();
	do
	{
		PlayGame();
	} 
	while (AskToPlayAgain() == true);
	return 0;			// exits the application
}

void PrintIntro()
{
	std::cout << "\n\n";
	std::cout << "   /(           )\\Welcome to  _________    " << std::endl;
	std::cout << "   \\ \\__-----__/ / Bulls     /         \\   " << std::endl;
	std::cout << "     /_       _\\      &     / _       _ \\  " << std::endl;
	std::cout << "    /- \\     \\ -\\    Cows! /-|         |-\\ " << std::endl;
	std::cout << "       \\\\_____\\/             | _____ /      " << std::endl;
	std::cout << "        (o_____o)           (o_____o)      " << std::endl;
	std::cout << " \n\n";
	std::cout << " Guess the secret isogram!\n";
	std::cout << " A Cow means you've found one of the secret word's letters in the wrong place. \n";
	std::cout << " A Bull means you've found a letter and it is in the right place.";
	std::cout << std::endl;
	return;
}

void PlayGame()
{
	BCGame.Reset();
	ChooseSecretWordLength();
	std::cout << "\n The secret isogram is " << BCGame.GetHiddenWordLength() << " letters long. \n It is a common noun and cannot be in its plural form. ";
	std::cout << "\n You have " << BCGame.GetMaxTries() << " tries.";
	int MaxTries = BCGame.GetMaxTries();							//pick the ammount of tries based on the words length
	while (!BCGame.IsGameWon() && BCGame.GetCurrentTry() <= MaxTries)//loop until the game is won for the ammount of turns the player can guess
	{
		FText Guess = GetValidGuess();
		FBullCowCount BullCowCount = BCGame.SubmitValidGuess(Guess);// Submit valid guess to the game
		std::cout << " Bulls = " << BullCowCount.Bulls;				// print number of bulls and cows
		std::cout << "  " << " Cows = " << BullCowCount.Cows << "  ";
		std::cout << "Your guess was " << Guess << ". \n";			// repeats player's guess
	}
	PrintGameSummary();
	return;
}

void ChooseSecretWordLength()
{
	bool LengthChosen = false;
	FText WordLengthInput = "";
	std::cout << " \n To choose the length of the secret word, \n please enter a number between 3 and 6 or 'R' to pick at random: ";
	while (!LengthChosen)												// loop until we've decided how long the secret word is
	{
		std::getline(std::cin, WordLengthInput);
		FText DesiredWordLength = WordLengthInput;						// redeclaring the word length to make the input c-string into a std::string/FText

		if (DesiredWordLength == "R" || DesiredWordLength == "r")
		{
			srand(time(NULL));
			BCGame.ChooseSecretWord(std::rand() % 3 + 3);
			LengthChosen = true;
		}
		else if (DesiredWordLength == "3" || DesiredWordLength == "4" || DesiredWordLength == "5" || DesiredWordLength == "6")
		{
			int32 WordLength = std::stoi(DesiredWordLength);			// make the deisred word length FText into an int32
			BCGame.ChooseSecretWord(WordLength);
			LengthChosen = true;
		}
		else
		{
			std::cout << " Please enter a number from 3 to 6 or 'R' for random: ";
		}
	}
}

FText GetValidGuess()
{
	FText Guess = "";
	EGuessStatus Status = EGuessStatus::Invalid_Status;
	do
	{
		std::cout << "\n \n Guess #" << BCGame.GetCurrentTry() << ". ";// get a guess from the player
		std::cout << "Enter your guess: ";
		std::getline(std::cin, Guess);
		Status = BCGame.CheckGuessValidity(Guess);					// check the guess validity

		switch (Status)												// give the player the apropriate feedback
		{
		case EGuessStatus::Wrong_Length:
			std::cout << "Please enter a " << BCGame.GetHiddenWordLength() << " letter word.";
			break;

		case EGuessStatus::Not_Lowercase:
			std::cout << "Please enter your guess in all lower case ";
			break;

		case EGuessStatus::Not_Isogram:
			std::cout << "Please enter an isogram, a word with no repeating letters.";
			break;

		default:
			break;

		}
	} while (Status != EGuessStatus::OK);							// loop until the player inputs a valid guess
	return Guess;
}

bool AskToPlayAgain()
{
	std::cout << "Do you wish to continue? Y/N: ";
	FText Response = "";
	std::getline(std::cin, Response);
		
	if (Response[0] == 'y' || Response[0] == 'Y')
	{
		std::cout << "\n";
		return true;
	}
	else if (Response[0] == 'n' || Response[0] == 'N')
	{
		std::cout << "\n";
		return false;
	}
	AskToPlayAgain();
	return true;
}

void PrintGameSummary()
{
	if (BCGame.IsGameWon() == true)
	{
		std::cout << " \n Congratulations! The word was " << BCGame.GetMyHiddenWord() << "! ";
	}
	else
	{
		std::cout << " \n The word was: " << BCGame.GetMyHiddenWord() << ". Better luck next time! ";
	}
}

FBullCowGame.h

#pragma once
#include <string>

using FString = std::string;
using int32 = int;

struct FBullCowCount 
{
	int32 Bulls = 0;
	int32 Cows = 0;
};

enum class EGuessStatus
{
	OK,
	Not_Isogram,
	Wrong_Length,
	Not_Lowercase,
	Invalid_Status
};

class FBullCowGame 
{
public:

	FBullCowGame(); // constructor

	int32 GetCurrentTry() const;
	int32 GetHiddenWordLength() const;
	bool IsGameWon() const;
	FString GetMyHiddenWord() const;
	void Reset();
	FString ChooseSecretWord(int32 NumberOfLetters);
	int32 GetMaxTries() const;
	EGuessStatus CheckGuessValidity(FString Guess) const;
	FBullCowCount SubmitValidGuess(FString);

private: // see constructor for initialisation

	int32 MyCurrentTry;
	FString MyHiddenWord;
	bool bIsGameWon;

	bool IsIsogram(FString) const;
	bool IsLowercase(FString) const;
};

FBullCowGame.cpp

#include "FBullCowGame.h"
#include <map>
#include <ctime>
#define TMap std::map

using int32 = int;

// Default constructor
FBullCowGame::FBullCowGame() { Reset(); }

// Getter functions
int32 FBullCowGame::GetCurrentTry() const { return MyCurrentTry; }
int32 FBullCowGame::GetHiddenWordLength() const { return MyHiddenWord.length(); }
bool FBullCowGame::IsGameWon() const { return bIsGameWon; }
FString FBullCowGame::GetMyHiddenWord() const { return MyHiddenWord; }

// Functions
void FBullCowGame::Reset()
{
	bIsGameWon = false;
	MyCurrentTry = 1;
	return;
}

FString FBullCowGame::ChooseSecretWord(int32 NumberOfLetters) // chooses a secret word from a collection of isograms based on the player's choice of word length
{
	srand(time(NULL));

	FString ThreeLetterWords[110] =
	{
		"ace", "air", "ash", "alp", "awe", "axe", "bag", "bed", "boy", "bus",
		"cab", "cam", "car", "cat", "cel", "chi", "cob", "cod", "cup", "cue",
		"dam", "dot", "dye", "ear", "eat", "ego", "elf", "elk", "fan", "fat",
		"fog", "fox", "fur", "gap", "gas", "gym", "ham", "hat", "ice", "ink",
		"ion", "ivy", "jab", "jag", "jam", "jaw", "jay", "jet", "job", "jog",
		"key", "lab", "lad", "lam", "lap", "led", "lei", "leg", "lip", "log",
		"lux", "mac", "man", "map", "mat", "maw", "may", "mud", "mug", "nap",
		"net", "nog", "nut", "oaf", "oak", "oar", "oat", "ohm", "oil", "ore",
		"owl", "pad", "pan", "paw", "pay", "pea", "pen", "pet", "pie", "pig",
		"pin", "pit", "ply", "rat", "ray", "rom", "sac", "spa", "spy", "sun",
		"toy", "tux", "urn", "van", "wax" ,"wok" ,"yak" , "yam", "yew", "yen",
	};

	FString FourLetterWords[136] =
	{
		"ache", "acid", "aloe", "alto", "ankh", "ante", "apex", "arch", "aunt", "auto",
		"axel", "bail", "bait", "band", "bang", "bank", "bard", "barn", "base", "bath",
		"bead", "beak", "beam", "bean", "bear", "bend", "bias", "bind", "bite", "bump",
		"cape", "clue", "cola", "deal", "dive", "exam", "face", "fact", "feat", "fern",
		"feta", "feud", "film", "firm", "fish", "fist", "game", "gear", "girl", "glow",
		"gnat", "gold", "golf", "hack", "haze", "head", "heat", "help", "home", "horn",
		"icon", "idea", "iron", "jail", "jean", "jolt", "jump", "kart", "kelp", "kilo",
		"lace", "lash", "lawn", "leaf", "leap", "lion", "lock", "lure", "lute", "mace",
		"melt", "milk", "mint", "mold", "name", "newt", "norm", "oath", "pace", "pack",
		"pawn", "pelt", "pile", "pine", "quay", "ramp", "rice", "rope", "sack", "sage",
		"sand", "shop", "show", "sign", "silk" ,"silo" ,"skin" , "sled", "soap", "soda",
		"soup", "star", "suit", "tail", "taxi", "toad", "tomb", "tram", "trap", "unit",
		"user", "vale", "veil", "vest", "vice", "wait", "walk", "wash", "wind", "wing",
		"wolf", "worm", "yarn", "yeti", "zeal" ,"zinc"
	};

	FString FiveLetterWords[143] =
	{
		"acorn", "adult", "alder", "angle", "ankle", "audit", "bacon", "badge", "bagel", "beach",
		"bonus", "brain", "break", "broth", "brute", "cabin", "cable", "cigar", "claim", "clerk",
		"cloud", "coble", "comet", "dance", "depot", "drain", "extra", "fable", "facet", "faith",
		"feast", "felon", "femur", "fiber", "fight", "flock", "flour", "forum", "gecko", "ghost",
		"ghoul", "giant", "gland", "glove", "gnome", "growl", "habit", "haiku", "haven", "house",
		"human", "image", "ivory", "kanji", "labor", "lapis", "laser", "lotus", "lumen", "lunge",
		"lymph", "march", "marsh", "mocha", "molar", "month", "motel", "nacho", "niche", "noble",
		"noise", "nomad", "ocean", "olive", "opera", "ounce", "padle", "paint", "patch", "phase",
		"pilot", "plain", "plume", "punch", "purse", "quack", "quake", "quilt", "quirk", "radio",
		"ramen", "realm", "recap", "rhino", "rivet", "roman", "saber", "sauce", "sault", "scalp",
		"scare", "scarf", "scrap", "screw", "shirt" ,"shock" ,"slant" , "slate", "slide", "slime",
		"slope", "sloth", "smirk", "smile", "straw", "stray", "study", "table", "talon", "tango",
		"thumb", "token", "topic", "torch", "tramp", "tribe", "tunic", "valet", "valse", "vinyl",
		"viper", "vista", "visor", "voice", "voter" ,"wafer", "wagon", "water", "wheat", "witch",
		"yacht", "yield", "zebra"
	};

	FString SixLetterWords[129] =
	{
		"action", "agency", "almond", "answer", "anthem", "ascent", "aspect", "author", "badger", "bakery",
		"bleach", "blight", "breath", "bridge", "bronze", "brunch", "bushel", "butler", "camper", "carpet",
		"cashew", "casino", "castle", "cavern", "chapel", "choker", "chorus", "cinder", "clause", "client",
		"climax", "combat", "curfew", "denial", "design", "domain", "dynamo", "eclair", "falcon", "family",
		"fedora", "felony", "fiance", "fiasco", "fidget", "flambe", "folder", "forest", "format", "garden",
		"glider", "goblet", "goblin", "hacker", "hockey", "hornet", "icebox", "income", "incult", "jacket",
		"jingle", "judoka", "keypad", "knight", "larynx", "latino", "lawyer", "legion", "locust", "magnet",
		"magpie", "maiden", "mantis", "matrix", "matron", "minute", "minuet", "motive", "muscle", "nectar",
		"object", "orchid", "orphan", "parcel", "pardon", "parent", "parody", "patrol", "patron", "pencil",
		"phrase", "pigeon", "pirate", "plague", "planet", "praise", "quartz", "refund", "region", "relish",
		"retina", "rocket", "rodent", "rumble", "sample" ,"scream" ,"sculpt" , "slicer", "sliver", "sludge",
		"smoker", "string", "stripe", "sundae", "symbol", "tavern", "teabox", "teacup", "trophy", "upload",
		"utopia", "vendor", "virtue", "walnut", "whisky", "wrench", "zealot", "zenith", "zombie"
	};
	
	if (NumberOfLetters == 3)
	{
		int32 RandIndex = rand() % 110 + 1;
		FString SecretWord = ThreeLetterWords[RandIndex];
		MyHiddenWord = SecretWord;
		return SecretWord;
	}

	else if (NumberOfLetters == 4)
	{
		int32 RandIndex = rand() % 136 + 1;
		FString SecretWord = FourLetterWords[RandIndex];
		MyHiddenWord = SecretWord;
		return SecretWord;
	}

	else if (NumberOfLetters == 5)
	{
		int32 RandIndex = rand() % 143 + 1;
		FString SecretWord = FiveLetterWords[RandIndex];
		MyHiddenWord = SecretWord;
		return SecretWord;
	}

	else if (NumberOfLetters == 6)
	{
		int32 RandIndex = rand() % 129 + 1;
		FString SecretWord = SixLetterWords[RandIndex];
		MyHiddenWord = SecretWord;
		return SecretWord;
	} 
}
int32 FBullCowGame::GetMaxTries() const
{
	TMap<int32, int32> WordLengthToMaxTries{ { 3, 6 },{ 4, 9 },{ 5, 10 },{ 6, 16 },{ 7, 20 } };
	return WordLengthToMaxTries[MyHiddenWord.length()];
}

EGuessStatus FBullCowGame::CheckGuessValidity(FString Guess) const
{
	if (!IsIsogram(Guess)) 
	{
		return EGuessStatus::Not_Isogram;
	}
	else if (!IsLowercase(Guess))  
	{
		return EGuessStatus::Not_Lowercase;
	}
	else if (Guess.length() != GetHiddenWordLength()) 
	{
		return EGuessStatus::Wrong_Length;
	}
	else
	{
		return EGuessStatus::OK;
	}
}

FBullCowCount FBullCowGame::SubmitValidGuess(FString Guess)			// receives a valid guess and increments Tries
{
	MyCurrentTry++;

	FBullCowCount BullCowCount;

	int32 HiddenWordLength = MyHiddenWord.length();
	for (int32 HWChar = 0; HWChar < HiddenWordLength; HWChar++)		// loop through all letters in the hidden word
	{
		for (int32 GChar = 0; GChar < HiddenWordLength; GChar++)	// for each of the hidden words letters, loop through all of the guesses letters
		{
			if (Guess[GChar] == MyHiddenWord[HWChar])				// and compare them
			{
				if (HWChar == GChar)								// if they match and are in the same position, they are Bulls
				{
					BullCowCount.Bulls++;
				}
				else												// if they just match, they are Cows
				{
					BullCowCount.Cows++;
				}
			}
		}
	}
	if (BullCowCount.Bulls == HiddenWordLength)						// if the player gets all Bulls, the game is won
	{
		bIsGameWon = true;
	}

	else 
	{
		bIsGameWon = false;
	}
	return 	BullCowCount;
}

bool FBullCowGame::IsIsogram(FString Guess) const
{
	if (Guess.length() <= 1) { return true; }						// treat 0 and 1 letter words as isograms

	TMap<char, bool> LetterSeen;									// TMap setup

	for (auto Letter : Guess)										// loop through all the letters of the guess
	{
		Letter = tolower(Letter);									//make the letter lower case and the function case insensitive by extension
		if (LetterSeen[Letter])										// if the letter has been added to the map, it's value will have been set to "true"
		{
			return false;											// we do NOT have an isogram
		}
		else
		{
			LetterSeen[Letter] = true;								// add the letter to the map, set it's value to "true"
		}
	}
	return true;													// The guess is a verified isogram
}

bool FBullCowGame::IsLowercase(FString Word) const
{
	if (Word.length() < 1) { return false; }
	for (auto Letter : Word)										// loop through the letters of the word
	{
		if (!islower(Letter))										// check if the letter is lowercase
		{
			return false;											// returns false if the lowercase check is failed by any of the letters
		}
	}
	return true;													// returns true if none of the letters are upper case
}

Hi Guys!
I just finished section 2.

I Added two small features:

  • chance to select the length of the word
  • The word is randomly chosen by an array based on the length of the word

This is my code:

main.cpp

     #include <iostream>
    #include <string>
    #include "FBullCowGame.h"
    using FText = std::string;
    using int32 = int;

    void PrintIntro();
    void PlayGame();
    bool AskToPlayAgain();
    FText GetValidGuess();

    FBullCowGame BCGame;
    int main()
    {
    	do
    	{
    		BCGame.Reset();
    		PrintIntro();
    		PlayGame();
    		BCGame.PrintGameSummary();
    	} while (AskToPlayAgain());
    	return 0;
    }

    //introduce to the game
    void PrintIntro()
    {
    	std::cout << "Welcome to Bulls and Cows, a fun word game.\n\n";
    	FString WordLenght = "";
    	int32 n;
    	while (true)
    	{
    		WordLenght = "";
    		n = 0;
    		std::cout << "Select word lenght (3-7): ";
    		std::getline(std::cin, WordLenght);
    		std::cout << "\n";
    		try
    		{
    			n = std::stoi(WordLenght);
    		}
    		catch (std::invalid_argument)
    		{
    			std::cout << "Please enter a number!\n\n";
    			continue;
    		}
    		catch (std::out_of_range)
    		{
    			std::cout << "Please enter a number between 3 and 7!\n\n";
    			continue;
    		}

    		if (n < 3 || n>7)
    		{
    			std::cout << "Please enter a number between 3 and 7!\n\n";
    			continue;
    		}
    		BCGame.SetWord(n);
    		break;
    	}
    }

    //Loop continually until the user gives a valid guess 
    FText GetValidGuess()
    {
    	FText Guess = "";
    	EGuessStatus status = EGuessStatus::Invalid_Status;
    	do
    	{
    		//get a guess from the player
    		std::cout << "Try: " << BCGame.GetCurrentTry() << " of " << BCGame.GetMaxTries() << ". Enter your guess: ";
    		Guess = "";
    		std::getline(std::cin, Guess);

    		//check status and give feedback
    		status = BCGame.CheckGuessValidity(Guess);
    		switch (status)
    		{
    		case EGuessStatus::Wrong_lenght:
    			std::cout << "Please enter a " << BCGame.GetGuessWordLenght() << " letter word\n\n";
    			break;
    		case EGuessStatus::Not_Isogram:
    			std::cout << "Please enter a word without repeating letters\n\n";
    			break;
    		case EGuessStatus::Not_Lowercase:
    			std::cout << "Please enter all lowercase letters\n\n";
    			break;
    		default:
    			break;	//assuming the guess is valid
    		}
    	} while (status != EGuessStatus::OK);	//keep looping until we get no errors
    	return Guess;
    }
    void PlayGame()
    {
    	int32 maxTry = BCGame.GetMaxTries();
    	while (!BCGame.IsGameWon() && BCGame.GetCurrentTry() <= maxTry)
    	{
    		FText Guess = GetValidGuess();




    		//submit a valid guess to the game, and receive counts
    		FBullCowCount BullCowCount = BCGame.SubmitValidGuess(Guess);

    		std::cout << "Bulls: " << BullCowCount.Bulls;
    		std::cout << " Cows: " << BullCowCount.Cows << "\n\n";
    	}
    }

    bool AskToPlayAgain()
    {
    	std::cout << "Do you want to play again? (y/n) ";
    	FText response = "";
    	std::getline(std::cin, response);
    	std::cout << "\n";
    	return (response[0] == 'y' || response[0] == 'Y');
    }`

FBullCowGame.h

#pragma once
#include <string>
#include <map>
using int32 = int;
using FString = std::string;
#define TMap std::map


struct FBullCowCount
{
	int32 Bulls = 0;
	int32 Cows = 0;
};
struct Words
{
	FString WordsToGuess[10];
};
enum EGuessStatus
{
	Invalid_Status,
	OK,
	Not_Isogram,
	Not_Lowercase,
	Wrong_lenght
};
class FBullCowGame
{
public:
	FBullCowGame();
	void Reset();
	void PrintGameSummary();
	void SetWord(int32 n);
	int32 GetGuessWordLenght() const;
	int32 GetMaxTries() const;
	int32 GetCurrentTry() const;
	bool IsGameWon() const;
	EGuessStatus CheckGuessValidity(FString);
	FBullCowCount SubmitValidGuess(FString);

private:
	int32 MyCurrentTry;
	FString MyHiddenWord;
	bool bGameWon;
	bool IsIsogram(FString) const;
	bool IsLowerCase(FString) const;
	TMap<int32, Words>WordsMap;

};

FBullCowGame.cpp

#include "FBullCowGame.h"
#include <iostream>
#include <map>
#define TMap std::map
using int32 = int;
using FString = std::string;
int32 FBullCowGame::GetMaxTries() const 
{ 
	TMap<int32, int32>WordLenghtToMaxTries = { {3,4},{4,7},{5,10},{6,16},{7,20} };
	return WordLenghtToMaxTries[MyHiddenWord.length()];
}

int32 FBullCowGame::GetCurrentTry() const { return MyCurrentTry; }

FBullCowGame::FBullCowGame()
{
	Reset();
	WordsMap.insert(std::pair<int32, Words>(3, { "man","job","mix","box","cup","one","gym","big","boy","day" }));
	WordsMap.insert(std::pair<int32, Words>(4, { "rock","quiz","wolf","jump","base","true","blue","burn","busy","work" }));
	WordsMap.insert(std::pair<int32, Words>(5, { "angle","angry","mouse","alone","actor","audio","blind","sword","brain","broad" }));
	WordsMap.insert(std::pair<int32, Words>(6, { "planet","action","agency","source","answer","active","advise","bridge","branch","custom" }));
	WordsMap.insert(std::pair<int32, Words>(7, { "mistake","bearing","careful","century","convert","explain","dynamic","improve","include","kitchen" }));
}

void FBullCowGame::Reset()
{
	MyCurrentTry = 1;
	bGameWon = false;
}

void FBullCowGame::PrintGameSummary()
{
	if (bGameWon)
	{
		std::cout << "Well Done! You Win!\n";
		return;
	}
	std::cout << "You Lose. Better luck next time!\n";
}

void FBullCowGame::SetWord(int32 n)
{
	
	int random = std::rand() % 11;
	MyHiddenWord = WordsMap[n].WordsToGuess[random];
}

int32 FBullCowGame::GetGuessWordLenght() const
{
	return MyHiddenWord.length();
}

bool FBullCowGame::IsGameWon() const
{
	return bGameWon;
}

EGuessStatus FBullCowGame::CheckGuessValidity(FString Guess)
{
	if (!IsIsogram(Guess))	//if the guess is not an isogram
	{
		return EGuessStatus::Not_Isogram;
	}
	else if (!IsLowerCase(Guess))	//if the guess is not all lowercase
	{
		return EGuessStatus::Not_Lowercase;
	}
	else if (GetGuessWordLenght() != Guess.length())	//if the guess lenght is wrong
	{
		return EGuessStatus::Wrong_lenght;
	}
	else
	{
		return EGuessStatus::OK;
	}
}
//receives a valid guess, increments turn, and return count
FBullCowCount FBullCowGame::SubmitValidGuess(FString Guess)
{
	MyCurrentTry++;
	FBullCowCount BullCowCount;
	int32 WordLenght = MyHiddenWord.length();	//assuming same lenght as guess

	//loop through all letters in the guess
	for (int32 i = 0; i < WordLenght; i++)
	{//compare letters against the hidden word
		for (int32 j = 0; j < WordLenght; j++)
		{
			if (MyHiddenWord[i] == Guess[j]) // if they match then
			{

				if (i == j)//if they are in the same place
				{
					BullCowCount.Bulls++; //increment bulls
				}
				else
				{
					BullCowCount.Cows++; //increment cows
				}
			}
		}


	}
	if (BullCowCount.Bulls == WordLenght)
		bGameWon = true;
	return BullCowCount;
}

bool FBullCowGame::IsIsogram(FString Word) const
{
	if (Word.length() < 2)
		return false;

	TMap<char, bool> letterSeen;
	for (char letter : Word)
	{
		letter = tolower(letter);
		if (letterSeen[letter])
			return false;
		letterSeen[letter] = true;
	}
	return true;
}

bool FBullCowGame::IsLowerCase(FString Word) const
{
	for (auto letter : Word)
	{
		if (!islower(letter))
			return false;
	}
	return true;
}

I merely added so that the player can chose the length of word thereby choosing the difficulty and also the conditions if the player chooses a word length other than what’s on the instructions.

I have implemented a number of changes, based off feedback from playtesting, as well as features that I thought should be included.

Firstly, I explained the game more explicitly - my playtesters had no idea what they were doing until I explained the rules of the game to them. They also needed the concept of Bulls and Cows more clearly explained. I thought it was worth explaining what an isogram was as well, to try to prevent players from every reaching the ‘repeated letter’ error message, though that is still in there as a safety measure!

Secondly, I implemented a system for players to choose the length of word that they wanted to play with. It currently simply selects from an array containing 5 words, varying from 3 to 7 letters in length. I am assuming that we will be importing a long list of words to play with in the next section, so I did not attempt that myself.

As a result of implementing a choice of word length, I decided to implement error messages for the player’s choice here. Error messages exist for void inputs, inputs which aren’t a number, and numbers which are outside the specified range.

I also updated guess validity checking to include checking for guesses containing a space, and checking for guesses that have already been tried (i.e. entering the same exact word twice).

Finally, I updated the game over message to reveal the hidden word to the player.

There are still changes that I want to make, although many of them wont be possible until I take the game across into UE4, such as implementing an optional system to highlight the bulls in the word, or an interface to allow players to mark off letters that they know aren’t in the word, or that they know aren’t in certain positions within the word.

I think I’m getting the hang of C++ and I’m keen to start the next section ASAP! :smiley:

Hi everybody,

Here is my code. I added a randomizer that determines what word the player has to guess.

Bulls&Cows.zip (3.6 KB)

Nice work @all members!
Here is my submission (just added ability to see cows after guess submission):

Notable features:

  • Ability to see guess progress (how many cows and bulls were hit as well at what positions!).
  • Ability to choose a difficulty.
  • Ability to add word lists via text file.
  • And that’s all!

Screen shots of play through:


Note: In the second screenshot it says “formidable is not the correct length” that is because I had hit the up arrow when entering the word and the guess was submitted along with the character for the up arrow which in this case comes up as invisible.

Note: I had started the course a while back but took a hiatus so the game might not match all latest requirements/suggestions for the game. Cheers!

Privacy & Terms