My Code So Far

My Header

// 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& PlayerInput) override;
	// Game Loop
	void SetUpGame();
	void WelcomePlayer() const;
	void EndGame();
	// Processing Player Guess
	void ProcessGuess(const FString& Guess);
	void PlayerDidNotWin(const FString& Guess);
	void PlayerLostLife();
	// Processing Words
	void BullCowsNumber(const FString& Word) const;
	bool IsIsogram(const FString& Word) const;
	TArray<FString> GetValidWords(const TArray<FString>&) const;

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

C++ File

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

#include "BullCowCartridge.h"

#include "Misc/FileHelper.h"

#include "Misc/Paths.h"

void UBullCowCartridge::BeginPlay() // When the game starts

{

    Super::BeginPlay();

    // Advanced Course Lesson to reduce compile times!

    const FString WordListPath = FPaths::ProjectContentDir() / TEXT("WordLists/HiddenWordList.txt");

    FFileHelper::LoadFileToStringArray(Words, *WordListPath);

    // We only need this done once

    WordList = GetValidWords(Words);

    SetUpGame();

    PrintLine(TEXT("The HiddenWord is %s"), *HiddenWord);           //Debug Line

    PrintLine(TEXT("It is %i characters Long"), HiddenWord.Len()); //Debug Line

    WelcomePlayer();

}

void UBullCowCartridge::OnInput(const FString& PlayerInput) // When the player hits enter

{

    ClearScreen();

    // If Game is over, ClearScreen() and SetupGame()

    if (bGameOver) {

        EndGame();

        return; // Using a return here to exit early to prevent Normal Game Loop

    }

    // Normal Game Loop

    // Check if Valid

    ProcessGuess(PlayerInput);

}

void UBullCowCartridge::SetUpGame()

{

    HiddenWord = WordList[FMath::RandRange(0, WordList.Num() - 1)];

    Lives = HiddenWord.Len();

    bGameOver = false;

}

void UBullCowCartridge::WelcomePlayer() const

{

    // Separating these may make it easier to simplify dialogue later

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

    PrintLine(TEXT("Guess the %i letter word!"), HiddenWord.Len());

    PrintLine(TEXT("You have %i lives."), Lives);

    PrintLine(TEXT("Please enter your guess and \nPress enter to continue..."));

}

void UBullCowCartridge::ProcessGuess(const FString& Guess)

{

    // No reason to check everything else if it already works

    if (Guess == HiddenWord)

    {

        PrintLine(TEXT("The Answer is %s"), *HiddenWord);

        EndGame();

        return;

    }

    if (HiddenWord.Len() != Guess.Len()) // Check Character Length

    {

        // Prompt to GuessAgain

        PrintLine(TEXT("The Hidden Word is %i characters long."), HiddenWord.Len());

        PrintLine(TEXT("You still have %i lives remaining. \nTry again!"), Lives);

        return;

    }

    if (!IsIsogram(Guess)) // Check if it is an Isogram

    {

        PrintLine(TEXT("No repeating Letters!\n"));

        PrintLine(TEXT("NOT"));

        PrintLine(TEXT("AN"));

        PrintLine(TEXT("ISOGRAM!!"));

        PrintLine(TEXT("You still have %i lives remaining. \nTry again!"), Lives);

        return;

    }

    // Answer is Valid, but not a victory, so now T or C

    PlayerDidNotWin(Guess);

}

void UBullCowCartridge::PlayerDidNotWin(const FString& Guess)

{

    //Didn't win? Now the bad stuff happens!

    BullCowsNumber(Guess);

    PlayerLostLife();

}

// This is taking care of itself, because there is only one way to win.

// Bull Cows Will be calculated separately

void UBullCowCartridge::PlayerLostLife()

{

    --Lives; // Keep it here because otherwise I'll be writting it twice.

    // Ask you dead or dying?

    if (Lives <= 0)

    {

        // If no lives, inform player they have perished.

        EndGame();

        return;  

    }

    if (Lives == 1)

    {

        // Warn them about how close they are to dying

        PrintLine(TEXT("YOU HAVE ONE FINAL LIFE!"));

        PrintLine(TEXT("YOU WILL PERISH IF YOU FAIL AGAIN"));

        return;

    }

    // If your not dead, your alive!

    // Prompt user for another guess

    PrintLine(TEXT("YOU MUST TRY AGAIN OR PERISH!"));

    PrintLine(TEXT("You have %i lives left"), Lives);

}

void UBullCowCartridge::BullCowsNumber(const FString& Word) const

{

    // PlaceHolder

    // We'll want to iterate through HiddenWord the player's Guess

    // Then we'll want to check for if a Player Guess Letter is in the same place as hidden word

    // We'll also want to check if a Player Guess contains a letter that is in HiddenWord

    int32 Bull = 0;

    int32 Cow = 0;

    // This will only be called after the user passes a verification check

    for (int32 Index = 0; Index < HiddenWord.Len(); ++Index)

    {

        for (int32 Comparison = Index; Comparison < Word.Len(); ++Comparison)

        {

            bool isCow = HiddenWord[Index] == Word[Comparison];

            bool isBull = HiddenWord[Index] == Word[Index];

            if (isCow)

            {

                ++Cow;
                --Bull; // Bull Will Pass the next check, otherwise we'd have too many

            }

            if (isBull)

            {

                ++Bull;
            }

        }

        // Bug fix

        // Bulls should never be negative

        if (Bull < 0)

        {

            Bull = 0;

        }

    }

    PrintLine(TEXT("You have %i Bulls"), Bull);

    PrintLine(TEXT("You have %i Cows"), Cow);

}

bool UBullCowCartridge::IsIsogram(const FString& Word) const

{

    // So used to brute forcing these, I'm rially happy making it more efficient

    // I'm checking up to the second to last because that was mentioned.

    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;

}

void UBullCowCartridge::EndGame()

{

    // If They've confirmed that they want to replay the game

    // Then let them re-initialize the game

    // This goes first so I have a way to exit early

    if (bGameOver) {

        // This is here because I feel like the EndGame function,

        // Should handle the events that occur EndGame()

        SetUpGame();

        WelcomePlayer();

        return; // Using a return here to exit early

    }

    // Set GameOver here so that next time function is called, we set the game up again

    bGameOver = true;

    // Extra shame to those that have failed

    // Also show user the hidden word for some closure

    if (Lives == 0) {

        PrintLine(TEXT("THEN PERISH"));

        PrintLine(TEXT("The hidden word was %s\n"), *HiddenWord);

    }

    else

    {

        PrintLine(TEXT("Oh yay. You won."));

        PrintLine(TEXT("The hidden word was %s"), *HiddenWord);

    }

    // Play Nice now

    PrintLine(TEXT("Press enter to play again..."));

}

TArray<FString> UBullCowCartridge::GetValidWords(const TArray<FString>& WordList) const

{

    TArray<FString> ValidWords;

    for (FString Word : WordList)

    {

        if ((Word.Len() >= 4) && (Word.Len() <= 8) && IsIsogram(Word))

        {

            ValidWords.Emplace(Word);

        }

    }

    return ValidWords;

}

I’m having a bit of an issue trying to work out the BullCowsNumber function. I’m getting a bit ahead of myself as always, but my code isn’t acting the way I want it to.

Maybe I’m doing something backwards. I’m almost there though. I’m very likely going about this in all the wrong ways.

Edit: I was on a track, but it wasn’t the right one, and I eventually did accidentally put my code backwards. Well that was fun! :slight_smile:

Privacy & Terms