Categories
Uncategorized

How to program: How does DidThanosKill.me work?

Shortly after the release of Avengers: Infinity War, the site DidThanosKill.me appeared, and many of my friends visited it and shared their results. There were two possible outcomes:

  • You were spared by Thanos.
  • You were slain by Thanos, for the good of the Universe.

If you tried to reload the page to get a different result, you quickly learned that you can’t cheat fate. Once you got a result from DidThanosKill.me, it stayed that way, no matter how many times you reloaded the page.

If you’re new to programming, or if you’ve hit that point where you can follow a programming tutorial but are having trouble taking an idea and turning it into an application, this article is part of an ongoing series that will help you.

Nerd interlude (or: a little background for people who didn’t read comics in the ’90s)

The eradication of half the life in the universe happened (both in the 1991 comic book The Infinity Gauntlet and the movie, which borrows bits and pieces from the comic book) when Thanos gathered the Infinity Gems/Stones (in the comics, they’re Gems, and they were originally called the Soul Gems; in the movies, they’re Stones)…

…into the Infinity Gauntlet, giving him near-unlimited power. In the comics, Thanos did this to impress and win the love of Death — the cosmic entity, personification of death, and Thanos’ dream woman:

The “to challenge humans is to court death” post-credits scene from the first Avengers movie gave the non-comic-book-reading audience its first glimpse of Thanos and hinted at the possibility that they might go with the same storyline…

…but instead, they went with a more mundane, Malthusian motivation for Thanos: bringing balance to the universe, and population control. It’s a pity, because the Marvel Cinematic Universe had already introduced a great death goddess character who would’ve worked wonderfully with the “Thanos is in love with Death” idea:

How DidThanosKill.me works: The high-level version

In both the movies and the comics, when Thanos finally gets all the gems/stones in the Gauntlet, he uses its power to kill off half the life in the universe by random selection. The word random should be your hint that a random number generator is involved.

The other thing that DidThanosKill.me does is remember whether or not Thanos killed you. When a web app remembers something about a particular visitor without requiring that visitor to log in first, that’s your hint that it’s probably making use of a cookie.

How DidThanosKill.me works: Looking at the actual code

Let’s take a look at DidThanosKill.me’s source. This is the complete source for that page, which you can see when you use your browser’s View Source functionality on DidThanosKill.me:

<!DOCTYPE html>
<html>
    <head>
        <title>Did Thanos Kill You?</title>
        <meta name="description" content="Did Thanos kill you?">
        <meta name="keywords" content="did,Thanos,kill,me,you,avengers,infinity,war,half,fifty,50,percent,snap">
        <meta name="author" content="Tristan Bellman-Greenwood">
        <script>
            function getCookie(cname) {
                var name = cname + "=";
                var decodedCookie = decodeURIComponent(document.cookie);
                var ca = decodedCookie.split(';');
                for(var i = 0; i <ca.length; i++) {
                    var c = ca[i];
                    while (c.charAt(0) == ' ') {
                        c = c.substring(1);
                    }
                    if (c.indexOf(name) == 0) {
                        return c.substring(name.length, c.length);
                    }
                }
                return "";
            }
            
            function onLoad() {
                var displayElement = document.getElementById("display");
                
                var randomNumber = getCookie("thanosNumber");
                
                if (!randomNumber) {
                    randomNumber = Math.random();
                    document.cookie = "thanosNumber=" + randomNumber + "; expires=Fri, 3 May 2019 00:00:00 UTC";
                } else {
                    randomNumber = Number(randomNumber);
                }
                
                if (randomNumber < 0.5) {
                    displayElement.textContent = "You were slain by Thanos, for the good of the Universe.";
                } else {
                    displayElement.textContent = "You were spared by Thanos.";
                }
            }
            
            function clearCookie() {
                document.cookie = "thanosNumber=; expires=Thu, 01 Jan 1970 00:00:00 UTC";
                location.reload();
            }
        </script>
    </head>
    <body onload="onLoad()">
        <div style="width: 100%; height: 100%;">
            <span id="display" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); font-family: sans-serif; font-size: 4em; text-align: center;"></span>
            <!-- <button onclick="clearCookie()">Clear Cookie</button> -->
        </div>
    </body>
</html>

Making the code run as soon as the page is done loading

On line 50, you’ll find the body tag:

<body onload="onLoad()">

This is an often-used technique to make something happen when the page loads:

  • The onload event occurs when the object it belongs to has fully loaded.
  • In the case of the body tag, the onload event happens when the body of the web page has fully loaded, which means that it has loaded all content, including images, scripts, CSS, and so on.
  • With this particular body tag, once the web page has fully loaded, it executes the onLoad() method.

The method where everything happens

The onLoad() method lives in the script tag (it starts at line 25) and looks like the code below, with one key exception: I’ve added a couple of numbers at the end of key lines, which correspond to the items below the code.

function onLoad() {
    var displayElement = document.getElementById("display");  // 1
    
    var randomNumber = getCookie("thanosNumber");             // 2
    
    if (!randomNumber) {                                      // 3
        randomNumber = Math.random();
        document.cookie = "thanosNumber=" + randomNumber + "; expires=Fri, 3 May 2019 00:00:00 UTC";
    } else {                                                  // 4
        randomNumber = Number(randomNumber);
    }
    
    if (randomNumber < 0.5) {                                 // 5
        displayElement.textContent = "You were slain by Thanos, for the good of the Universe.";
    } else {                                                  
        displayElement.textContent = "You were spared by Thanos.";
    }
}

1. It gets the HTML element that will display the “You were slain” or “You were spared” message.

It does this by getting the element whose id is display and assigns it to the variable displayElement.

If you look at line 52 of the source, you’ll see that this element is a span that is:

  • Set in the center of the screen (as determined by position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); in the style attribute)
  • Has a large font size (as determined by font-family: sans-serif; font-size: 4em; in the style attribute)
  • Is center-aligned (as determined by text-align: center; in the style attribute)

The span will eventually contain the text that will display either “You were slain by Thanos, for the good of the Universe”, or “You were spared by Thanos.”

2. It checks to see if the user’s fate was already determined and memorized during a previous visit.

This is done by calling a method named getCookie(), which tries to get the value of the cookie with the given key. In this case, the key is thanosNumber. A cookie with the key thanosNumber would exist on the user’s system only if the user had visited the site before and didn’t clear the browser’s cookies since then.

  • If user’s system contains a cookie with the key thanosNumber, getCookie("thanosNumber") returns the value associated with that key, and that value gets put into the variable randomNumber.
  • If the user has never visited DidThanosKill.me before (or cleared their cookies since their last visit), the user’s system not contain a cookie with the key thanosNumber. The call to getCookie("thanosNumber") would result in a null value, which would then be put into the variable randomNumber.

At this point, randomNumber contains either nullor a value.

3. If  the user’s fate hasn’t already been determined and memorized, it determines and memorizes the user’s fate.

If randomNumber contains null, the following happens:

  • A random number between 0 and up to (but not including) 1 is generated and stored in randomNumber. This number represents the user’s fate.
  • The value in randomNumber is saved to the user’s system in a cookie with the keythanosNumber.

4. If  the user’s fate has already been determined and memorized, it recalls the user’s fate.

The value in randomNumber is converted to its numerical equivalent.

5. It displays the user’s fate.

At this point, as a result of either step 3 or step 4, randomNumber is guaranteed to contain a number between 0 and up to (but not including) 1.

  • If the value in randomNumber is less than 0.5, the user was slain by Thanos, and this fate is displayed onscreen.
  • If the value in randomNumber is greater than 0.5, the user was spared by Thanos, and this fate is displayed onscreen.

And thus half the universe is spared, and half the universe dies.

An exercise for the reader

There are still two bits of the code that I haven’t yet explained, and they’re the ones that read and write the cookie data. The first is the getCookie() function…

  var name = cname + "=";
  var decodedCookie = decodeURIComponent(document.cookie);
  var ca = decodedCookie.split(';');
  for(var i = 0; i <ca.length; i++) {
      var c = ca[i];
      while (c.charAt(0) == ' ') {
          c = c.substring(1);
      }
      if (c.indexOf(name) == 0) {
          return c.substring(name.length, c.length);
      }
  }
  return "";
}

…and the second is clearCookie():

function clearCookie() {
    document.cookie = "thanosNumber=; expires=Thu, 01 Jan 1970 00:00:00 UTC";
    location.reload();
}

See if you can figure them out. I’ll explain how they work in a future article.