## The Lucky Numbers Game and JS Simulations

Lately I’ve become low-key obsessed with The Lucky Numbers Game. You have a grid of numbers, ranging from 1 to 100, and “behind” one of those numbers is a coveted prize. The catch is the winning number is randomly assigned, and each guess costs as much as the number you’re selecting. So guessing 13 will cost you \$13 (or chips, tokens, credits, what have you), and guessing 72 will cost 72.

The goal is to discover the winning lucky number in as few guesses/lowest cost as possible.

It’s a math puzzle? (sigh) “I’m in, you son of a bitch.”

There are three obvious approaches to take when confronting a lucky number grid:

1. start at 1, and systematically work your way towards 100
2. start at 100, and systematically work your way towards 1
3. make 100 random guesses, hoping to get lucky and pick it earlier rather than later

So which approach helps us find the lucky number fastest? That’s where the JavaScript comes in. We can build a lo-fi version of the Lucky Number game fairly quickly, and test each approach thousands of times to figure out what the average number of guesses and costs for each approach is.

``````// gets a random number between min and max, inclusive. Source: Mozilla Developer Network
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min);
}

// find lucky number, going from 1 to 100, in sequence
function runLowToHighTrial(){

let guessHistory = [];
let guessTokens = 0;
let results = {};

// randomly assign our lucky number
let luckyNumber = getRandomIntInclusive(1,100);

// make guesses from 1 to 100, until lucky number is found
for (let x = 1; x <= 100; x++) {
thisGuess = x;
guessTokens = guessTokens + thisGuess;

if (thisGuess == luckyNumber){
break;
} else {
guessHistory.push(thisGuess);
}
}

results.luckyNumber = luckyNumber;
results.guessHistory = guessHistory;
results.guessTokens = guessTokens;

return results;
}

// find lucky number, going from 100 to 1, in sequence
function runHighToLowTrial(){

let guessHistory = [];
let guessTokens = 0;
let results = {};

// randomly assign our lucky number
let luckyNumber = getRandomIntInclusive(1,100);

// make guesses from 100 to 1, until lucky number is found
for (let x = 100; x >= 1; x--) {
thisGuess = x;
guessTokens = guessTokens + thisGuess;

if (thisGuess == luckyNumber){
break;
} else {
guessHistory.push(thisGuess);
}
}

results.luckyNumber = luckyNumber;
results.guessHistory = guessHistory;
results.guessTokens = guessTokens;

return results;
}

// find lucky number, random guesses, remember/ignore previous guesses
function runRandomTrial(){

let guessHistory = [];
let guessTokens = 0;
let results = {};

// randomly assign our lucky number
let luckyNumber = getRandomIntInclusive(1,100);

// make one hundred random guesses, no duplicates
for (let x = 1; x <= 100; x++) {
do{
thisGuess = getRandomIntInclusive(1,100);
} while ( guessHistory.indexOf(thisGuess) > -1 );

guessTokens = guessTokens + thisGuess;

if (thisGuess == luckyNumber){
break;
} else {
guessHistory.push(thisGuess);
}
}

results.luckyNumber = luckyNumber;
results.guessHistory = guessHistory;
results.guessTokens = guessTokens;

return results;
}

// calculate the average number of guesses and costs for each trial/approach
function getAverages(arrTrials){

let totalGuesses = 0;
let totalTokens = 0;
let results = {};

arrTrials.forEach( function(trial){
totalGuesses = totalGuesses + trial.guessHistory.length;
totalTokens = totalTokens + trial.guessTokens;
});

results.avgGuesses = totalGuesses / arrTrials.length;
results.avgTokens = totalTokens / arrTrials.length;

return results;
}

// we need to store our trial results
let lowToHighTrials = [];
let highToLowTrials = [];
let randomTrials = [];

// run 100K trials of each kind, storing results for averaging later
for (let a = 1; a <= 100000; a++){
lowToHighTrials.push( runLowToHighTrial() );
highToLowTrials.push( runHighToLowTrial() );
randomTrials.push( runRandomTrial() );
}

// calculate averages, show results
console.log("Low To High Trials:");
console.log( getAverages(lowToHighTrials) );

console.log("High To Low Trials:");
console.log( getAverages(highToLowTrials) );

console.log("Random Trials:");
console.log( getAverages(randomTrials) );``````

I decided to run 100,000 trials of each approach, and this is what I found:

Low To High Trials: { average Guesses: 49.57338, average Cost: 1721.8413 }

High To Low Trials: { average Guesses: 49.41141, average Cost: 3379.74295 }

Random Trials: { average Guesses: 49.48288, average Cost: 2549.10839 }

Although no approach has an edge over the others in the fewest number of guesses, there’s definitely a difference in cost between them!

To be fair, we’re testing each approach in isolation– as if the Lucky Number game has only a single player. In reality, the Lucky Number game can have multiple simultaneous players, each competing to uncover the winning number before another player does. Players seem to start off with the Low To High approach initially, but change over to the Random Pick approach, possibly succumbing to the pressure of “What if one of my competitors finds the winning number in a higher range while I’m wasting my time down here in the lower ranges?”

It might be interesting to modify the JavaScript to have multiple players searching for the lucky number using the different approaches, to see how they fare against each other in direct competition. But not right now, I need to eat breakfast.

## Jack Has Problems With His Dates

A long time ago, Jack needed a function to return a JavaScript date object which represented New Year’s Day for any year which he specified, or for the current year if none was provided.

This is what he came up with:

``````function getNewYearsDate(yr){

var nyDate = new Date("");

if ( typeof yr === "undefined"){
var tmpDate = new Date();
yr = tmpDate.getFullYear();
}

nyDate.setMonth(0);
nyDate.setDate(1);
nyDate.setYear(yr);

return nyDate;
}``````

Of course, Jack tested his function carefully . . .

``````getNewYearsDate(2020);
// returns Wed Jan 01 2020 00:00:00 GMT-0500 (Eastern Standard Time)
getNewYearsDate();
// returns Tue Jan 01 2019 00:00:00 GMT-0500 (Eastern Standard Time)``````

and seeing the results he expected to see, Jack pushed this change to production where it worked wonderfully for many months.

This morning, Jack identified a need for a similar function which represents July 4th/Independence Day. Because Jack is in a rush, he decides to copy and alter his getNewYearsDate function to handle returning the date for Independence Day in the United States (July 4th).

Here’s what he did:

``````function getIndependenceDate(yr){

var idDate = new Date("");

if ( typeof yr === "undefined"){
var tmpDate = new Date();
yr = tmpDate.getFullYear();
}

idDate.setMonth(6);
idDate.setDate(4);
idDate.setYear(yr);

return idDate;
}``````

But this time, when he tests his new function, he gets surprising results!

``````getIndependenceDate()
Tue Jan 01 2019 00:00:00 GMT-0500 (Eastern Standard Time)
getIndependenceDate(2020)
Wed Jan 01 2020 00:00:00 GMT-0500 (Eastern Standard Time)``````

Can you see why the getNewYearsDate() function works, while the getIndependenceDate() function fails?

If so, how would you alter the getIndependenceDate() function so it returns the correct value?

## “Make Me Feel Like A Wizard” Isn’t A Feature. Or Is It?

There’s something about typing in a trigger word into your code editor, hitting the Tab key, and seeing it suddenly transformed into a custom function, class, object, or whatever which you’ve created– it feels magical.

I know it’s not truly magical, obviously. But it feels like it.

For context, I’m mentioning this because I’m in the middle of migrating from the 32-bit version of my coding editor to the 64-bit version. The plugin I currently use to manage my reusable code snippets is only available in a 32-bit DLL, it hasn’t been updated in nearly 6 years, etc. There is a different code snippet plugin available for the 64-bit version, it’s actively developed and maintained, has great documentation, arguably better UX/UI, and does everything the previous plugin does . . . except for the trigger word/Tab key transformation.

The analytical part of my brain says this is a no-brainer: “Migrate your code snippets to the new plugin.”

The creative side of me, however, is like: “But it’s missing THE coolest feature! Can’t we download the plugin source from GitHub and re-compile it in Visual Studio as a 64-bit DLL, or make a wrapper or something?”

The silly part is, no one actually watches me write code. There’s no “wow factor” or whatever you want to call it. If you stop and think about it, the whole trigger word/Tab transformation scheme doesn’t even scale well– because every snippet you add requires you to remember a new trigger word. And if you try to export and share your snippets with someone else, they won’t have memorized your trigger words. It’s impractical.

But typing in “errorhandler”, hitting the Tab key, and suddenly seeing your text replaced with the method to invoke your organization’s error catching and logging object– that’s some Doctor Strange meets Jeffrey Zeldman in Area 51 kinda mojo right there. Using a category scheme with a dropdown UI to ultimately pick the “errorhandler” function from a displayed list of matching functions, just isn’t as satisfying.

I think the secret behind this feature’s irrational appeal is “It makes one feel like a wizard.”  You type in the secret word, press the magic button, and (voila!) something cool happens. It creates an empowering feeling, and that’s a big piece of “locking people in” to your product.  (Seriously, the fact that I’m even considering trying to recompile a plugin which hasn’t been updated in 6 years as a 64-bit DLL shows you how ridiculous this is.)