Monday, 15 May 2023

Chapter 26 // Exercise 5 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 5

Add a test to the set of binary_search tests to try and catch the (unlikely) error of a binary_search modifying the sequence.

I was a bit confused by this as std::binary_search does not modify anything passed to it. The sequence must already be sorted (or it won't work properly). I tried googling for this and couldn't find any information on it. I then asked ChatGPT-4 and it said std::binary_search will not modify but could provide undefined behaviour if the sequence wasn't sorted.

I then thought that maybe, the predicate function you can pass to binary_search would be able to modify the sequence in some way however the variables are passed to the predicate by const so you would have to const_cast to change them which is considered very bad.

I then told Chat this and gave it the name of the book, the author and the exercise and it pretty much told me "you're reading into this too much, you're supposed to just write a test that shows it doesn't modify the sequence". Should've added "duh" at the end as well.

Once again, taking things literally and overthinking was not the answer.

So instead in TestAll I made a local copy of the sequence then added an if that checks if the sequence has changed after the binary_search was done. No new test needed.


Monday, 8 May 2023

Ray Tracing In One Weekend // Making it Multi-threaded

So back in February 2021 I completed the online book; Ray Tracing In One Weekend. You can read my post (and review) on it here:
https://lptcp.blogspot.com/2021/02/ray-tracing-in-one-weekend-review.html

I originally wrote and executed the ray tracer on my previous computer; a 4 core intel 4790k. As the program is single threaded, it took 27 minutes to render a single 400x225 image with 100 samples per pixel. I attempted to do a 1k version but after 6 hours I stopped the program.

2 years later, I sat back down and decided now was the time to finally attempt to multithread it to speed it up. I actually tried to after writing the first post however, at the time, my programming experience was still a bit too limited and I just couldn't wrap my head around the concepts of multi-threaded programming. With an extra 2 years experience under my belt, I was able to get it done.

Code:

My attempt isn't perfect but it I'm happy with the result and I can improve on it from here. 

I initially did some very quick "fun with multithreading" exercises in the console window:

I'll probably make a seperate post on simple threading as the MSDN documentation is great but it's not great for someone with little experience and these are the types of examples I would've wanted 2 years ago (not lambda's operating on matrices).

Adding More Threads
So the main issue with just adding more threads to the ray tracer is the output method. It currently writes each pixel as a line in a text file which is saved as a PPM. This means that cout <<  is accessing a single text file every time a pixel is calculated.

If you add more threads, cout is not thread safe and the threads will fight with each other to access the text file and data will be overwritten/jumbled up.

A simple fix for this would be for a thread to lock access to the text file while it's accessing it, then unlock when it's done. This would prevent corrupted data. However, threads spun up by a for loop do not guarantee any kind of order so whereas all the pixels would be calculated correctly, the picture would still be a jumbled mess.

My solution was to therefore split up the picture into "tiles" or "chunks". I was inspired by Blender's cycles renderer which renders a tile at a time. If you choose to render with your CPU, it will use a core per tile and you can see it working on X-cores at a time.

Using std::thread::hardware_concurrency(), I allowed the program to spin up as many threads as it can. Each of these threads then writes pixels to a fixed-sized 2D array. Each thread writes to a certain portion of the array so they aren't overwriting each other. To visualise, if the image is 500x500 and using 4 threads, then thread 1 would render pixels x0-x249 and y0-y249:


Then, once all the threads have have finished, we then go through the vector array of pixels and write them out to a file to create the image.

I ran this new code on my new pc; a 16-Core AMD 3950x using the same width and pixel sample as the old pc and it completed the image in 2 minutes (vs 27 on a single core). It outputs it upside down but that can be easily fixed:

A 1K version took just over 7 minutes; the power of multithreading!

My original solution to this used 4 hardcoded threads that wrote to their own text files and then once they were finished, I stitched the files together. However, I completely forgot how PPM files worked and my chunks wouldn't stitch together properly. Using an array bypassed this problem.

My next task will be to transfer the output to the screen using a GUI library like FLTK or SMFL so you can see the tiles being created in real-time. 

Tuesday, 18 April 2023

Dev Diary // Using ChatGPT to make an Idle Clicker Game

There is an idle clicker game called Cookie Clicker. It's amazing, you click a giant cookie, to make more cookies, to buy buildings and upgrades that will make cookies. There is a grandma apocalypse, a dragon called krumblor and ascensions. Kittens can give you milk multipliers which boosts your cookies per second. The entire goal is to make as many cookies as possible per second. It is utterly addictive and completely free:

Cookie Clicker was first released back in 2013 and I started playing around 2015. Soon after release, the creator made a website: Idle Game Maker, which turned the cookie clicker code into an engine and allowed users to make their own idle game. It's really intuitive and easy to use; and you can host your game for free using their website.

Whilst messing around with that engine recently; I realised a lot is hidden away from the user. Being an engine programmer, I wanted to know more about how clicker games actually function but I have next to no knowledge of web development; enter ChatGPT.


The above video is me sitting down with Chat and trying to get it to create the bare bones of an idle clicker with as little coding from me as possible and I'm amazed at the results. I filmed for 50 minutes and by the end of that I had a clicker game with 2 buildings and a lucky button that appears randomly. I edited about 3 lines of code in total but used ChatGPT to generate everything for me. Even asking it to move CSS elements to different places.

I don't think I would continue trying to get it to create the entire game as that would get a bit tedious; but now I've got the bare bones, I've got a solid starting point to tinker with. I would probably start making it look prettier first before adding anything else.

HTML, CSS and Javascript are completely outside my zone of knowledge but having looked through the generated code; the html and JS is actually pretty readable and easy to add to. The CSS on the other hand will require some more thought.

You can find all the code generated in this session here:

Monday, 17 April 2023

Chapter 26 // Exercise 4 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 4

Devise a format for test data so that you can define a sequence once and run several tests against it.

I honestly did not understand what he meant by this at first. I guessed that we had defined the test sequence
{ testName value { sequence } testResult }
and so he wants us to be able to define any test sequence like:
{ testName { sequence } value testResult } 
or even
{ testName } { value } { sequence } { testResult }

So in this case I decided to make an enum that held each part of the sequence:

Then, the user could just create a vector<TestSequences> and pass through whatever sequence type they want like:

I then made the functions into a class that held the sequence type, so it could check it whilst using operator>>. I then used a switch statement to define what to do for each part of sequence.

All you have to do then is create a TestSequence, give it a type, set the sequence type and then load in the text file.

It does rely on there being spaces though. Without the spaces, the entire line will be read in however, I imagine that that would be a major "you must do this when writing tests" line in the documentation for creating tests.

Sunday, 16 April 2023

Chapter 26 // Exercise 3 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 3

Repeat exercise 1 with the version of binary_search that takes a comparison criterion. Make a list of new opportunities for errors introduced by that extra argument.

I continued using the code from exercise 2 and created a new testAll function called testAllWithPredicate. This takes in the input stream and a function pointer. I then modified testType to take in function pointer and used some std library comparison functions found in <functional>: