Announcing Ultima 1 Revenge

I am currently working on an engine port of Ultima 1: The First Age of Darkness, called Ultima 1 Revenge. This means I am reverse engineering the game files and building a new game engine for it, using C++ and SDL2.

Ultima 1: The First Age of Darkness was one of the first open-world Computer Role Playing Games (CRPGs). Originally released in 1981 and remade for the PC in 1986, Ultima 1 was followed by a series of games that lasted almost 30 years, generated a cult following, inspired countless other RPGs, and pushed the boundaries of technology.

Ultima 1 is a fairly weird game, featuring an unusual combination of medieval fantasy and space travel. The world of Sosaria is being ravaged by the monsters of the evil wizard Mondain. Before you can face him in battle, you have to complete dungeoneering quests in the service of the lords of the land, become a space ace, free a princess, and travel back in time using a time machine.

The 1986 PC remake, on which the Ultima 1 Revenge project is based, is very old technology, by today’s standards. Still, it provides a vast array of learning areas. The game’s graphics are made up of three tilesets (CGA, EGA, and Tandy 1000), giving a choice for the differently powered machines of the time. The game world is stored in a small map file, where each four bits is an index into the tileset you’re using. Space travel is a combination of top-down 2D and first-person views. The dungeons are simple 3D-like line drawings, randomly generated based on a seed stored in the savegame file (so they remain consistent for each playthrough, but change if you start a new game). The different parts of the game run in different executables, and a special savegame file is used to pipe the player state from one to the other. Savegames mostly use 16-bit numbers, with the least significant byte stored first. Decoding the game files is an ongoing effort that powers tools such as the online map viewer I built in 2015, and the engine itself.

Today, I have released a demo of the engine. So if you own a copy of Ultima 1 (if you don’t, you can grab a copy from GOG), grab it from the downloads page, set the path to your original Ultima 1 folder in the settings file, and take a tour of Sosaria!

Making Webpages Printer Friendly

The sample from this article is available at the Gigi Labs BitBucket repository.

Introduction

I am a bit of a living paradox. Despite being a millennial and a techie, I often identify myself better with earlier generations: I like 20th century entertainment (video games, movies and music) a lot better than the more contemporary stuff, I stay away from overhyped tech I don’t need, and… I like to read text printed on good old paper a lot better than reading from a computer monitor or a tiny mobile phone screen.

Back when I launched my first website in 2002, it was common for content-based websites that wanted to be printable to provide a separate “printer-friendly version” of the page. This practice is still common today. It seemed like a lot of hassle to me at the time, though, to have to maintain two separate versions of the same content.

Fortunately, it was not long before I discoverd @media print. Indeed, the same media queries we use nowadays to allow our websites to dynamically adapt to different screen sizes (also known as responsive web design) already existed in limited form back then in CSS2. I might have used @media print as early as 2003. If nowadays we give so much importance to different output devices such as mobile phones and tablet, there is really no reason why we should disregard printers.

Showcase

Microsoft’s article on How to auto scale a cloud service is a fine example of a great printer-friendly page. Look, the page itself has lots of navigation and stuff on the side that you don’t care about when reading a printed page:

But when you go and print, poof, all thet extra stuff is gone, and you are left with a perfect, content-only page:

Not all websites have this consideration for printers. A few months ago, I opened a bug report about the Microsoft Orleans documentation not being printer-friendly. As you can see in the bug report, while the navigation was indeed being hidden, it was still taking up space, resulting in lots of wasted whitespace on the side. There was also a problem with some text overlaying the content. This was identified as a bug in DocFX, the software used to generate the Orleans documentation, and has since been fixed.

Forbes is a much worse offender. Look at how much ink and paper you have to waste on empty space, ads, videos, and stock photos, when all you want to do is read the article:

Well, at least there is some way you can read the content. Let’s now take a look at the Akka .NET documentation, for instance, the Akka.Cluster Overview:

This looks ike something I should easily be able to print and read on a plane, right? Let’s try that.

The Akka .NET team take the prize here, because the printed version of their documentation is a lot more interesting than the version you read online.

Using @media print

Making a page printer-friendly is not rocket science. Essentially, all you need is to hide stuff you don’t need (e.g. navigation) and resize your content to make full use of the available space. You can do this easily with @media print; the rules you specify inside its context will apply only to printing devices:

@media print
{
    /* Hide navigation etc */
}

Let’s take a really simple example of how to do this. This is a website layout that you can create in a few minutes. It consists of a main heading, a left navigation, and a main content section:

<!doctype html>
<html>
    <head>
        <title>My Website</title>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
        <nav id="leftnav">
            <ul>
                <li>Home</li>
                <li>About</li>
                <li>etc...</li>
            </ul>
        </nav>
        <header>My Website</header>
        
        <section id="main">
            <header>Content!</header>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras tincidunt mollis tellus, eget maximus enim mollis lacinia. Phasellus suscipit bibendum tristique. Etiam laoreet justo ac erat tempus volutpat. Nam auctor viverra commodo. Duis magna arcu, tristique eget felis sit amet, placerat facilisis ipsum. Sed nibh dolor, congue quis ultricies sit amet, bibendum non eros. Maecenas lectus dolor, elementum interdum hendrerit vitae, placerat a justo. Sed tempus dignissim consectetur. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent tincidunt hendrerit metus, eget egestas odio volutpat scelerisque. Suspendisse sit amet bibendum eros.</p>
            <p>Nullam vestibulum blandit velit, ornare porttitor quam vehicula ac. Nam egestas orci quis orci porttitor, at lacinia risus faucibus. Vestibulum ut purus nibh. Ut ac metus magna. Nunc dictum magna non molestie luctus. Cras ornare dolor nec leo posuere, at ullamcorper lectus cursus. Curabitur pellentesque sem et nibh pellentesque pulvinar. Vestibulum non libero fermentum, luctus libero et, molestie nibh. Etiam ligula enim, mollis et ullamcorper vitae, dapibus eu tortor. Praesent pharetra volutpat orci, non lacinia leo consectetur in. Nulla consequat arcu dignissim eros ultrices, sit amet luctus ipsum ultricies.</p>
            <p>Cras non tellus leo. Cras malesuada sollicitudin mi quis tincidunt. Morbi facilisis fermentum aliquam. Donec tempor orci est, id porta massa varius a. Phasellus pharetra arcu nisl, at eleifend magna rhoncus et. Mauris fermentum diam eget accumsan dignissim. Vivamus pharetra condimentum ante, eget ultricies nisi ornare quis. Aliquam erat volutpat.</p>
            <p>Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse placerat nibh sit amet erat dignissim rhoncus. Suspendisse ac ornare augue. Proin metus diam, convallis a dolor eget, gravida auctor orci. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nullam at risus non neque tincidunt aliquam. Nulla convallis interdum imperdiet. Vivamus condimentum erat eget lacus tempor, id vulputate tortor condimentum. Cras ipsum velit, cursus eu sodales vitae, ornare a magna. Mauris ullamcorper gravida vestibulum. Phasellus rhoncus metus nec nulla finibus sagittis. Ut sit amet enim in purus viverra tempor vel convallis tellus.</p>
            <p>Nulla maximus urna non leo eleifend, sed efficitur libero gravida. Curabitur quis velit quam. Vivamus ac sem dui. Suspendisse quis sagittis enim. Cras feugiat nibh in lorem faucibus lobortis. Quisque sit amet nisl massa. Fusce finibus facilisis erat sed dictum. Donec ac enim ut est mattis bibendum pharetra non arcu. In hac habitasse platea dictumst. In mattis, justo non tempor convallis, ex enim luctus velit, a facilisis quam erat nec augue. Nam ac metus vel velit commodo iaculis quis a magna. Vestibulum volutpat sapien lorem, et convallis lectus lacinia non. Vestibulum fermentum varius rutrum. Etiam dignissim leo at pulvinar posuere. Sed metus nibh, commodo vitae tincidunt sit amet, pretium ut nibh. In pellentesque dui egestas, rhoncus velit nec, lobortis magna.</p>
        </section>
    </body>
</html>

We’ll throw in some CSS to put each part in its place and make the whole thing look at least semi-decent:

body
{
    margin-left: 200px;
    font-family: Arial;
    background-color: #EEE;
}

nav#leftnav
{
    position: absolute;
    margin-left: -180px;
    margin-top: 78px;
    width: 150px;
    padding-left: 10px;
    background-color: #E3E3E3;
    border-radius: 8px;
    border: 2px solid #E7E7E7;
}

nav#leftnav ul
{
    padding: 0px;
}

nav#leftnav li
{
    list-style-type: none;
}

body > header
{
    text-align: center;
    font-size: 24pt;
    font-weight: bold;
    padding-bottom: 42px;
    margin-top: 26px;
}

section#main
{
    background-color: #E3E3E3;
    margin-right: 20px;
    padding: 8px;
    border-radius: 8px;
    border: 2px solid #E7E7E7;
}

section#main > header
{
    font-size: 16pt;
    font-weight: bold;
    margin-left: 8px;
    border-bottom: dashed 2px #FFF;
}

So when you fire it up in a browser, it looks like this:

Now if we print this, we get:

Not good! Let’s fix this with a little printer-specific CSS:

@media print
{
    body
    {
        margin-left: 0px; /* remove left indentation */
    }
    
    nav#leftnav
    {
        display: none; /* hide navigation */
    }
    
    section#main
    {
        border: 0px; /* remove border */
    }
}

It looks much better now:

You can see how easy it is to make content fit neatly in a printed page without waste. In fact, in this particular example, we saved having to print a second page for content that is perfectly capable of fitting in a single page.

Not all websites need to be printer-friendly. But if your website is full of content that is meant to be read, then making them printer-friendly is probably a good idea. Given how easy it is, there is no reason why you shouldn’t make a handful of people like me happy. 🙂

The Sorry State of the Web in 2016

When I republished my article “Bypassing a Login Form using SQL Injection“, it received a mixed reception. While some applauded the effort to raise awareness on bad coding practices leading to serious security vulnerabilities (which was the intent), others were shocked. Comments on the articles and on Reddit were basically variants of “That code sucks” (of course it sucks, that’s exactly what the article is trying to show) and “No one does these things any more”.

If you’ve had the luxury of believing that everybody writes proper code, then here are a few things (limited to my own personal experience) that I ran into during 2016, and in these first few days of 2017.

SQL Injection

I was filling in a form on the website of a local financial institution a few days ago, when I ran into this:

It was caused by the apostrophe in my surname which caused a syntax error in the SQL INSERT statement. The amateur who developed this website didn’t even bother to do basic validation, let alone parameterised queries which would also have safeguarded against SQL injection.

Airlines and Apostrophes

My experience with airlines is that they tend to go to the other extreme. In order to keep their websites safe, they simply ban apostrophes altogether. This is a pain in the ass when your surname actually has an apostrophe in it, and airlines stress the importance of entering your name and surname exactly as they show on your passport.

United Airlines, for instance, believe that the surname I was born with isn’t valid:

Virgin America, similarly, takes issue with my whole full name:

We’re in 2017. Even shitty Air Malta accepts apostrophes. All you need to do is use parameterised queries or a proper ORM. Using silly and generic error messages doesn’t help avoid customer frustration.

Plagiarism

Speaking of Air Malta, here’s a classic which they ripped off from some other US airline:

US Federal law? In Malta? Go home, Air Malta. You’re drunk.

Don’t Piss People Off

I’ve had a really terrible experience with booking domestic flights with US airlines. There is always some problem when it comes to paying online with a VISA.

United Airlines, for instance, only accepts payments from a specific set of countries. Malta is not on that list, and there is no “Other” option:

Delta gives a variety of billing-address-related errors depending on what you enter.

Southwest provides fields to cater for payments coming from outside the US:

And yet, you need to provide a US State, Zip Code and Billing Phone Number.

The worst offender, though, is Virgin America. While the overall experience of their AngularJS website is quite pleasant, paying online is a hair-ripping experience. If you choose a country where the State field does not apply (such as Malta, or the UK), a validation error fires in JavaScript (it doesn’t appear in the UI) and does not let you proceed:

It’s almost like the developers of this website didn’t quite test their form. Because developers normally do test their code, right? Right?

Well, when I reported the error to Virgin, and offered to provide a screenshot and steps to reproduce, the support representative gave me this canned bullshit:

“So sorry for the web error. Can recommend using one of our compatible browsers chrome or safari. Clearing your cookies and cache.  If no resolve please give reservations a ring [redacted] or international [redacted] you’ll hear a beep then silence while it transfers you to an available agent.  Thanks for reaching out.~”

I had to escalate the issue just so that I could send in the screenshot to forward to their IT department. Similarly, I was advised to complete the booking over the phone.

Over a month later, the issue is still there. It’s no wonder they want people to book via telephone. Aside from the international call rate, they charge a whooping $20 for a sales rep to book you over the phone.

Use SSL for Credit Card And Personal Details

In July 2016, I wanted to book a course from the local Lifelong Learning unit. I found that they were accepting credit card details via insecure HTTP. Ironically, free courses (not needing a credit card) could be booked over an HTTPS channel. When I told them about this, the response excuse was:

“This is the system any Maltese Government Department have been using for the past years.”

It is NOT okay (and it’s probably illegal) to transmit personal information, let alone credit card details, over an insecure channel. That information can be intercepted by unauthorised parties and leaked for the world to see, as has happened many times before thanks to large companies that didn’t take this stuff seriously.

To make matters worse, Lifelong Learning don’t accept cheques by post, so if you’re not comfortable booking online, you have to go medieval and bring yourself to their department to give them a cheque in person.

I couldn’t verify if this problem persists today, as the booking form was completely broken when I tried filling it a few days ago – I couldn’t even get to the payment screen.

Update 8th January 2017: I have now been able to reproduce this issue. The following screenshots are proof, using the Photo Editing course as an example. I nudged the form a little to the right so that it doesn’t get covered by the security popup.

Update 9th January 2017: Someone pointed out that the credit card form is actually an iframe served over HTTPS. That’s a little better, but:

  • From a security standpoint, it’s still not secure.
  • From a user experience perspective, a user has no way of knowing whether the page is secure, because the iframe’s URL is hidden and the browser does not show a padlock.
  • The other personal details (e.g. address, telephone, etc) are still transmitted unencrypted.

Do Server Side Validation

When Times of Malta launched their fancy new CMS-powered website a few years ago, they were the object of much derision. Many “premium” articles which were behind a paywall could be accessed simply by turning off JavaScript.

Nowadays, you can still access premium articles simply by opening an incognito window in your browser.

Let’s take a simple example. Here’s a letter I wrote to The Times a few years ago, which is protected by the paywall:


Back in 2014, I used to be able to access this article simply by opening it in an Incognito window. Let’s see if that still works in 2017:

Whoops, that’s the full text of the article, without paying anything!

Like those critics of my SQL injection article, you’d think that people today know that client-side validation is not enough, and that it is easy to bypass, and that its role is merely to provide better user experience and reduce unnecessary roundtrips to the server. The real validation still needs to be server-side.

Conclusion

Many people think we’re living in a golden age of technology. Web technology in particular is progressing at a breathtaking pace, and we have many more tools nowadays than we could possibly cope with.

And yet, we’re actually living in an age of terrible coding practices and ghastly user experience. With all that we’ve learned in over two decades of web development, we keep making the same silly mistakes over and over again.

I hope that those who bashed my SQL injection article will excuse me if I keep on writing beginner-level articles to raise awareness.

Drawing Lines with SDL2

We’ve seen before how we can draw pixels using SDL2, similar to a basic Paint application. The next step is to draw lines. To do this, we can reuse our pixel drawing code with something like Bresenham’s line algorithm, or we can use SDL_RenderDrawLine(), which SDL2 gives us out of the box.

The source code for this article is available at the Gigi Labs BitBucket repository.

Let’s start with the following code, which gives us a basic empty window:

#include <SDL.h>

int main(int argc, char ** argv)
{
    // variables

    bool quit = false;
    SDL_Event event;

    // init SDL

    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window * window = SDL_CreateWindow("SDL2 line drawing",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
    SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);

    // handle events

    while (!quit)
    {
        SDL_Delay(10);
        SDL_PollEvent(&event);

        switch (event.type)
        {
            case SDL_QUIT:
                quit = true;
                break;
            // TODO input handling code goes here
        }

        // clear window

        SDL_SetRenderDrawColor(renderer, 242, 242, 242, 255);
        SDL_RenderClear(renderer);

        // TODO rendering code goes here

        // render window

        SDL_RenderPresent(renderer);
    }

    // cleanup SDL

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

A line is usually drawn between two endpoints: (x1, y1) and (x2, y2). When the user presses the left mouse button, we’ll store the place he clicked as (x1, y1), and then draw a line to the current cursor position (x2, y2). When he lets go of the left mouse button, we’ll store that line.

Thus, we need an integer variable for each coordinate, and a boolean that indicates whether a line is being drawn (i.e. whether the left mouse button is pressed):

    int x1 = 0;
    int y1 = 0;
    int x2 = 0;
    int y2 = 0;
    bool drawing = false;

We can now add handlers for when the left mouse button is pressed and released. At a basic level, this involves toggling the drawing flag. However, when the left mouse button is pressed (i.e. when a new line is started), we also need to record the current cursor position as (x1, y1), and reset the value of (x2, y2).

            case SDL_MOUSEBUTTONDOWN:
                switch (event.button.button)
                {
                    case SDL_BUTTON_LEFT:
                        x1 = event.motion.x;
                        y1 = event.motion.y;
                        x2 = event.motion.x;
                        y2 = event.motion.y;
                        drawing = true;
                        break;
                }
                break;
            case SDL_MOUSEBUTTONUP:
                switch (event.button.button)
                {
                    case SDL_BUTTON_LEFT:
                        drawing = false;
                        break;
                }
                break;

An additional event handler, this time for mouse movement, updates the value of (x2, y2) while the left mouse button is pressed, giving a feel that the line is being dragged:

            case SDL_MOUSEMOTION:
                if (drawing)
                {
                    x2 = event.motion.x;
                    y2 = event.motion.y;
                }
                break;

We can now add code to actually draw the line, before the call to SDL_RenderPresent():

        // draw current line

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        if (drawing)
            SDL_RenderDrawLine(renderer, x1, y1, x2, y2);

Great, we now can draw lines by left-clicking and dragging the mouse:

…but the lines disappear whenever the left mouse button is released!

In order to keep the drawn lines in the window, we’ll need to store them, and draw them on each iteration of the game loop. We first need to declare a struct that represents each line we draw:

struct Line
{
    int x1;
    int y1;
    int x2;
    int y2;
};

We then need a simple data structure to store the lines, such as a list from the C++ standard library:

#include <list>

Let’s declare a variable for the list that will hold our lines:

std::list<Line> lines;

Then, we’ll add some code so that when the left mouse button is released, the new line is added to the list:

            case SDL_MOUSEBUTTONUP:
                switch (event.button.button)
                {
                    case SDL_BUTTON_LEFT:
                        drawing = false;
                        Line line = { x1, y1, x2, y2 };
                        lines.push_back(line);
                        break;
                }
                break;

Finally, before the code that draws the new line, let’s add some code that draws all the old ones:

        // draw stored lines

        SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);

        for (std::list<Line>::const_iterator i = lines.begin(); i != lines.end(); ++i)
        {
            Line line = *i;
            SDL_RenderDrawLine(renderer, line.x1, line.y1, line.x2, line.y2);
        }

Now, all the lines you draw will remain in the window:

SDL2 Bounding Box Collision Detection

One of the most basic and fundamental pieces of logic in a game is to check whether an object hit another object. This is known as collision detection. You can use it to check whether a bullet hit the player or an enemy, or to see if the player bumped into an impassable barrier, etc.

The source code for this article is available at the Gigi Labs BitBucket repository.

Although objects in a 2D game may have various shapes, it’s usually easiest to check for collisions by assuming a rectangular boundary (i.e. a bounding box) around each object, and testing those rectangles for intersections. This is both easy to write and efficient to compute, and although it may not be 100% faithful to the shape of the object, in most cases it’s good enough.

To see how this works, we’ll draw two rectangles, and test whether they intersect. We’ll start off with the following code, which is quite similar to what we had in earlier SDL2 articles here:

    // variables

    bool quit = false;
    SDL_Event event;
    int x = 288;
    int y = 208;

    // init SDL

    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window * window = SDL_CreateWindow("SDL2 Bounding Box Collision Detection",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
    SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);

    SDL_Rect rect1 = { x, y, 100, 100 };
    SDL_Rect rect2 = { 50, 50, 100, 80 };

    // handle events

    while (!quit)
    {
        SDL_Delay(10);
        SDL_PollEvent(&event);

        switch (event.type)
        {
            case SDL_QUIT:
                quit = true;
                break;
            // TODO keyboard input handling goes here
        }

        // TODO rendering & collision detection goes here
    }

    // cleanup SDL

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;

This doesn’t do anything other than render an empty window. We can draw a couple of rectangles by using SDL_RenderFillRect(). Add the following code in the place of the rendering and collision detection comment:

        SDL_SetRenderDrawColor(renderer, 242, 242, 242, 255);
        SDL_RenderClear(renderer);

        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
        SDL_RenderFillRect(renderer, &rect1);

        SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
        SDL_RenderFillRect(renderer, &rect2);

        SDL_RenderPresent(renderer);

First we’re clearing the window to a light grayish colour with SDL_RenderClear(), and then we’re drawing two rectangles (one blue, one green) using SDL_RenderFillRect(). Before each of these calls, we must set the drawing colour using SDL_SetRenderDrawColor(). Finally, SDL_RenderPresent() will render everything to the window.

Now we have two rectangles:

Adding some simple keyboard handling code (as per “Handling Keyboard and Mouse Events in SDL2“) will allow us to move the green rectangle with the arrow keys on the keyboard:

        switch (event.type)
        {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                switch (event.key.keysym.sym)
                {
                    case SDLK_LEFT:  rect1.x--; break;
                    case SDLK_RIGHT: rect1.x++; break;
                    case SDLK_UP:    rect1.y--; break;
                    case SDLK_DOWN:  rect1.y++; break;
                }
                break;
        }

Now we can work on the actual collision detection: if you move the green rectangle such that it hits the blue one, we’ll change the colour of both to red. We could write the code to check whether the rectangles overlap (e.g. as in Lazy Foo’s collision detection tutorial), but why bother? SDL2 gives us a handy SDL_HasIntersection() function that we can use out of the box. Our rendering and collision detection code changes thus:

        SDL_bool collision = SDL_HasIntersection(&rect1, &rect2);

        SDL_SetRenderDrawColor(renderer, 242, 242, 242, 255);
        SDL_RenderClear(renderer);

        if (collision)
            SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        else
            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
        SDL_RenderFillRect(renderer, &rect1);

        if (collision)
            SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        else
            SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
        SDL_RenderFillRect(renderer, &rect2);

        SDL_RenderPresent(renderer);

This code simply decides what colour to draw for each rectangle based on whether a collision occurred. Sure enough, when you drive the green rectangle into the blue one, both change to red:

Sweet! In this article, we learned how to draw rectangles and check whether they intersect using functions that SDL2 provides. Rectangle intersection is a simple way of checking for collision detection between the bounding boxes of objects in a 2D game. This is a simple and fast approach that works well enough for most situations. Sometimes though, you may need to use more appropriate geometry (e.g. a bounding circle works best for a ball).

"You don't learn to walk by following rules. You learn by doing, and by falling over." — Richard Branson