Bulls and Cows @vid77 end. my source code snapshot w/notes to self

BullCowCartridge.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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


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;
	void SetupGameSession();
	void EndTheGame();
	void YouWon();
	void ProcessTheGuess(const FString& Guess);
	bool IsIsogram(const FString Word) const;
	TArray<FString> GetValidWords(const TArray<FString>& WordList) const;
	//REMEMBER! Guess, Word and WordList do not even need to be written here, i think video 77 is where Mike says that.! 
	//DO not make the mistake of thinking that this here is what defines the variable names used in the .cpp

	// Your declarations go below!
	private:
	FString HiddenWord;
	int32 ChancesLeft;
	bool bGameOver;
	TArray<FString> Words;
};

BullCowCartridge.cpp

//My second attempt at comprehending c++. first was the previous tutorial.
//Any comments within are notes for myself and not meant as fact or instruction
//and are also probably way off base


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

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

    //I prefered the pull from file route
    const FString WordListPath = FPaths::ProjectContentDir() / TEXT("WordLists/WordsList.txt");
    FFileHelper::LoadFileToStringArray(Words, *WordListPath);

    SetupGameSession();     //Call to initial setup of the game
}

void UBullCowCartridge::OnInput(const FString& PlayersInput) // When the player hits enter
{
    if (bGameOver) //this if statement ensures only Enter on a blank line will reset the game when over
    {
        if (PlayersInput.Len() == 0)
        {
            ClearScreen();
            SetupGameSession();
            return;
        }
    }

    ProcessTheGuess(PlayersInput);     //Call to function that processes the guess
}


void UBullCowCartridge::SetupGameSession() //Game setup/configuration here
{
    bGameOver = false;
    HiddenWord = GetValidWords(Words)[FMath::RandRange(0, GetValidWords(Words).Num() -1)]; //Neat
    ChancesLeft = HiddenWord.Len() + 1;    // sets the amount of chances

    PrintLine(TEXT("Welcome to the Bull/Cow Game\n"));
    PrintLine(TEXT("Guess the %i letter isogram and press enter\n"), HiddenWord.Len());
    PrintLine(TEXT("You have %i guesses"), ChancesLeft);
    //PrintLine(TEXT("Debug: # of Valid Words = %i"), GetValidWords(Words).Num());
    PrintLine(TEXT("Debug: Hidden word is %s"), *HiddenWord);
}

void UBullCowCartridge::EndTheGame() //Calls back to OnInput function
{
    bGameOver = true;
    PrintLine(TEXT("The hidden word was %s.\n\n -=[Press Enter to play again]=-\n"), *HiddenWord);
}

void UBullCowCartridge::YouWon()
{
    //PrintLine(TEXT("Will end up playing 'Success' sound here"));
}

void UBullCowCartridge::ProcessTheGuess(const FString& Guess) //Actually process the entered guess
{
    if (bGameOver) //This is required as part of ensuring that only an empty line restarts the game
    {
        EndTheGame();
        return;
    }

    if (Guess.Len() == 0) //ignore empty guess during gameplay
    {
        return;
    }

    if (Guess.Len() != HiddenWord.Len()) //if the guess has the incorrect amount of letters, does not penalize. also remind how many letters are expected
    {
        PrintLine(TEXT("\nIncorrect amount of letters.\n"));
        PrintLine(TEXT("\nGuess again. %i letters\n"), HiddenWord.Len());
        return;
    }

    if (Guess == HiddenWord) //yay! Success!
    {
        PrintLine(TEXT("\nCorrect!\n"));
        EndTheGame();
        PrintLine(TEXT("\nYou Won!!"));
        YouWon();
        return;
    }

    if (!IsIsogram(Guess)) //Executes if not an isogram, penalty issued
    {
        ChancesLeft--;
        if (ChancesLeft < 1) //if 0 or less chances left, call EndTheGame function
        {
            PrintLine(TEXT("\n%s is not an Isogram\n(Cannot have repeated letters)."), *Guess);
            PrintLine(TEXT("\nYou have %i guesses remaining\n"), ChancesLeft);
            EndTheGame();
            return;
        }
        if (ChancesLeft >= 1) //If 1 or more chances left, display amount of chances left and allow continued play.
        {
            PrintLine(TEXT("\n%s is not an Isogram\n\n(Cannot have repeated letters)."), *Guess);
            PrintLine(TEXT("\nYou have %i guesses remaining\n"), ChancesLeft);
            return;
        }
  
    }

    if (IsIsogram(Guess)) //Executes if the guess Is an isogram, but not the correct word. Penalizes
    {
        ChancesLeft--;
        if (ChancesLeft < 1) //if 0 or less guesses left, calls EndTheGame function
        {
            PrintLine(TEXT("\nYou are out of guesses!\n"), *HiddenWord);
            EndTheGame();
            return;
        }

        if (ChancesLeft >= 1) //if 1 or more chances left, displays amount of chances left and allow continued play.
        {
            PrintLine(TEXT("\nSorry %s is not the correct word.\n\nYou have %i guesses remaining\n"), *Guess, ChancesLeft);
            return;
        }
    }
}

/* Checks to see if the guess is an isogram. It does this by comparing each letter to the ones after it based on word length.
If they find a match to themself, it is not an isogram, so, check returns false. Will also be used to check our list of words so
they can be filtered by the GetValidWords function.
I think this is pretty elegant and cool*/

bool UBullCowCartridge::IsIsogram(FString Word) const
{
    for (int32 Index = 0; Index < Word.Len(); 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;

/* This type of for loop iterates over the entire collection. so, self incrementing. hence the lack of the usual formatting.
seems to be called a 'ranged for loop'. although easier to read, i have a slightly harder time recognizing the logic here.
it seems that for every/all WordsWithin our WordList, which is actually the Words that got sent here from line 48,
which initially came in as a TArray of FStrings from the external file by way of lines 15 and 16 .. I think..
it then takes any words that are atleast 4 letters long and less than 9 letters long and sends them to be verified isogram or not isogram.
then, the words that survived those filters get put in the scoped TArray known as ValidWords. Which somehow becomes the new Words??
If i even happened to get the rest of it correct, this is where it loses me. I do not understand how ValidWords becomes Words, or if 
that is what is really even happening.
 */
    for (FString WordsWithin : WordList)
        if (WordsWithin.Len() >= 4 && WordsWithin.Len() <= 8 && IsIsogram(WordsWithin))
        {
            ValidWords.Emplace(WordsWithin);
        }
    return ValidWords;
}

This is not verbatim code. I added and perhaps changed a tiny bit.

Privacy & Terms