We are happy to offer our loyal readers a special discount price on our book.
Use code: BLEARNC++ and this link.

Michael Haephrati, Ruth Haephrati

Posted on by Michael Haephrati מיכאל האפרתי | 2 Comments

Feel the change: Meet the new C++23 insert_range()

C++23 brought a new and useful addition to the language: insert_range().

This new member function provides a more efficient way to insert a range of elements into a vector. Until C++23, the only way to insert a range of elements into a vector was to use a loop. For example:

std::vector<int> vec1 = {1, 2, 3, 4, 5};

std::vector<int> vec2 = {6, 7, 8, 9, 10}; 

for (int i = 0; i < vec2.size(); i++)

{

vec1.insert(vec1.begin() + 2 + i, vec2[i]);

}

// Print the vector

for (int i = 0; i < vec1.size(); i++)

{

std::cout << vec1[i] << ” “;

}

Our output will be: 1 2 6 7 8 9 10 3 4 5

However, especially when we use large vectors, loops could be very inefficient. When you loop through a large container, you are essentially accessing each element in the container one at a time. This can be a very slow process, especially if the container is very large.

The insert_range() function solves this problem by providing a single function that can be used to insert a range of elements into a vector with a single function call. So now, instead of using the loop in our previous code, we can simply write:

std::vector<int> vec1 = {1, 2, 3, 4, 5};

std::vector<int> vec2 = {6, 7, 8, 9, 10};

auto iter = vec1.insert_range(vec1.begin() + 2, vec2.begin(), vec2.end());

for (int i = 0; i < vec1.size(); i++)

std::cout << vec1[i] << ” “;

}

As you can see, we don’t need to use a loop, we simply add the new range to our vector.

The new insert_range() function is a really valuable addition to the C++ language, and we expect it will be widely used.

In our new book Learning C++ published by Manning Publication, we teach C++ core language features, including the newest C++23 standard.

Posted in Uncategorized | Tagged , , , , , | Leave a comment

Making files and folders invisible

Do you know there are ways to hide files and folders and make them invisible? Normally you have the option to check or uncheck the “hidden items” checkbox in the Explorer, as shown in the screenshot below:

Nevertheless, when a file or a folder is hidden at the lowest level, it won’t show even if the “hidden items” option is checked.

In the digital age, privacy and security are of paramount importance. While operating systems like Windows provide basic mechanisms to hide files and folders, these methods can often be circumvented. In this blog post, we’ll explore a more robust approach to hiding files and folders using a custom filesystem driver in C++20. This technique ensures that hidden files and folders remain concealed even at the lowest system level.

Why hide files or folders

The need for advanced file and folder hiding techniques arises when you want to protect sensitive data from prying eyes or malicious software. By creating a filesystem driver, we can create a mechanism for truly hiding that sensitive data. Other use cases may be Lawful Interception.

Developing a Custom Filesystem Driver

Creating a custom filesystem driver is an advanced and challenging task that requires in-depth knowledge of system programming and kernel-level development. The slightest bug may bring the well-known BSOD. Always conduct code review. Here is a high-level overview of the steps involved:

  1. Setting up the Development Environment
    To develop a filesystem driver in C++20, you’ll need a Windows development environment with the Windows Driver Kit (WDK) installed. You’ll also need a code editor that supports kernel-mode development.
  2. Designing the Driver
    The first step in creating a filesystem driver is to design it. You’ll need to decide how your driver will intercept file and folder operations and determine the mechanisms for hiding and revealing them.
  3. Writing the Code
    Implement your filesystem driver using C++20, adhering to the Allman code style indentation, as mentioned in the user profile. You’ll need to handle functions like file creation, deletion, and enumeration, ensuring that your driver can identify hidden files and folders and prevent them from being displayed.
  4. Compiling and Testing
    Compile your driver and load it into a test environment. Thoroughly test its functionality, ensuring that hidden files and folders remain invisible, even to users and applications with elevated privileges.
  5. Deployment and Security Considerations
    If your tests are successful, consider the deployment of your filesystem driver with caution. Kernel-level development carries significant risks, and deploying a flawed driver can destabilize the system. Ensure that your driver is well-documented and thoroughly reviewed for security vulnerabilities.

Hiding files and folders at a low-level using a custom filesystem driver in C++20 is a powerful technique for enhancing data security and privacy. However, it’s essential to approach this task with caution and expertise, as kernel-level development can be complex and carries significant risks. Always prioritize system stability and security when developing and deploying such drivers.

Code Signing requirements

Requirements for Submitting a Driver to Microsoft Labs
Driver Stability and Security: Your driver must be thoroughly tested and verified for stability and security. It should not introduce system instability or security vulnerabilities. Ensure that your code adheres to best practices for kernel-mode development.

Driver Compatibility: The driver should be compatible with the target Windows operating systems (e.g., Windows 10, and Windows 11). Microsoft may require you to provide documentation specifying which OS versions your driver is intended for.

Clean Code: Make sure your code is well-documented and follows established coding standards and practices, such as using the Allman code style indentation as specified in your user profile.

Driver Package: Create a driver package that includes the driver binaries, INF files, and any necessary support files. The INF file provides installation instructions to Windows and specifies the driver’s information.

Driver Testing: Perform rigorous testing on various Windows configurations to ensure compatibility and stability. You should also verify that the driver can be properly installed and uninstalled.

Digital Signature: Your driver package should be digitally signed with a valid code signing certificate. Microsoft Labs will not sign drivers without proper digital signatures.

The Process of Submitting a Driver to Microsoft Labs

Prepare Your Driver Package: Ensure that your driver package includes all necessary files, including the driver binaries, INF file, and any additional files required for installation or operation.

Acquire a Code Signing Certificate: If you don’t already have a code signing certificate, you’ll need to obtain one from a trusted Certificate Authority (CA). Microsoft recommends using a certificate issued by a CA that is a member of the Windows Hardware Developer Program. Since January 2016, and as I explained in this article, you need an EV Code Signing Certificate and each driver must be counter-signed by Microsoft as well.

Driver Submission: Visit the Windows Hardware Dev Center Dashboard (https://partner.microsoft.com/en-us/dashboard/hardware). If you don’t have an account, you’ll need to create one. Once logged in, you can start the driver submission process.

Fill in Driver Information: Provide information about your driver, including its name, version, and a brief description. Specify the hardware IDs and compatible IDs for the devices your driver supports.

Upload Driver Package: Upload your driver package to the Windows Hardware Dev Center. Ensure that the package is complete and correctly configured. Note that to create such a package, you would need to sign the driver file/s first, then create a special archive called .cab, and then sign that .cab again. Here is an article I wrote about the process.

Code Signing Request: During the submission process, you’ll be prompted to submit a request for code signing. Provide the details of your code signing certificate, and Microsoft Labs will use it to sign your driver package.

Review and Validation: Microsoft Labs will review your driver submission for compliance with their requirements. This process may take some time, and they may contact you for additional information or clarifications.

Digital Signature: Once your driver package is approved, Microsoft Labs will digitally sign it with a Microsoft signature. This signature signifies that the driver has passed their review and is safe for installation on Windows systems.

Note: in many cases, Microsoft will not sign the driver and in that case, you will have to download a report specifying the reasons for the rejection, and then fix whatever needs to be fixed and go through the entire process again.

Download the Signed Driver: After your driver package is signed, you can download the signed package from the Windows Hardware Dev Center.

Distribution: Distribute your signed driver to your users or through appropriate channels, knowing that it has the Microsoft signature, which enhances its trustworthiness.

How to enjoy both worlds

There is an option to enjoy both worlds. Obtain a Source Code license for a ready-made source code project, where you have everything you need to add file and folder hiding capabilities to your software.

We have developed everything needed, so you don’t even have to code sign any driver, but just use a ready-made SDK and the source code. These can be obtained here and is demonstrated in this video.

Posted in Uncategorized | Tagged , , , , , , , , , , , , | Leave a comment

MEAP updated

New chapters out!

You can now access the following chapters of Learning C++ by Michael Haephrati & Ruth Haephrati:

Chapter 9, Making a good point – Pointers at work
Chapter 10, OOPs, we did it again – Object Oriented Programming and C++
Chapter 11, STL here – The C++ Standard Template Library
Chapter 12, C++ conceptual art: Templates and concepts
Chapter 13, Dancing the lambda
Chapter 14, The Power Ranges – C++ Ranges; Date and time handling.

Posted in Learning C++ | Tagged , , , , , , | 1 Comment

Final code exercise

We already shared with you Brain Train, our final exercise, brought to you in Chapter 18 of our book.

Happy to share a Mac demonstration of the same multiplatform source code. Remember, we are happy to offer our loyal readers a special discount price on our book. Use code: BLEARNC++ and this link.

Posted in Uncategorized | Tagged , , , , , , , , , | Leave a comment

Going over editorial comments

We have developed our own software (in C++ of course) that uses Office Automation and sqlite3 to maintain a database of all editorial comments we receive from Technical Editors about our book‘s chapters.

For example, there is an option to enumerate all comments in the Word document, along with the status (resolved / not resolved), and any additional replies within the thread.

We use the same log function described in Chapter 17, WriteLogFile() to store the information in a log file as well, and in this video, we show what that log file looks like.

For example, here is how we extract the entire text form a given comment in a Word document


OLECHAR *OfficeAutomation::GetTextOfComment(IDispatch *pComment)
{
	IDispatch *pCommentRange = nullptr;

	// First we get the Range object associated with the text of the comment
	VARIANT result;
	VariantInit(&result);
	m_hr = OLEMethod(DISPATCH_PROPERTYGET, &result, pComment, (LPOLESTR)L"Range", 0);
	if (FAILED(m_hr))
	{
		return nullptr;
	}

	if (result.vt == VT_DISPATCH && result.pdispVal != nullptr)
	{
		pCommentRange = result.pdispVal;

		// Now we get the text from the Range object
		m_hr = OLEMethod(DISPATCH_PROPERTYGET, &result, pCommentRange, (LPOLESTR)L"Text", 0);
		pCommentRange->Release();

		if (FAILED(m_hr))
		{
			return nullptr;
		}
		if (result.vt == VT_BSTR)
		{
			return result.bstrVal; // comment text returned
		}
	}

	return nullptr;
}



You can also see how AI can be used to help with such automation tasks in this article we published in InfoQ.

Posted in Uncategorized | Tagged , , , , , , , , , , , | 1 Comment

The Simpson Show and C++ 20 concepts

Read more about that in Chapter 12 of our book.
The Simpsons is one of the oldest and most beloved cartoon TV shows since 1987, and, unless you’ve been living in a cave, you probably know it. In the opening credits of each chapter, Bart Simpson, as the troublemaker he is, repeatedly writes a sentence on a chalkboard, in the old “write it 100 times” punishment. The thing is, each episode features a different sentence Bat writes on the chalkboard, such as “I will not fake seizures”, “Organ transplants are best left to professionals”, “The principal’s toupee is not a Frisbee”, and – well.. you get the idea.

source – wikipedia


And why are we telling you all this? well, throughout this book, we talked about templates more than a few times, and by now, you should have a pretty good idea about the general concept of templates, and that we use them to store whole part of a changing set of instructions – it can be a formula, a routine, or a function of any kind, as we can fill the changeable parts later.

we can draw an analogy between The Simpson’s opening sequence and the combination of C++ templates and concepts. Templates and concepts serve as frameworks that allow us to create different instances based on a common structure, just like The Simpsons opening follows a consistent structure that sets the stage for the episodes that follow.

Similar to templates, the core components of The Simpsons opening, such as the catchy theme song, the couch gag, and Lisa’s saxophone solo, remain unchanged, representing the constants or invariant parts. They form the foundational structure, just as templates provide a fixed foundation for creating various instances.

However, within this template, there are dynamic or variable elements that change with each episode. For instance, the chalkboard gag presents a different witty phrase written by Bart in every episode, adding a unique touch and personalization. This variability aligns with the customizable parts of a template, demonstrating the adaptability of concepts that allow different types to satisfy the requirements. The Simpsons opening is renowned for its clever references, cultural satire, and irreverent humor, showcasing the creative flexibility that concepts offer. Just as concepts empower developers to define requirements and ensure various types meet those criteria, the Simpsons opening incorporates humor and social commentary, ensuring its freshness and relevance with each iteration. For these reasons, we have selected the Simpsons opening sequence as an analogy to the combination of C++ templates and concepts. It combines constant elements that establish a recognizable structure with variable elements that provide unique and evolving content. This analogy underscores the significance of templates and concepts in programming, emphasizing their role in creating adaptable and flexible code structures.

Let’s dive into the code

#include <iostream>
#include <concepts>
#include <random>
#include <vector>
#include <limits>
#include <string>


// #B
template <typename T>
concept HasCloseFunction = requires(T t)
{
{ t.close() } -> std::same_as<void>;
};

// #C
template <typename T>
concept OpeningSequence = HasOpenFunction<T> && HasCloseFunction<T>;

// #D
template <OpeningSequence T>
void ExecuteOpeningSequence(T& opening)
{
opening.open();
opening.close();
}

// #E
class SimpsonsOpening
{
public:
void open()
{
std::cout << “INT. SIMPSONS LIVING ROOM – DAY\n”;
std::cout << “The Simpsons opening sequence starts.\n”;
std::cout << “We see the following elements:\n”;
for (const auto& element : openingElements)
{
std::cout << “- ” << element << “\n”;
}
std::cout << “CUT TO:\n”;
}

void close()
{
std::cout << “CUT TO BLACK.\n”;
std::cout << “The Simpsons opening sequence ends.\n”;
}

void addOpeningElement(const std::string& element)
{
openingElements.push_back(element);
}

// #F
void randomizeBartActivities()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<size_t> dist(0, bartActivities.size() – 1);

size_t randomIndex = dist(gen);
openingElements[0] += ” ” + bartActivities[randomIndex];

if (bartActivities[randomIndex] == “writing on the chalkboard”)
{
openingElements[0] += ” – ” + GetRandomElement(chalkboardOptions);
}
}

private:
std::vector<std::string> openingElements;
std::vector<std::string> bartActivities =
{
“writing on the chalkboard”,
“skateboarding”,
“pranking Moe’s Tavern”,
“skipping school”,
“eating his homework”
};

std::vector<std::string> chalkboardOptions =
{
“\”I will not skateboard in the hallway\””,
“\”I will not throw spitballs in class\””,
“\”I will not prank call Moe’s Tavern\””,
“\”I will not skip school\””,
“\”I will not eat my homework\””
};

// #G
template <typename T>
T GetRandomElement(const std::vector<T>& vec)
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<size_t> dist(0, vec.size() – 1);
return vec[dist(gen)];
}
};

int main()
{
// Introduction
std::cout << “Welcome to the Simpsons Opening Script Generator!\n”;
std::cout << “Press ‘Enter’ to view a Hollywood script style random Simpsons opening script, or ‘q’ to quit.\n”;

bool quit = false;

while (!quit)
{
// Wait for user input
std::string input;
std::getline(std::cin, input);

if (input == “q”)
{
quit = true;
}
else
{
// #H
SimpsonsOpening opening;
opening.addOpeningElement(“Bart”);
opening.addOpeningElement(“Lisa playing the saxophone”);
opening.addOpeningElement(“Maggie being scanned at the grocery store”);

opening.randomizeBartActivities();

ExecuteOpeningSequence(opening);

std::cout << “\nPress Enter to view another Simpsons opening, or ‘q’ to quit.\n”;
}
}

std::cout << “Thank you for using the Simpsons Opening Viewer!\n”;

return 0;
}
#A Concept to check if a type has the member function `open`
#B Concept to check if a type has the member function `close`
#C Concept to check if a type satisfies the requirements of being an opening sequence
#D Function template to execute the opening sequence of a given type
#E Example class representing the opening sequence of The Simpsons
#F Function template for randomizing Bart’s activities and chalkboard writings
#G Utility function to get a random element from a vector
#H Generate a random Simpsons opening

Here is a typical output to expect

.

Posted in Uncategorized | Tagged , , , , , , , | 1 Comment

Measuring User’s Activity level

In 2017, during a meeting with a client, a government agency, we discussed a project we developed and offered them, (in C++, obviously…), they asked us if it was possible to add log entries about a PC user’s activity level. That gave us the idea to add such a feature and in this post, I am going to show you how we can assign a dedicated thread to measure your level of activity while using your PC. That is, in fact, the final exercise of Chapter 15, Multithreading the Needle.

When you wish to monitor the activity level of the user, you would need a separate thread for
that. In our example, we will start the program and constantly display a gauge as a User
Interface element to help us provide that feedback.

Here is the source code, which is compatible with Windows and Mac OSX.

#include <iostream>
#include <atomic>
#include <chrono>
#ifdef _WIN32
#include <Windows.h>
#elif defined(__APPLE__)
#include <ApplicationServices/ApplicationServices.h>
#else
#error Unsupported platform
#endif
// Constants for activity level gauge
constexpr int MinActivityLevel = 0;
constexpr int MaxActivityLevel = 10;
constexpr int GaugeWidth = 20;
constexpr int InactivityThreshold = 5; // Number of seconds of inactivity before decrementing activity level

// Atomic variable to hold the current activity level
std::atomic<int> activityLevel(0);
std::chrono::time_point<std::chrono::system_clock> lastActivityTime;

// Low-level keyboard hook procedure
#ifdef _WIN32
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode >= 0)
    {
        // Increase the activity level when a key is pressed
        if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
        {
            activityLevel = std::min<int>(activityLevel.load() + 1, MaxActivityLevel);
            lastActivityTime = std::chrono::system_clock::now(); // Update last activity time
        }
        else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
        {
            activityLevel = std::max<int>(activityLevel.load() - 0.5, MinActivityLevel);
            lastActivityTime = std::chrono::system_clock::now(); // Update last activity time
        }
    }

    // Call the next hook procedure in the hook chain
    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
#elif defined(__APPLE)
{
    CGEventRef KeyboardEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon)
    {
        if (type == kCGEventKeyDown || type == kCGEventFlagsChanged)
        {
            activityLevel = std::min<int>(activityLevel.load() + 1, MaxActivityLevel);
            lastActivityTime = std::chrono::system_clock::now(); // Update last activity time
        }
        else if (type == kCGEventKeyUp)
        {
            activityLevel = std::max<int>(activityLevel.load() - 0.5, MinActivityLevel);
            lastActivityTime = std::chrono::system_clock::now(); // Update last activity time
        }

        return event;
    }
}
#endif
// Low-level mouse hook procedure
#ifdef _WIN32
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode >= 0)
    {
        // Increase the activity level when the mouse moves
        if (wParam == WM_MOUSEMOVE)
        {
            activityLevel = std::min<int>(activityLevel.load() + 0.5, MaxActivityLevel);
            lastActivityTime = std::chrono::system_clock::now(); // Update last activity time
        }
        else if (wParam == WM_LBUTTONDOWN || wParam == WM_RBUTTONDOWN || wParam == WM_MBUTTONDOWN)
        {
            activityLevel = std::min<int>(activityLevel.load() + 1, MaxActivityLevel);
            lastActivityTime = std::chrono::system_clock::now(); // Update last activity time
        }
    }

    // Call the next hook procedure in the hook chain
    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
#elif defined (__APPLE__)
CGEventRef MouseEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon)
{
    if (type == kCGEventMouseMoved || type == kCGEventLeftMouseDown || type == kCGEventRightMouseDown)
    {
        activityLevel = std::min<int>(activityLevel.load() + 1, MaxActivityLevel);
        lastActivityTime = std::chrono::system_clock::now(); // Update last activity time
    }
    else if (type == kCGEventLeftMouseUp || type == kCGEventRightMouseUp)
    {
        activityLevel = std::max<int>(activityLevel.load() - 0.5, MinActivityLevel);
        lastActivityTime = std::chrono::system_clock::now(); // Update last activity time
    }

    return event;
}
#endif


// Function to calculate activity level based on keyboard and mouse events
void calculateActivityLevel()
{
#ifdef _WIN32

    // Set low-level keyboard hook
    HHOOK keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProc, nullptr, 0);
    if (!keyboardHook)
    {
        std::cerr << "Failed to set keyboard hook." << std::endl;
        return;
    }

    // Set low-level mouse hook
    HHOOK mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, nullptr, 0);
    if (!mouseHook)
    {
        std::cerr << "Failed to set mouse hook." << std::endl;
        return;
    }

    // Message loop to keep the hooks active
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Unhook the keyboard
        // Unhook the keyboard hook
    UnhookWindowsHookEx(keyboardHook);

    // Unhook the mouse hook
    UnhookWindowsHookEx(mouseHook);

#elif defined( __APPLE__)
    // Create the event tap for keyboard events
    CGEventMask keyboardEventMask = CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventFlagsChanged);
    CFMachPortRef keyboardEventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, keyboardEventMask, KeyboardEventCallback, nullptr);
    if (!keyboardEventTap)
    {
        std::cerr << "Failed to create keyboard event tap." << std::endl;
        return;
    }

    // Create a run loop source from the keyboard event tap
    CFRunLoopSourceRef keyboardRunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, keyboardEventTap, 0);
    if (!keyboardRunLoopSource)
    {
        std::cerr << "Failed to create keyboard run loop source." << std::endl;
        CFRelease(keyboardEventTap);
        return;
    }

    // Add the keyboard run loop source to the current run loop
    CFRunLoopAddSource(CFRunLoopGetCurrent(), keyboardRunLoopSource, kCFRunLoopDefaultMode);

    // Enable the keyboard event tap
    CGEventTapEnable(keyboardEventTap, true);

    // Create the event tap for mouse events
    CGEventMask mouseEventMask = CGEventMaskBit(kCGEventMouseMoved) | CGEventMaskBit(kCGEventLeftMouseDown) | CGEventMaskBit(kCGEventRightMouseDown) | CGEventMaskBit(kCGEventLeftMouseUp) | CGEventMaskBit(kCGEventRightMouseUp);
    CFMachPortRef mouseEventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, mouseEventMask, MouseEventCallback, nullptr);
    if (!mouseEventTap)
    {
        std::cerr << "Failed to create mouse event tap." << std::endl;
        return;
    }

    // Create a run loop source from the mouse event tap
    CFRunLoopSourceRef mouseRunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mouseEventTap, 0);
    if (!mouseRunLoopSource)
    {
        std::cerr << "Failed to create mouse run loop source." << std::endl;
        CFRelease(mouseEventTap);
        return;
    }

    // Add the mouse run loop source to the current run loop
    CFRunLoopAddSource(CFRunLoopGetCurrent(), mouseRunLoopSource, kCFRunLoopDefaultMode);

    // Enable the mouse event tap
    CGEventTapEnable(mouseEventTap, true);

    // Run the current run loop
    CFRunLoopRun();
#endif
}


// Function to display the color gradient gauge for a given activity level
// Function to display the color gradient gauge for a given activity level
void displayColorGradientGauge(int activityLevel)
{
    const int GaugeWidth = 20;

    // Define the color codes for gradient display
    const std::string ResetColor = "\033[0m";

    // Calculate the number of filled and empty cells for the activity level gauge
    int filledCells = std::round(static_cast<double>(GaugeWidth * activityLevel) / 10);
    int emptyCells = GaugeWidth - filledCells;

    // Display the activity level gauge
    std::cout << "[";

    // Display the filled cells with color gradient
    for (int i = 0; i < filledCells; ++i)
    {
        double percentage = static_cast<double>(i) / (GaugeWidth - 1);
        if (percentage <= 0.5)
        {
            // Gradient from light green to yellow
            int greenValue = static_cast<int>(255 - 128 * percentage / 0.5);
            int redValue = static_cast<int>(255 * percentage / 0.5);
            std::cout << "\033[38;2;" << redValue << ";" << greenValue << ";" << 0 << "m#";
        }
        else
        {
            // Gradient from yellow to red
            int redValue = 255;
            int greenValue = static_cast<int>(255 * (1.0 - percentage) / 0.5);
            std::cout << "\033[38;2;" << redValue << ";" << greenValue << ";" << 0 << "m#";
        }
    }

    // Display the empty cells in the default color
    std::cout << ResetColor;
    for (int i = 0; i < emptyCells; ++i)
    {
        std::cout << "-";
    }

    std::cout << "]" << std::endl;
}
// Function to display the activity gauge on the console
void displayActivityGauge()
{
    int previousLevel = -1; // Store the previous activity level
    lastActivityTime = std::chrono::system_clock::now();

    while (true)
    {
        int currentLevel = activityLevel.load(); // Read the current activity level

        // Check for inactivity
        std::chrono::time_point<std::chrono::system_clock> currentTime = std::chrono::system_clock::now();
        std::chrono::duration<double> elapsedTime = currentTime - lastActivityTime;
        if (elapsedTime.count() >= InactivityThreshold)
        {
            activityLevel = std::max<int>(activityLevel.load() - 1, MinActivityLevel);
            lastActivityTime = currentTime; // Update last activity time
        }

        // Clear the console
#ifdef _WIN32
        system("cls");
#elif defined(__APPLE__)
        system("clear");
#endif

        // Display the activity level gauge
        std::cout << "System Activity Level Gauge" << std::endl;
        displayColorGradientGauge(currentLevel);
        previousLevel = currentLevel; // Update the previous activity level

        // Sleep
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}


int main()
{
  // Start the activity level calculation thread
  std::thread calculationThread(calculateActivityLevel);
  // Start the activity gauge display thread
  std::thread displayThread(displayActivityGauge);

  // Wait for the threads to finish
  calculationThread.join();
  displayThread.join();

  return 0;
}

Posted in Uncategorized | Tagged , , , , , , , , | Leave a comment

A Slot Machine using ASCII Art

As explained in Chapter 12 of our book, Learning C++, the idea of this exercise is to develop a Las Vegas-style slot machine game. Slot machines allow the player to press a button and get 3 or more images. The goal is to be lucky enough to get identical images in identical colors. Since our code snippets throughout this book are console based, we will be using ASCII art images. For simplicity, the images are ones from a deck of cards, which creates a somewhat new game that combines a cards deck game and a slot machine.

What is ASCII art

ASCII art is a graphic design technique that uses printable characters from the ASCII standard to create images and designs. We discussed ASCII in Chapter 7. In a nutshell, ASCII art offers an alternative to drawing an image with lines and shapes. In ASCII art, you build the image by arranging ASCII characters in a way that the different characters and symbols take a form that resembles the subject you want to depict. Essentially, the ASCII characters serve as the ‘pixels’ of the artwork.

Our Program

You can see our program on our YouTube channel. Here is how it looks. Here is the source code of our program, and how it works.

The Source Code

#include <algorithm>
#include <chrono>
#include <iostream>
#include <random>
#include <vector>
#undef _WIN32
#ifdef _WIN32
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#endif

#ifdef _WIN32
#define CLEAR_SCREEN system("cls")
#else
#define CLEAR_SCREEN system("clear")
#endif

enum class Color
{
    DEFAULT,
    RED,
    GREEN,
    BLUE,
    YELLOW,
    MAGENTA,
    CYAN
};

struct Pixel
{
    char character;
    Color color;
};

void setTextColor(Color color)
{
    switch (color)
    {
        case Color::DEFAULT:
            std::cout << "\033[0m";
            break;
        case Color::RED:
            std::cout << "\033[31m";
            break;
        case Color::GREEN:
            std::cout << "\033[32m";
            break;
        case Color::BLUE:
            std::cout << "\033[34m";
            break;
        case Color::YELLOW:
            std::cout << "\033[33m";
            break;
        case Color::MAGENTA:
            std::cout << "\033[35m";
            break;
        case Color::CYAN:
            std::cout << "\033[36m";
            break;
    }
}

void displayMatrix(const std::vector<std::vector<Pixel>>& matrix)
{
    for (const auto& row : matrix)
    {
        for (const Pixel& pixel : row)
        {
            setTextColor(pixel.color);
            std::cout << pixel.character;
        }

        std::cout << std::endl;
    }

    setTextColor(Color::DEFAULT);
}

std::vector<std::string> generateRandomImage()
{
    // #A

    std::vector<std::vector<std::string>> images = 
    {
        {" _______ ", "|A      |", "|       |", "|   o   |", "|       |",
         "|_______|"},
        {" _______ ", "|2      |", "|   o   |", "|       |", "|   o   |",
         "|_______|"},
        {" _______ ", "|3      |", "|   o   |", "|   o   |", "|   o   |",
         "|_______|"},
        {" _______ ", "|4      |", "| o   o |", "|       |", "| o   o |",
         "|_______|"},
        {" _______ ", "|5      |", "| o   o |", "|   o   |", "| o   o |",
         "|_______|"},
        {" _______ ", "|6      |", "| o   o |", "| o   o |", "| o   o |",
         "|_______|"},
        {" _______ ", "|7      |", "| o o o |", "|       |", "| o o o |",
         "|_______|"},
        {" _______ ", "|8      |", "| o o o |", "| o   o |", "| o o o |",
         "|_______|"},
        {" _______ ", "|9      |", "| o o o |", "| o o o |", "| o o o |",
         "|_______|"},
        {" _______ ", "|10     |", "| o o o |", "|o o o o|", "| o o o |",
         "|_______|"},
        {" _______ ", "|J      |", "|   o   |", "|   o   |", "| o   o |",
         "|_______|"},
        {" _______ ", "|Q      |", "|   o   |", "| o o o |", "| o   o |",
         "|_______|"},
        {" _______ ", "|K      |", "| o   o |", "| o o o |", "| o   o |",
         "|_______|"},
        {" _______ ", "|A      |", "|   /\\ |", "|  /  \\|", "| /____\|",
         "|_______|"},
        {" _______ ", "|2      |", "| /\\ /\|", "| \\/ \\|", "|       |",
         "|_______|"},
        {" _______ ", "|3      |", "|   /\\ |", "|       |", "|  \\/  |",
         "|_______|"},
        {" _______ ", "|4      |", "| /\\  /|", "|       |", "|\\/  \\|",
         "|_______|"},
        {" _______ ", "|5      |", "| /\\  /|", "|   /\\ |", "|\\/  \\|",
         "|_______|"},
        {" _______ ", "|6      |", "| /\\  /|", "| /\\  /|", "|\\/  \\|",
         "|_______|"},
        {" _______ ", "|7      |", "| /\\ /\|", "|       |", "|  /\\  |",
         "|_______|"},
        {" _______ ", "|8      |", "| /\\ /\|", "| /  \\ |", "|  \\/  |",
         "|_______|"},
        {" _______ ", "|9      |", "| /\\ /\|", "|  /\\  |", "|  \\/  |",
         "|_______|"},
        {" _______ ", "|10     |", "|  /\\  |", "| /  \\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|J      |", "|   /\\ |", "|  /  \\|", "|/____\\|",
         "|_______|"},
        {" _______ ", "|Q      |", "|   /\\ |", "|  /  \\|", "|/    \\|",
         "|_______|"},
        {" _______ ", "|K      |", "|   /\\ |", "|  /  \\|", "|/____\\|",
         "|_______|"},
        {" _______ ", "|A      |", "|  /\\  |", "| /__\\ |", "|/    \\|",
         "|_______|"},
        {" _______ ", "|2      |", "|  /\\  |", "| /_\\  |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|3      |", "|  /\\  |", "| \\_/  |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|4      |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|5      |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|6      |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|7      |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|8      |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|9      |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|10     |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|J      |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|Q      |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|K      |", "|  /\\  |", "| //_\\ |", "|/____\\|",
         "|_______|"},
        {" _______ ", "|A      |", "|  _    |", "| | |   |", "| |_|   |",
         "|_______|"},
        {" _______ ", "|2      |", "|   _   |", "|  |_)  |", "|   /   |",
         "|_______|"},
        {" _______ ", "|3      |", "|   __  |", "|  |_ \\|", "|____/\\|",
         "|_______|"},
        {" _______ ", "|4      |", "|  |_ _||", "|   |   |", "|   |   |",
         "|_______|"},
        {" _______ ", "|5      |", "|  |__  |", "|     | |", "|  |__| |",
         "|_______|"},
        {" _______ ", "|6      |", "|  |__  |", "|  |__| |", "|  |__| |",
         "|_______|"},
        {" _______ ", "|7      |", "| |___  |", "|    / /|", "|   / / |",
         "|_______|"},
        {" _______ ", "|8      |", "|  /\\  |", "| /  \\ |", "| \\  / |",
         "|_______|"},
        {" _______ ", "|9      |", "|  /\\  |", "| |__|  |", "|   /   |",
         "|_______|"},
        {" _______ ", "|10     |", "|  /\\  |", "| |__)  |", "|____   |",
         "|_______|"},
        {" _______ ", "|J      |", "|    /\\|", "|   / / |", "|  / /  |",
         "|_/_____|"},
        {" _______ ", "|Q      |", "|    /\\|", "|   /  \|", "|  |\\__|",
         "|_/_____|"},
        {" _______ ", "|K      |", "|   \\_/|", "|   / \\|", "|  |   \|",
         "|_/_____|"}};

    std::random_device rd;
    std::mt19937 rng(rd());
    std::shuffle(images.begin(), images.end(), rng);

    std::vector<std::string> selectedImages;

    int randomIndex = rand() % images.size();
    const std::vector<std::string>& image = images[randomIndex];
    for (const std::string& line : image)
    {
        selectedImages.push_back(line);
    }
    images.erase(images.begin() + randomIndex);

    return selectedImages;
}

std::vector<std::vector<Pixel>>
composeCombinedImage(const std::vector<std::string>& image1,
                     const std::vector<std::string>& image2,
                     const std::vector<std::string>& image3, Color color1,
                     Color color2, Color color3)
{
    // #B
    int maxLines = std::max({image1.size(), image2.size(), image3.size()});

    // #C
    int maxWidth =
        std::max({image1[0].size(), image2[0].size(), image3[0].size()});

    std::vector<std::vector<Pixel>> combinedImage(maxLines);

    // #D
    for (int i = 0; i < image1.size(); ++i)
    {
        for (char c : image1[i])
        {
            combinedImage[i].push_back({c, color1});
        }

        // #E
        int spacesToAdd = maxWidth - image1[i].size();
        for (int j = 0; j < spacesToAdd; ++j)
        {
            combinedImage[i].push_back({' ', Color::DEFAULT});
        }
    }

    // #F
    for (int i = image1.size(); i < maxLines; ++i)
    {
        for (int j = 0; j < maxWidth; ++j)
        {
            combinedImage[i].push_back({' ', Color::DEFAULT});
        }
    }

    // #G
    for (int i = 0; i < image2.size(); ++i)
    {
        for (char c : image2[i])
        {
            combinedImage[i].push_back({c, color2});
        }

        // #H
        int spacesToAdd = maxWidth - image2[i].size();
        for (int j = 0; j < spacesToAdd; ++j)
        {
            combinedImage[i].push_back({' ', Color::DEFAULT});
        }
    }

    // #I
    for (int i = image2.size(); i < maxLines; ++i)
    {
        for (int j = 0; j < maxWidth; ++j)
        {
            combinedImage[i].push_back({' ', Color::DEFAULT});
        }
    }

    // #J
    for (int i = 0; i < image3.size(); ++i)
    {
        for (char c : image3[i])
        {
            combinedImage[i].push_back({c, color3});
        }

        // #K
        int spacesToAdd = maxWidth - image3[i].size();
        for (int j = 0; j < spacesToAdd; ++j)
        {
            combinedImage[i].push_back({' ', Color::DEFAULT});
        }
    }

    return combinedImage;
}

bool checkWin(const std::vector<std::string>& image1,
              const std::vector<std::string>& image2,
              const std::vector<std::string>& image3, Color color1,
              Color color2, Color color3)
{
    return (image1 == image2 && image2 == image3 && color1 == color2 &&
            color2 == color3);
}

int main()
{
    srand(static_cast<unsigned>(time(0)));

    std::cout << "Welcome to the Slot Machine Game!" << std::endl;
    std::cout << "Press any key to start..." << std::endl;

    while (true)
    {
        _getch();

        CLEAR_SCREEN;

        // #L
        std::vector<std::string> image1 = generateRandomImage();
        std::vector<std::string> image2 = generateRandomImage();
        std::vector<std::string> image3 = generateRandomImage();

        // #M
        Color color1 =
            static_cast<Color>(rand() % static_cast<int>(Color::CYAN) + 1);
        Color color2 =
            static_cast<Color>(rand() % static_cast<int>(Color::CYAN) + 1);
        Color color3 =
            static_cast<Color>(rand() % static_cast<int>(Color::CYAN) + 1);

        // #N
        std::vector<std::vector<Pixel>> combinedImage = composeCombinedImage(
            image1, image2, image3, color1, color2, color3);

        // #O
        displayMatrix(combinedImage);

        // #P
        if (checkWin(image1, image2, image3, color1, color2, color3))
        {
            std::cout << "Congratulations! You win!" << std::endl;
        }
        else
        {
            std::cout << "Sorry, better luck next time!" << std::endl;
        }

        std::cout << std::endl;
    }

    return 0;
}
Here are the code annotations:


#A Array of ASCII art images
#B Determine the maximum number of lines among the three images
#C Determine the maximum width among the three images
#D Add pixels from image1
#E Add spaces to match the maximum width
#F Add empty pixels to align with image2 and image3
#G Add pixels from image2
#H Add spaces to match the maximum width
#I Add empty pixels to align with image3
#J Add pixels from image3
#K Add spaces to match the maximum width
#L Generate three random images
#M Generate three random colors
#N Compose the combined image
#O Display the combined image
#P Check if the user wins

Posted in Code Snippets | Tagged , , , , , , , , | Leave a comment

Our YouTube channel

We have created a new YouTube channel where we intend to publish clips showing code listings from the book and how the programs actually look and feel.

Our amazing maze program is shown in this video.

Our final exercise, Brain-Train is shown here.

More videos to come soon…

Posted in Uncategorized | Tagged , , , , , , , , | Leave a comment