BullCow Extra credit - Word Length Selection

So for the extra credit portion I decided add in the ability to choose the length of the word that you want go guess. I got hung up trying to figure out why a few things weren’t working but in the end got it figured all out.

BullCowCartridge.cpp

#include "BullCowCartridge.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Misc/Char.h"
#include "Misc/CString.h"

void UBullCowCartridge::BeginPlay() // When the game starts
{
    Super::BeginPlay();

    const FString WordListPath = FPaths::ProjectContentDir() / TEXT("WordLists/HiddenWordList.txt");
    FFileHelper::LoadFileToStringArrayWithPredicate(Isograms, *WordListPath, [](const FString& Word)
    {
        return Word.Len() >= 4 && Word.Len() <= 8 && IsIsogram(Word);
    });

    SetupGame();          
}

void UBullCowCartridge::OnInput(const FString& Input) // When the player hits enter
{
    if (bGameOver)
    {
        ClearScreen();
        SetupGame();
    }
    else if (!bLengthSet)
    {
        RequestLength(Input);

        if (bLengthSet)
        {
            InitializeGame();
        }  
    }
    else
    {
        ProcessGuess(Input);
    } 
}

void UBullCowCartridge::SetupGame()
{
    bGameOver = false;
    bLengthSet = false;  
    bIsInitialized = false;
    FWordLength WordLength = ProcessLength();
    WordMin = WordLength.Min;
    WordMax = WordLength.Max;

    PrintLine(TEXT("Welcome to Bull Cows!\n"));

    PrintLine(TEXT("The goal is to guess an isogram of the\nlength of your choice.\n"));

    PrintLine(TEXT("The shortest word is %i long."), WordMin);
    PrintLine(TEXT("The longest word is %i long.\n"), WordMax);
    PrintLine(TEXT("Please enter the length of a word you\nwould like to guess and hit enter."));
} 

//Builds the array of words of the length that you chose, sets lives, sets HiddenWord
void UBullCowCartridge::InitializeGame()
{
    IsogramsLength = LengthListBuild(Isograms);
    HiddenWord = IsogramsLength[FMath::RandRange(0, IsogramsLength.Num() - 1)]; 
    Lives = (HiddenWord.Len()) * 2; 
    bIsInitialized = true;
    
    PrintLine(TEXT("Guess the %i letter word."), HiddenWord.Len());
    PrintLine(TEXT("You have %i lives."), Lives);  

    PrintLine(TEXT("\nType in your guess and press enter to \ncontinue...")); // Prompt player for guess
}


void UBullCowCartridge::EndGame()
{
    bGameOver = true;
    PrintLine(TEXT("\nPress enter to play again..."));
}

void UBullCowCartridge::ProcessGuess(const FString& PlayerGuess)
{
    //Check for correct guess.
    if (PlayerGuess == HiddenWord)
    {
        ClearScreen();
        PrintLine(TEXT("\nYou guessed correctly!"));
        EndGame();
        return;
    }

    //Check if guess is the correct number of characters.
    if (PlayerGuess.Len() != HiddenWord.Len())
    {
        PrintLine(TEXT("The word is %i characters long!\nPlease enter a word with the correct\namount of characters...\n"), HiddenWord.Len()); 
        return;
    }

    //Check if the guess is an isogram.
    if (!IsIsogram(PlayerGuess))
    {
        PrintLine(TEXT("\nThe word you have entered is not an\nisogram..."));
        PrintLine(TEXT("\nPlease guess again...\n"));
        return;
    }
    
    PrintLine(TEXT("You did not guess correctly..."));
    PrintLine(TEXT("You have %i lives left...\n "), --Lives); 
    
    //check for remaining lives.
    if (Lives <= 0) 
    {
        ClearScreen();
        PrintLine(TEXT("You are out of lives!"));
        PrintLine(TEXT("The word was... %s"), *HiddenWord);
        PrintLine(TEXT("Better luck next time..."));
        EndGame();
        return;
    }

    //Show player the bulls and the cows
    FBullCowCount BCCount = GetBullCows(PlayerGuess);

    PrintLine(TEXT("You have %i Bulls and %i Cows"), BCCount.Bulls, BCCount.Cows);
}


// Checks that a word is an isogram.
bool UBullCowCartridge::IsIsogram(const FString& Word)
{
    for (int32 Index = 0; Index < Word.Len() - 1; Index++)
    {
        for (int32 Comparison = Index + 1; Comparison < Word.Len(); Comparison++)
        {
            if (Word[Index] == Word[Comparison])
            {
                return false;
            }
        }
    }
    return true;        
}

TArray<FString> UBullCowCartridge::GetValidWords(const TArray<FString>& WordList) const
{
    TArray<FString> ValidWords;
    
    for (FString WordInList : WordList)
    {
        if (WordInList.Len() >= 4 && WordInList.Len() <= 8 && IsIsogram(WordInList))
        {
            ValidWords.Emplace(WordInList);
        }
    }
    return ValidWords;
}

//Returns the amount of Bulls and Cows in the guess made by the player
FBullCowCount UBullCowCartridge::GetBullCows(const FString& Guess) const
{
    FBullCowCount Count;

      for (int32 GuessIndex = 0; GuessIndex < Guess.Len(); GuessIndex++)
    {
        if (Guess[GuessIndex] == HiddenWord[GuessIndex])
        {
            Count.Bulls++;
            continue;
        }

        for (int32 CowIndex = 0; CowIndex < Guess.Len(); CowIndex++)
        {
            if (Guess[GuessIndex] == HiddenWord[CowIndex])
            {
                Count.Cows++;
                break;
            }  
        }
    }  

    return Count;
}

//Will check and return length of the shortest and longest word in the list
FWordLength UBullCowCartridge::ProcessLength() const
{
    FWordLength Length;
    
    //Checks for the longest word
    for (int32 Index = 0; Index < Isograms.Num(); Index++)
    {
        if (Isograms[Index].Len() > Length.Max)
        {
            Length.Max = Isograms[Index].Len();
        }
    }
    
    Length.Min = Length.Max;

    //Checks for the shortest word
    for (int32 Index = 0; Index < Isograms.Num(); Index++)
    {
        if (Isograms[Index].Len() <= Length.Min)
        {
            Length.Min = Isograms[Index].Len();
        }
    }
    return Length;
}

//Checks that the length requested by the player is an integer and that it is within the valid range
void UBullCowCartridge::RequestLength(FString Input)
{
    ChoiceInt = 0;

    //Verifies that the input is an integer
    for (int32 Index = 0; Index < Input.Len(); Index++) 
    {
        if (isdigit(Input[Index]) == false) 
        {
            bIsValidInt = false;
            PrintLine(TEXT("You have not entered an integer..."));
            return;
        }
        else
        {
            bIsValidInt = true;
        }    
    }

    //Atoi is only used after the input has been confirmed to be an integer as it does not return any errors.
    ChoiceInt = FCString::Atoi(*Input);

    //Verifies that the integer is within the Min and Max lenth of words.
    if (ChoiceInt > WordMax || ChoiceInt < WordMin)
    {
        ClearScreen();
        PrintLine(TEXT("The number you have entered is not within the valid range.\n"));
        PrintLine(TEXT("The range is %i - %i"), WordMin, WordMax);
        bIsValidInt = false;
        return;
    }

    //PrintLine(TEXT("The number entered: %i"), ChoiceInt);
    bLengthSet = true;
    ClearScreen();
    return;
}

// Returns the words that match the requested length from the list of all isograms
TArray<FString> UBullCowCartridge::LengthListBuild(const TArray<FString>& IsogramsList) const
{
    TArray<FString> ValidWords;
    
    for (int32 Index = 0; Index < IsogramsList.Num(); Index++)
    {
        if (IsogramsList[Index].Len() == ChoiceInt)
        {
            ValidWords.Emplace(IsogramsList[Index]);
        }
    }
    return ValidWords;
}
 

BullCowCartridge.h

#pragma once

#include "CoreMinimal.h"
#include "Console/Cartridge.h"
#include "BullCowCartridge.generated.h"

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

struct FWordLength
{
	int32 Min = 0;
	int32 Max = 0;
};

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BULLCOWGAME_API UBullCowCartridge : public UCartridge
{
	GENERATED_BODY()

	public:
	virtual void BeginPlay() override;
	virtual void OnInput(const FString& Input) override;
	FWordLength ProcessLength() const;
	void RequestLength(FString Input);
	void ProcessGuess(const FString& PlayerGuess);
	void SetupGame();
	void EndGame();
	void InitializeGame();
	static bool IsIsogram(const FString& Word);
	TArray <FString> GetValidWords(const TArray<FString>& WordList) const;
	TArray <FString> LengthListBuild(const TArray<FString>& IsogramList) const;
	FBullCowCount GetBullCows(const FString& Guess) const;
	

	// Your declarations go below!
	private:
	int32 Lives;
	int32 WordMin = 0;
	int32 WordMax = 0;
	int32 ChoiceInt;
	FString HiddenWord;
	bool bIsValidInt;
	bool bLengthSet;
	bool bGameOver;
	bool bIsInitialized;
	TArray<FString> Words;
	TArray<FString> Isograms;
	TArray<FString> IsogramsLength;
};

2 Likes

Awesome job! :grin:

Hello,
No one will give you a small payday advance, and even if they could, the bank will take two or three business days to get you the money. That’s not enough time. And even if you did get it, they’ll deposit it in your checking account and use the amount to cover your existing overdraft first rather than handing you the cash, which you need now