My Bull Cows Extras: Help and Scorescreen, Better Indexing, and Winstreaks

Hey there! I’m relatively a newbie at coding, but I still took some time to add some additional features regarding managing scores and usage improvements. I also tuned the environment to give it a significantly vibrant appearance with some comfortable colors. Usually I’d tinker those a lot because I’ve mainly practiced heavily on art and design before.

One significant thing I recognized was that UE4 would crash when guesses are longer than the hidden word. I patched that up on the lambda of the GetBullCows function by adding a condition to keep the GuessIndex lower than the Guess from scanning past the HiddenIndex. It doesn’t break the gameplay loop nor the game itself, so that’s a plus.

My Code:

Main

// Fill out your copyright notice in the Description page of Project Settings.
#include "BullCowCartridge.h"
#include "HiddenWordList.h"

void UBullCowCartridge::BeginPlay() // When the game starts
{
    Super::BeginPlay();
    PrintLine(TEXT("|INPUTS| Instructions: needhelp | Score Screen: needscore |"));
    SetupGame();

    //PrintLine(TEXT("The number of possible words is %i."), Words.Num());
    //PrintLine(TEXT("The number of valid words is %i."), GetValidWords(Words).Num());
}

void UBullCowCartridge::OnInput(const FString& PlayerInput)   // When the player hits enter
{
    if (bGameOver)                                      // If game is over, reset
    {
        ClearScreen();
        SetupGame();
    }
    else                                                // If game is not over, continue playing
    {
        ProcessGuess(PlayerInput);
    }
}

void UBullCowCartridge::SetupGame()
{
    PrintLine(TEXT("Welcome to Bull Cows, pardner!"));

    Isograms = GetValidWords(Words, Wins + 4);      // Takes words from our HiddenWordList header file
    HiddenWord = Isograms[FMath::RandRange(0,Isograms.Num() - 1)];
    
    if (RewardConvert(Lives) != 0)
    {
        PrintLine(TEXT("+%i BONUS LIVES!"), RewardConvert(Lives));
    }

    Lives = RewardConvert(Lives) + HiddenWord.Len() * 2;
    bGameOver = false;

    PrintLine(TEXT("ROUND: %i - Guess the %i letter word in %i lives!"), Wins + 1, HiddenWord.Len(), Lives);
    PrintLine(TEXT("Press tab to type and enter to input."));
    PrintLine(TEXT("[DBG]The HiddenWord %s is %i characters."), *HiddenWord, HiddenWord.Len()); //Debug Line
}

int32 UBullCowCartridge::RewardConvert(const int32& Performance) const
{
    if (Performance >= 9)
    {
        return 6;
    }
    if (Performance >= 6)
    {
        return 3;
    }return 0;
}

void UBullCowCartridge::EndGame()
{
    bGameOver = true;
    return;
}

int32 UBullCowCartridge::EndResult(const int32& Outcome)
{
    if (Outcome == 1)
    {
        Wins++;
    }
    if (Outcome == 2)
    {
        if (BestWins < Wins)
        {
            BestWins = Wins;
        }
        PrintLine(TEXT("GG! You've got %i wins.\nYour highest winstreak was %i"), Wins, BestWins);
        Wins = 0;
    }
    return Wins;
}

void UBullCowCartridge::ProcessGuess(const FString& Guess)
{
    if (Guess == TEXT("needhelp"))
    {
        HelpScreen(true);
        return;
    }

    if (Guess == TEXT("needscore"))
    {
        ScoreScreen(true);
        return;
    }

    FBullCowCount Scope = GetBullCows(Guess);   // Returns FBullCowCount, updated Bull Cows of guess. 
    PrintLine(TEXT("You have %i Bulls and %i Cows"), Scope.Bulls, Scope.Cows);
    
    if (Guess == HiddenWord)
    {
        PrintLine(TEXT("Gratz! Press Enter to continue. WINS: %i"), Wins + 1);
        if (Lives >= 6)
        {
            PrintLine(TEXT("%i lives remained. +%i bonus lives!"), Lives, RewardConvert(Lives));
        }
        EndResult(1);
        EndGame();
        return;
    }

    PrintLine(TEXT("Nope! %i lives remain."), --Lives); // Remove Life and show new lives

    if (Lives <= 0) // Check if lives > 0
    {
        PrintLine(TEXT("\nGame Over!"));
        PrintLine(TEXT("The Hidden Word was %s"), *HiddenWord);
        EndResult(2);
        EndGame();
        return;
    }

    if (Guess.Len() != HiddenWord.Len())     // Check right num of char and print
        {
            PrintLine(TEXT("Remember, it must be %i characters."), HiddenWord.Len());
        }

    if (!IsIsogram(Guess))                  // Invoker for checking if guess is Isogram. Takes Guess. 
        {
            PrintLine(TEXT("Not an Isogram. Letter repeats found!"));
            return;
        }
}

bool UBullCowCartridge::IsIsogram(const FString& Word) const    // Function for checking if guess is Isogram
{
    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>& WordsList, const int32 WinsCount) const // Wordlist chooses based on difficulty
{
    TArray<FString> ValidWords;

    for (FString EachWord : WordsList)  // For WordList's each named EachWord
    {
        if (WinsCount > 9)
            {
                if (EachWord.Len() >= 7 && EachWord.Len() <= 9 && IsIsogram(EachWord))
                {
                    ValidWords.Emplace(EachWord);   
                }            
            }
        else 
        {
            if (EachWord.Len() == WinsCount && IsIsogram(EachWord))
                {
                    ValidWords.Emplace(EachWord);   
                }            
        }

    }
    return ValidWords;
}

FBullCowCount UBullCowCartridge::GetBullCows(const FString& Guess) const
{
    FBullCowCount Count;
    for (int32 GuessIndex = 0; GuessIndex < Guess.Len() && GuessIndex < HiddenWord.Len(); GuessIndex++)      // Checks how many characters are right in the guess. Bulls are position. 
    {                                                                       // Failsafe past AND stops Guess Index from going past HiddenWord Array max.
        if (Guess[GuessIndex] == HiddenWord[GuessIndex])                    // If Guess char is == Hiddenword Char Bull +1. Goes position to position. 
        {
            Count.Bulls++;
            continue;
        }
        for (int32 HiddenIndex = 0; HiddenIndex < HiddenWord.Len(); HiddenIndex++)  // Checks how many characters match up from the hidden word's view and length. Cows are char. 
        {                                                                           // If 1 bull, then character and position is correct! 
            if (Guess[GuessIndex] == HiddenWord[HiddenIndex])                       // Chosen GuessIndex spans across entire HiddenWord to see what words are right. 
            {
                Count.Cows++;
                break;
            }
        }
    } return Count;
}


void UBullCowCartridge::HelpScreen(const bool& bHelpNeeded) const
{
    if (bHelpNeeded)
    {
        PrintLine(TEXT("In Bull Cows, you need to find the hidden word!\nYou get hints from Bulls and Cows!"));
        PrintLine(TEXT("Bulls are letters in the right spot.\nCows are letters in the word in the wrong spot."));
        PrintLine(TEXT("Lives are 2x the wordlength with added bonuses.\nWinning with +9 Lives reward 6 next round. +6 rewards 3."));
    }
}

void UBullCowCartridge::ScoreScreen(const bool& bScoreNeeded) const
{
    if (bScoreNeeded)
    {
        PrintLine(TEXT("SCORE- Wins: %i      Highest Win Streak: %i"), Wins, BestWins);
    }
}

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"

struct FBullCowCount
{
	int32 Bulls = 0;
	int32 Cows = 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;
	void SetupGame();
	void EndGame();
	void ProcessGuess(const FString& Guess);
	bool IsIsogram(const FString& Word) const;
	TArray<FString> GetValidWords(const TArray<FString>& WordsList, const int32 WinsCount) const;
	FBullCowCount GetBullCows(const FString& Guess) const;
	int32 EndResult(const int32& bOutcome);
	int32 RewardConvert(const int32& Performance) const;
	void HelpScreen(const bool& bHelpNeeded) const;	
	void ScoreScreen(const bool& bScoreNeeded) const;	

	// Your global member declarations go below!
	private:
	FString HiddenWord;
	int32 Lives;
	bool bGameOver;
	TArray<FString> Isograms;
	int32 Wins = 0;
	int32 BestWins = 0;
};
Screenshots of it in action

A nice course so far! It’s good to start with it on C++ after 2 months of Python. Hoping to master using both art and programming together.

1 Like

For a newbie you are doing a great job! Keep it up!

1 Like

Privacy & Terms