Over the past week or so I’ve been a little too obsessed with Cookie Clicker. It’s an awesome little JavaScript-based game and I can’t just leave it alone.

I’m at a point in the game where it takes hours before I can buy the next item I’m looking for. Just out of curiousity, I was doing the math to figure out exactly how long it would take at my current rate to hit certain milestones. Since I was doing that so much, I decided to build a little user script I could install that would do the math for me and keep a running countdown going.

The script is available for download but I’m going to break it down a bit (in the spirit of those code samples I’ve talked so much about over the last couple days) here.

// ==UserScript== // @name CookieClicker Target Countdown // @version 1.0.0 // @namespace http://www.clarkrasmussen.com/ // @description Enter a target amount of banked cookies and see the amount of time it will take to reach that amount at your current CPS // @include http://orteil.dashnet.org/cookieclicker/* // // ==/UserScript== const VERSION = &quot;1.0.0&quot;; window.addEventListener('load', function() { exec(function () {

We start with some pretty standard header stuff. On page load we run the *exec()* function, passing into it a function that wraps all of the stuff to continue. The *exec()* function is last in my code so I’ll get to it near the bottom of this.

// write out our elements var output_div = document.createElement('div'); output_div.setAttribute('id', 'cookieclicker_countdown_container'); output_div.setAttribute('style', 'position: fixed; right: 30px; bottom: 20px; margin: 0; padding: 7px 15px; border: 1px solid #999; background-color: #ccc; color: #000;'); var countdown_target_label = document.createElement('label'); countdown_target_label.setAttribute('style', 'display: block; padding-bottom: .25em; font-size: 80%; font-weight: bold;'); countdown_target_label.setAttribute('for', 'countdown_target_input'); countdown_target_label.innerHTML = 'Target Cookie Value:'; var countdown_target_input = document.createElement('input'); countdown_target_input.setAttribute('style', 'display: block; font-size: 90%;'); countdown_target_input.setAttribute('id', 'countdown_target_input'); countdown_target_input.setAttribute('value', '0'); var output_content = document.createElement('p'); output_content.setAttribute('style', 'padding-top: .5em; text-align: center;'); output_content.innerHTML = '00:00'; output_div.appendChild(countdown_target_label); output_div.appendChild(countdown_target_input); output_div.appendChild(output_content); document.body.appendChild(output_div);

Next we define all of the HTML elements we need to actually display our output and we append them to the page. This includes a form element which we’ll use to define our target value.

// do the check every second setInterval(function () { countdown_check(); }, 1000);

Simple enough, run the *countdown_check()* function once a second.

function calculate_countdown (countdown_target) { var cookies = Game.cookies; var cookiesps = Game.cookiesPs; var seconds = 0; var time = ''; if (countdown_target &gt; cookies) { if (cookiesps &gt; 0) seconds = (countdown_target - cookies) / cookiesps; var days = Math.floor(seconds / 86400); var hours = Math.floor((seconds % 86400) / 3600); var minutes = Math.floor(((seconds % 86400) % 3600) / 60); seconds = Math.round(((seconds % 86400) % 3600) % 60); if (days &gt; 0) { time = days + ':'; if (hours &gt; 0) { time += zero_pad(hours, 2); } else { time += '00'; } time += ':'; } else if (hours &gt; 0) { time += hours + ':'; } time += zero_pad(minutes, 2) + ':' + zero_pad(seconds, 2); } else { time = '00:00'; } return time; }

In the *calculate_countdown()* function we get the actual time remaining to the target. We pass in the target value, then grab the amount of cookies the user has and their cookies per second from the *Game* object so we don’t have to get it out of any HTML elements. It’s already readily available, why not take advantage? Then we do the math to determine how many cookies we need to hit the target and how many seconds it will take to get there.

If the number of seconds is greater than zero, we do some math to break the time into days, hours, minutes and seconds. Then we reassemble that into a string. If the time has already passed, we return *00:00*.

The *zero_pad()* function seen implemented here is defined below.

function countdown_check () { var countdown_target = parseInt(document.getElementById('countdown_target_input').value); if (countdown_target &gt; 0) { var time = calculate_countdown(countdown_target); output_content.innerHTML = time; } }

Here’s that *countdown_check()* function. It grabs the countdown target from the form element and, if it’s greater than zero, throws it off to *calculate_countdown()* to get the time. The time returned is then put into the *output_content* container.

function zero_pad (num, length) { var s = num + ''; while (s.length &lt; length) s = '0' + s; return s; } }); }); function exec (fn) { var script = document.createElement('script'); script.setAttribute('type', 'application/javascript'); script.textContent = '(' + fn + ')();'; document.body.appendChild(script); document.body.removeChild(script); }

Last we define *zero_pad()* and close out the containers, as this has all been wrapped in *AddEventListener()* and *exec()*. The *exec()* function is then defined as what allows us to do this all via script injection, which is what gives us access to that helpful *Game* object.

Hardly anything groundbreaking, in fact I’d bet someone else has done this before. Just another one of those things I thought I’d share.

For the record, this is what the countdown looks like on the page. Small enough to not interfere with a golden (or red) cookie appearing in the same spot.

**Update – 10:20 PM, 10/1/2013:** After some quick feedback I made two changes. One extracted the time formatting logic into its own function, the aptly-named *format_time()*, while the other fixed a bug by moving the rounding in *calculate_countdown()*. Those two functions now look as follows:

function calculate_countdown (countdown_target) { var cookies = Game.cookies; var cookiesps = Game.cookiesPs; var seconds = 0; var time = ''; if (countdown_target &gt; cookies) { if (cookiesps &gt; 0) seconds = Math.round((countdown_target - cookies) / cookiesps); var days = Math.floor(seconds / 86400); var hours = Math.floor((seconds % 86400) / 3600); var minutes = Math.floor(((seconds % 86400) % 3600) / 60); seconds = ((seconds % 86400) % 3600) % 60; time = format_time(days, hours, minutes, seconds); } else { time = '00:00'; } return time; } function format_time (days, hours, minutes, seconds) { var time = ''; if (days &gt; 0) { time = days + ':'; if (hours &gt; 0) { time += zero_pad(hours, 2); } else { time += '00'; } time += ':'; } else if (hours &gt; 0) { time += hours + ':'; } return (time + zero_pad(minutes, 2) + ':' + zero_pad(seconds, 2)); }