Use inline if statement to assign a value to a variable (BullCowGame)

Hi,

So I know in Python you can use inline if statements i.e. numTry = ‘try!’ if len(guesses) <= 1 else ‘tries!’

Is it possible to do the same in C++? I had a look and came up with a similar version using the ternary operator ?:

I am working on the BullCowGame and want to neaten up the ‘Lives’ section of the game. Currently, I depreciate the lives with every wrong answer saying ‘you have X lives left’. However, when it gets to 1 life left, I want to say the word ‘life’.

So, I tried this in my setup function to enable me to use it to format an FString. I did declare it in the headerfile as a FString member variable as well:

//*** In SetupGame() Function section of code to define variables***

HiddenWord = TEXT("Texts"); //Set the HiddenWord from List
Lives = HiddenWord.Len(); //Set Lives
NumLives = Lives == 1 ? TEXT("life") : TEXT("lives"); //change 'lives' to 'life' when lives = 1
Try = TEXT("\nTry again!");
bGameOver = false;

//*** In OnInput() Function section of code to see if the word length matches and to depreciate a life if not ***

   if (bGameOver)
   {
        ClearScreen();
        SetupGame(); 
   }
   else //Checking PlayerGuess
   {
       if (Input == HiddenWord)
        {
            PrintLine(TEXT("You Win!"));
            EndGame();
        }
        else
        {
            if (Input.Len() != HiddenWord.Len())
            {
                --Lives;
                PrintLine(TEXT("Incorrect Guess. You have %i %s left.\n%s"), Lives, *NumLives, *Try);
                //PrintLine (TEXT("%i"), Lives);
            }           
            
        }
   }

   if (Lives == 1) // changes 'try again' text to nothing when Lives == 1
   {
       Try = TEXT("\n");
   }
   else if (Lives < 1) // checks if no lives and ends game
   {
       PrintLine(TEXT("You Have Lost..."));
       EndGame();
   }

It doesn’t want to work, not sure if this is the right way to do this? The ‘Try’ formatting works perfectly, but not the inline if statement

Cheers,

Nic

If you’re only ever setting that in SetupGame then it’s never going to change from that. Also to save allocations you can just use const TCHAR* which doesn’t need * when formatting.

const TCHAR* LifeText = Lives == 1 ? TEXT("life") : TEXT("lives");
const TCHAR* TryText = Lives == 1 ? TEXT("\n") : TEXT("\nTry again!");
PrintLine(TEXT("Incorrect Guess. You have %i %s left.\n%s"), Lives, LifeText, TryText);

Of course, that makes sense!

I have added it into the OnInput part of the game above all the if statements, and it works great now, although I did have to change the lives to ‘Lives == 2’ to get it to work with life #1.

Is this because of the null character in the FString text? So the null character is actually 0 rather than the last character in the HiddenWord. Lives uses the length of the hidden word as the number of lives. I did print to terminal to see if the lives were depreciating correctly, and they do. So on the last life ‘Lives == 1’, yet I have to say ‘Lives == 2’ for it to work?

Also, using const TCHAR* will make the variable constant and unchanging right? Therefore I can’t change the variable to different words? Also, will I still have to define the variables as member variables in the header file?

Thanks for the help Dan :slight_smile:

EDIT: So I tried what you said, and it dawned on me that if I declare the variables in the same function they are being called in, I don’t need to define them as member variables.

Also, the ‘const TCHAR*’ works, however, I don’t really understand the difference between that and using * to format the string. Would you mind elaborating please?

It’s not clear if your edit fixed things for you. However

FString::Len does not count the null character.

This means it’s a pointer-to-const, so the thing it’s pointing to can’t be modified.


"string literals" in your code are placed into read only memory of the binary itself. There’ll be a section with it stored e.g.

.text
    "hello"

And that’s baked into the compiled code which is why it can’t be modified. So what you’re doing is just pointing to that location.

FString::Printf uses one of the printf family of functions from C which takes a null terminated string in the form of a pointer to the first character which const TCHAR* already is.
FString::operator* is just a way to get that from an FString.

Thanks Dan, very informative :slight_smile:

So I thought I had solved this issue with the inline if statement, but it turns out I have a new issue.

I have progressed further through the Bull Cows Game (Early Returns Section) and have put my input processing into its own function. See below:

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

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

    SetupGame();
    

    //PrintLine(TEXT("The HiddenWord is: %s."), *HiddenWord); //Debug line
}

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

      

}

void UBullCowCartridge::SetupGame()
{
    //Welcome Player
    PrintLine(TEXT("Welcome to BullCow!\n"));

    HiddenWord = TEXT("cakes"); //Set the HiddenWord from List
    Lives = HiddenWord.Len(); //Set Lives
    bGameOver = false;

    PrintLine(TEXT("Guess the %i letter word! You have %i lives.\n"), HiddenWord.Len(), Lives);
    PrintLine(TEXT("Hit TAB to access the terminal, \ntype your guess, and hit enter to begin \nplaying the game!\n")); // Prompt PlayerGuess
}

void UBullCowCartridge::EndGame()

{
    bGameOver = true;
    PrintLine(TEXT("\nPress Enter to play again."));
}

void UBullCowCartridge::ProcessGuess(FString Guess)
{
    const TCHAR* NumLives = Lives == 1 ? TEXT("life") : TEXT("lives"); //change 'lives' to 'life' when lives = 1
    //const TCHAR* ReTry = Lives == 1 ? TEXT("\n") : TEXT("Guess again!"); //changes 'try again' text to nothing when Lives == 1

    if (Guess == HiddenWord)
    {
        PrintLine(TEXT("You Win!"));
        EndGame();
        return;
    }

    //Check if Isogram
    // if (!IsIsogram)
    // {
    //     PrintLine(TEXT("No repeating letters, %s"), ReTry);
    // }
    

    if (Guess.Len() != HiddenWord.Len())
    {
        PrintLine(TEXT("Incorrect answer. You have %i %s \nremaining. Guess again!"), Lives, NumLives);
        PrintLine(TEXT("The hidden word is %i letters long."), HiddenWord.Len());
        // PrintLine(TEXT("%i"), Lives);
        return;
    }

    --Lives;    //Remove life
    PrintLine(TEXT("-1 life"));
    

    if (Lives <= 0)
    {
        ClearScreen();
        PrintLine(TEXT("You have no lives left!"));
        PrintLine(TEXT("The hidden word was %s"), *HiddenWord);
        EndGame();
        return;
    }

    //Show player Bulls and Cows
    // PrintLine(TEXT("%i"), Lives);
    PrintLine(TEXT("Unlucky! You have %i %s \nremaining."), Lives, NumLives);

}

However, I now come to the issue that when the Lives == 1 the string formatting does not work right away. I am only depricating a life when the player enters the correct length of the hidden word. If I get down to 1 life, the string still outputs ‘… 1 lives remaining.’. However, if I then type in a word that doesn’t have the same amount of letters as the hidden word, I get ‘… 1 life remaining’.

It’s like I am using a post decrement rather than a pre, yet when I print the lives to the terminal it is consistent in how it depricates. I’m not sure if this is an ordering thing? I can see that I am using two instances of the string formatting, but I don’t think that would change the way the inline if statement works?

So to sum up because I’m not great at explaining this. It seems like I have to action the ternary operator twice before it actually works.
Maybe I’m getting ahead of myself, but it’d be great to know what I’m missing here…

It has nothing to do with the pre vs post. You’re creating NumLives at the start of the function before lives is decremented. Don’t do that. Always create variables at the point of use.

Yeah I thought as much, in my brain it was behaving as though that was the case.

Ok, that makes sense. So I want to create NumLives after decrementing Lives?

If I do that then NumLives is not defined in the string formatting because that happens before I have declared it. How do I get around that situation?

Not quite sure how to point at that when the code is read top down. This is why I put it at the top of the function. Every time there is a guess, the ProcessGuess() function is called, so it ‘refreshes’ the function. That was my thinking at least… :sweat_smile:

Just create different variables.

if (Guess.Len() != HiddenWord.Len())
{
    const TCHAR* NumLives = Lives == 1 ? TEXT("life") : TEXT("lives"); //change 'lives' to 'life' when lives = 1
    PrintLine(TEXT("Incorrect answer. You have %i %s \nremaining. Guess again!"), Lives, NumLives);
    PrintLine(TEXT("The hidden word is %i letters long."), HiddenWord.Len());
    // PrintLine(TEXT("%i"), Lives);
    return;
}
// code...
const TCHAR* NumLives = Lives == 1 ? TEXT("life") : TEXT("lives"); //change 'lives' to 'life' when lives = 1
PrintLine(TEXT("Unlucky! You have %i %s \nremaining."), Lives, NumLives);

Or reassign it. It doesn’t make much difference either way.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms