Triple X - More complicated (and difficult) version with an "animated" intro

Just finished the section of the Unreal course on pure C++ where you make Triple X

I already have knowledge of C# and Python so I was able to spice it up a bit (and make it more difficult).


I added an “animated” intro that makes it feel like you’re logging on to an old computer,

added a loading screen of sorts between codes,

allowed the player to select the difficulty parameters at the start of the game (between set values that you can change in the code, I’ve set them at 1 and 20),

and most of all generated codes of n length, meaning the game is a lot more difficult as codes can go to a length of 22 (with the hard coded difficulty limits)

Here’s the code:

#include <iostream>
#include <thread>
#include <chrono>
#include <string>
#include <ctime>
#include <vector>

bool GameLogic(int Difficulty);

// Printing functions
void PrintIntro();
void PrintLoading(std::string Text);
void SlowPrint(std::string Text, int Delay);

// Maths functions
int ClampInteger(int Input, const int Range[2]);
int SumVector(std::vector<int> Vector);
int ProductVector(std::vector<int> Vector);

int main()
{
    srand(time(NULL));

    const int DifficultyRange[2] = {1, 20};
    int InputLevelDifficulty, InputMaxDifficulty;

    std::cout << "* TripleX DOS varient *\n";

    std::cout << "Specify an integer between " << DifficultyRange[0] << " and " << DifficultyRange[1] << " for starting difficulty: ";
    std::cin >> InputLevelDifficulty;
    std::cout << "Specify an integer between " << DifficultyRange[0] << " and " << DifficultyRange[1] << " for maximum difficulty: ";
    std::cin >> InputMaxDifficulty;

    if (InputMaxDifficulty < InputLevelDifficulty)
    {
        InputMaxDifficulty = InputLevelDifficulty;
    }

    PrintIntro();

    int LevelDifficulty = ClampInteger(InputLevelDifficulty, DifficultyRange);
    const int MaxDifficulty = ClampInteger(InputMaxDifficulty, DifficultyRange);
    while (LevelDifficulty <= MaxDifficulty)
    {
        bool bLevelComplete = GameLogic(LevelDifficulty);
        std::cin.clear();
        std::cin.ignore();

        if (!bLevelComplete)
        {
            std::cout << "Code incorrect\n";
            std::cout << "Your hard disk has been encrypted to secure your data from possible intruders\n";

            std::system("Pause");
            return 0;
        }

        ++LevelDifficulty;
    }

    std::cout << "\nPassword reset successfull, new temporary password: admin\n";
    std::system("Pause");
    return 0;
}

bool GameLogic(int Difficulty)
{
    std::vector<int> Codes = {};
    const int NumberOfCodes = rand() % Difficulty + 3;

    for (int i = 0; i < NumberOfCodes; i++)
    {
        // Still using + 1 rather than + Difficulty as with varying code sizes would be impossible
        Codes.push_back(rand() % Difficulty + 1);
    }

    const int CodeSum = SumVector(Codes);
    const int CodeProduct = ProductVector(Codes);

    PrintLoading("Generating Code");
    std::cout << "\nCode difficulty: " << Difficulty << "\n";
    std::cout << "[#] There are " << NumberOfCodes << " segments in the code\n";
    std::cout << "[#] The segments in the code add up to " << CodeSum << "\n";
    std::cout << "[#] The segments in the code multiply to " << CodeProduct << "\n";

    // Store player input
    std::vector<int> Guesses = {};

    for (int i = 0; i < NumberOfCodes; i++)
    {
        int Guess;

        std::string Input;
        while (true)
        {
            std::cout << "C:\\> ";
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Found this online to ignore spaces
            std::cin >> Input;

            try
            {
                Guess = std::stoi(Input);
                break;
            }
            catch (const std::exception &e)
            {
                std::cout << "Input not a number\n";
            }
        }

        Guesses.push_back(Guess);
    }

    int GuessSum = SumVector(Guesses);
    int GuessProduct = ProductVector(Guesses);

    // Check win condition
    if (GuessSum == CodeSum && GuessProduct == CodeProduct)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void PrintIntro()
{
    using namespace std::this_thread;
    using namespace std::chrono;

    PrintLoading("Starting MS-DOS");

    std::cout << "\nPlease enter your password:";
    std::cout << "\nC:\\> ";

    sleep_for(1s);
    SlowPrint("reset password", 100);

    std::cout << "\nTo reset your password please complete the following security check\n";
    std::cout << "You will be reqired to solve a number of codes of varying length\n";
    std::cout << "To enter a segment of a code, simply type a number and press enter\n";
    std::system("Pause");
}

void PrintLoading(std::string Text)
{
    using namespace std::this_thread;
    using namespace std::chrono;

    const int Delay = 300;
    for (int i = 0; i < 3; i++)
    {
        std::system("CLS");
        std::cout << Text << ".";
        sleep_for(milliseconds(Delay));
        std::cout << ".";
        sleep_for(milliseconds(Delay));
        std::cout << ".";
        sleep_for(milliseconds(Delay));
    }
}

void SlowPrint(std::string Text, int Delay)
{
    using namespace std::this_thread;
    using namespace std::chrono;

    for (int i = 0; i < Text.length(); i++)
    {
        std::cout << Text[i];
        sleep_for(milliseconds(Delay));
    }
}

int ClampInteger(int Input, const int Range[2])
{
    if (Input < Range[0])
    {
        Input = Range[0];
    }
    else if (Input > Range[1])
    {
        Input = Range[1];
    }

    return Input;
}

int SumVector(std::vector<int> Vector)
{
    int Sum = 0;

    for (int i = 0; i < Vector.size(); i++)
    {
        Sum += Vector[i];
    }

    return Sum;
}

int ProductVector(std::vector<int> Vector)
{
    int Product = 1;

    for (int i = 0; i < Vector.size(); i++)
    {
        Product *= Vector[i];
    }

    return Product;
}

Edit:

After playing around with it for a bit I’ve noticed if you don’t enter a number for the first segment of a code (works fine otherwise) then it doesn’t try and validate your input, but waits for more.

It’s not a game breaking bug so I’m just going to continue with the course and leave it in there.

Edit 2:
Noticed the win screen didn’t pause and closes the game instantly, fixed above.

5 Likes

Hi, I really liked what you have done here. Not only it increases the Difficulty but also the Number of characters to be entered at the later stage of the Game.

Even i was trying to do the same but never got any success. I am a 3D artist and coding part is really new to me. I just know about HTML and some bits about Javascript.

I tried to study your code but inbetween some things went above my head.
It will be really helpful if you could explain that how are you adding Number of characters on every new level And also about the Amazing Animation.
Thanks

1 Like

Hi, thanks for your feedback and I’ll try to explain the best I can.

The animation is done using delays and clearing the screen (The PrintIntro() function does all this, as well as the PrintLoading() and SlowPrint() which emulate a 3 dot loading thing (. .. ... repeat), and a person typing by slowly writing each character of a string with a fixed delay)

The GameLogic() function handles each individual level and is passed a difficulty.
The way I’m adding more digits is by using a C++ vector (C++ version of a dynamic array, meaning it doesn’t have a fixed size) and “pushing” a new digit to the Codes vector until i equals NumberOfCodes which is generated randomly.

An excerpt showing the code generation:

std::vector<int> Codes = {};
const int NumberOfCodes = rand() % Difficulty + 3;

for (int i = 0; i < NumberOfCodes; i++)
{
    // Still using + 1 rather than + Difficulty as with varying code sizes would be impossible
    Codes.push_back(rand() % Difficulty + 1);
}
1 Like

Thanks for your reply. Now i’m able to link some of the things together and getting well into the logic.

I’m still unclear about

using namespace std::this_thread;

    using namespace std::chrono;

and

int ClampInteger(int Input, const int Range[2])
{
    if (Input < Range[0])
    {
        Input = Range[0];
    }
    else if (Input > Range[1])
    {
        Input = Range[1];
    }

    return Input;
}

int SumVector(std::vector<int> Vector)
{
    int Sum = 0;

    for (int i = 0; i < Vector.size(); i++)
    {
        Sum += Vector[i];
    }

    return Sum;
}

int ProductVector(std::vector<int> Vector)
{
    int Product = 1;

    for (int i = 0; i < Vector.size(); i++)
    {
        Product *= Vector[i];
    }

    return Product;
}

What is happening in ClampInteger(int Input, const int Range[2]) Function?
What is std::vector ??

Thanks alot you have really boosted my knowledge and working in C++

1 Like

Using namespaces

From http://www.cplusplus.com/doc/oldtutorial/namespaces/

Namespaces allow to group entities like classes, objects and functions under a name. This way the global scope can be divided in “sub-scopes”, each one with its own name.

So, by using, for example, using namespace std;, instead of typing std::cout, we can just type cout.
Now I wouldn’t recommend something with a scope as wide as using namespace std, however for a smaller namespace like std::this_thread, it saves writing out std::this_thread::sleep_for every time I want to call sleep_for.

Now the functions:

ClampInteger is a C++ implementation of the clamp function found in a variety of programming languages. It takes an input to clamp, and a range (min, max) to clamp it between. If the input is less than the minimum (0th index of the range array), then return the minimum, else if input is greater than maximum (1st index of range array), return maximum, else return input

SumVector loops through elements of a vector containing integers, and adds each one to a total, then returns the total.

ProductVector is the same as SumVector, except it multiplies each element by the product of each element before it.

And finally, vectors

Vectors in C++ are just its version of a dynamic array, meaning it has no fixed size.
So unlike a normal array, which once you define, you cant change the size of without destroying the original array first, a vector can have elements added to it and doesn’t have a fixed size.

1 Like

Okay Now i understand that ClampInteger is helping to create a range of levels to be played but I don’t understand that Range[1] will provide with 2 values in an array but then why are we writing Range[2].

And How are the segments in a code are increasing with increase in level?

1 Like

I think you’re mixing up array definition, and accessing an element of an array.

const int Range[n] defines an array of length n
whereas Range[n] accesses the nth element of the array

It might look confusing at first but it’s obvious which is being used depending on context, so it shouldn’t be an issue when you’re used to programming.

Finally, by segments of code increasing, I assume you’re referring to the variable length of the code? If so, I already provided an explanation of that in one of my previous posts.

1 Like

I get that now… I thought that just like many things Arrays work the same in C++ just like in Javascript (as i know about it). I searched on google and read about it after questioning you the same and I got to know about the slight differences in the working of Array in C++ and Javascript.
It was pretty dumb of me to think that they might work the same way.

I know you tried to explain variable length of code (really thanks for that too) and even i tried to study the code to understand that but it still goes over my head. maybe I still need to educate myself alot in C++.

1 Like

First of all, if you’re used to a none-typed interpreted language like JavaScript, it’s not dumb to think C++ would work the same way.

Secondly, if you’re better at JavaScript, I can write the code generation in it for you to help you understand.

1 Like

Well I just have basic knowledge of JavaScript and know about its working, I’m not familiar with its libraries like jquery.
I think code generation in javascript might be a good idea and help me understand.
Thanks

1 Like

I’ve written it in JavaScript with more comments than before too:

<script>
// This is just a standard js min (inclusive), max (exclusive) random int function
function RandomInt(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}

// This is the code generation as its own function
// Note: you could pass this a negative number,
// but it would generate a code of length 0 as the for loop wouldn't run
function GenerateCode(Difficulty) {
    let Code = [];

    // The plus 3 means the code is always at least 3 long
    const NumberOfSegments = RandomInt(0, Difficulty) + 3;

    for (let i = 0; i < NumberOfSegments; i++) {
        Code.push(RandomInt(0, Difficulty) + 1);
    }

    return Code;
}

// Here's an example function call that generates a code as though it was difficulty 3
console.log(GenerateCode(3).join(" "));
</script>

Note, I wrapped it in script tags so just paste it into a HTML file and look at the console.

1 Like

I’ve also made a small webpage that uses the script above, and lets you test the code generation:

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.1/css/bootstrap.min.css" integrity="sha384-VCmXjywReHh4PwowAiWNagnWcLhlEJLA5buUprzK8rxFgeH0kww/aWY76TfkUoSX" crossorigin="anonymous">

    <title>Code Generation Test</title>
</head>
<body>
    <div style="margin: 10px;">
    <form id="mainForm">
        <label>Generate a code:</label><br>
        <input id="diffEntry" type="number" placeholder="difficulty" min="1" max="20" style="min-width: 100px;">
        <input type="submit" value="Generate">
    </form>
    <br>
    <div>
        <span>Generated code: </span>
        <span id="codeDisp" style="color: rgb(255, 148, 25);"></span>
    </div>
    </div>

    <script>
        const form = document.getElementById("mainForm");
        const textElement = document.getElementById("diffEntry")
        const targetElement = document.getElementById("codeDisp")

        function HandleForm(event) {
            event.preventDefault();
            targetElement.innerHTML = GenerateCode(parseInt(textElement.value)).join(" ")
        }

        // This is just a standard js min (inclusive), max (exclusive) random int function
        function RandomInt(min, max) {
            return Math.floor(Math.random() * (max - min)) + min;
        }

        // This is the code generation as its own function
        // Note: you could pass this a negative number,
        // but it would generate a code of length 0 as the for loop wouldn't run
        function GenerateCode(difficulty) {
            let code = [];

            // The plus 3 means the code is always at least 3 long
            const numberOfSegments = RandomInt(0, difficulty) + 3;

            for (let i = 0; i < numberOfSegments; i++) {
                code.push(RandomInt(0, difficulty) + 1);
            }

            return code;
        }

        form.addEventListener('submit', HandleForm);
    </script>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.1/js/bootstrap.min.js" integrity="sha384-XEerZL0cuoUbHE4nZReLT7nx9gQrQreJekYhJD9WNWhH8nEW+0c5qq7aIo2Wl30J" crossorigin="anonymous"></script>
</body>
</html>

Note: there’s a button at the top right of every code box (hard to see as it’s covered by the scroll bars) that copies the contents to your clipboard.

1 Like

Wow…! I’m really really thankful to you for that effort. You have done more than enough that i could ever think off. Thanks for fetching some time just to make me understand whats happening around here in this code. This Javascripting has actually made the code alot more clear.

I’m really sorry for replying that late. But after i couldn’t understand your code I realized that i need to learn more of the basics in C++. So I went onto study more about it and covered some basics first. And today when I again opened your C++ coding it didn’t felt like it was an alien language anymore…I was able to understand most of the things by my own and of course JAVASCRIPT code also helped me.

A Big thanks and your CSS was great too.

1 Like

Thanks!
Glad I could help!

1 Like

Hi thanks for sharing,
this is really cool and inspiring so I gave it a try and animated my version of TripleX game aswell :sweat_smile:

#include <iostream>
#include <thread>
#include <chrono>
#include <string>
#include <ctime>
#include <vector>

bool GameLogic(int Difficulty);

// Printing functions
void PrintIntro();
void PrintLoading(std::string Text);
void SlowPrint(std::string Text, int Delay);
void PrintOutro();

int main()
{
    srand(time(NULL)); // Create new random sequence based on time of the day
   
    std::cout << "Hello, world";

    int LevelDifficulty = 1;
    int const MaxDifficulty = 5;

    PrintIntro();

    while (LevelDifficulty <= MaxDifficulty)
    {
        bool bLevelComplete = GameLogic(LevelDifficulty);
        std::cin.clear(); // Clears any errors
        std::cin.ignore(); // Discards the buffer

        if (bLevelComplete)
        {
            ++LevelDifficulty;
        }
        
    }

    PrintOutro();

    std::system("Pause");

    return 0;
} 

bool GameLogic(int Difficulty)
{
    // Declare 3 number code
    const int CodeA = rand() % Difficulty + Difficulty;
    const int CodeB = rand() % Difficulty + Difficulty;
    const int CodeC = rand() % Difficulty + Difficulty;

    const int CodeSum = CodeA + CodeB + CodeC;
    const int CodeProduct = CodeA * CodeB * CodeC;

    PrintLoading("Generating code");
    std::cout << "\nYour current rank: " << Difficulty << "\n";
    std::cout << "* The code adds up to " << CodeSum << "\n";
    std::cout << "* The code multiplies to " << CodeProduct << "\n";

    // Store player guess
    int GuessA, GuessB, GuessC;
    std::cout << "C:\\> ";
    std::cin >> GuessA;
    std::cout << "C:\\> ";
    std::cin >> GuessB;
    std::cout << "C:\\> ";
    std::cin >> GuessC;

    int GuessSum = GuessA + GuessB + GuessC;
    int GuessProduct = GuessA * GuessB * GuessC;

    // Check if the players guess is correct
    if (GuessSum == CodeSum && GuessProduct == CodeProduct)
    {
        std::cout << "\nID verification succesful: \n";
        return true;
    }
    else
    {
        std::cout << "\nWrong, you may try again...";
        return false;
    }    
}

void PrintIntro() 
{
    using namespace std::this_thread;
    using namespace std::chrono;

    std::cout << "* entering  G O L D E N  city *\n";
    std::system("Pause");

    PrintLoading("Starting the entry system");
    std::cout << "\n   _____________________ \n";
    std::cout << "  |  _________________  | \n";
    std::cout << "  | |                 | | \n";
    std::cout << "  | |    _ _ _ _ _    | | \n";
    std::cout << "  | |                 | | \n";
    std::cout << "  | |   L O C K E D   | | \n";
    std::cout << "  | |_________________| | \n";
    std::cout << "  |_____________________| \n\n";
    std::cout << "\nPlease enter your passcode: ";

    sleep_for(1s);
    SlowPrint("*****", 100);

    std::cout << "\nC:\\> ";

    sleep_for(1s);
    SlowPrint("ID verification needed", 100);

    std::cout << "\nYour passcode is invalid, please complete the following security check";
    std::cout << "\nYou will be required to solve a number of codes of varying length";
    std::cout << "\nTo enter a segment of a code, simply type a number and press enter";
    std::system("Pause");
}

void PrintLoading(std::string Text)
{
    using namespace std::this_thread;
    using namespace std::chrono;

    const int Delay = 300;
    for (int i = 0; i < 3; i++)
    {
        std::system("CLS");
        std::cout << Text << ".";
        sleep_for(milliseconds(Delay));
        std::cout << ".";
        sleep_for(milliseconds(Delay));
        std::cout << ".";
        sleep_for(milliseconds(Delay));
    }
}

void SlowPrint(std::string Text, int Delay)
{
    using namespace std::this_thread;
    using namespace std::chrono;

    for (int i = 0; i < Text.length(); i++)
    {
        std::cout << Text[i];
        sleep_for(milliseconds(Delay));
    }
}

void PrintOutro() 
{
    using namespace std::this_thread;
    using namespace std::chrono;

    PrintLoading("Welcome to the");
   
    sleep_for(1s);
    SlowPrint("\nG O L D E N\n", 100);
}
1 Like

Looks good!