by Guy Jacobson
Answer: SWOOSH
Problem: Arbor Day Town/​Bloomsday Town

This puzzle is based on a Bloom filter, an efficient and handy data structure for testing membership in a set quickly using a series of hash functions and a vector of bits.

Solvers can play around with the checker on the web page, but it’s not likely that they will make much progress doing so, because there are only a small number of words that are “allowed” and guessing them will be difficult. We do provide one concrete example of an allowed word in the flavortext.

But since the code implementing the Bloom filter is written up quite clearly in bloomfilter.js, solvers can rip this code out and just feed a large wordlist through it to see what it accepts. Comments in the HTML and Javascript invite solvers to investigate the code. (All the words that are “allowed” are real words, most are pretty familiar, and most should be included on even a modest list of English words.) The easiest way to do this is just use node.js and run the code in bloomfilter.js directly, but solvers who program should be able to reimplement this without too much trouble in the language of their choice if they hate Javascript.

Running a wordlist through the checker will discover the following list of allowed words:

ACETONE
ALADDIN
ALLEGRO
ALTERNATION
AMBASSADOR
ARGO
ARIES
ARROW
BENTLEY
BIT
BLOOM
BOON
BOTSWANA
BRIEFCASE
BROWNIE
CHAKRA
CHORINE
COCKTAIL
COLLATERAL
CORCORAN
CORIOLANUS
CORNEA
DAMNATION
DEADEYE
EASIER
FIREBASE
FLAME
FUTHARK
GALE
GENIE
HASH
HEBE
HOLDOVER
HYDRA
IAGO
INTONATION
JACKKNIFE
JAFAR
JAGUAR
JUNIOR
LAWRENCE
LENS
LOWEST
MALARKEY
MINI
MORGAN
OBLIVION
ODIUM
OKLAHOMA
PORTER
PUPIL
QUEENSIDE
RATES
RETINA
RHINO
RUE
SENIOR
SOLILOQUY
STAGNATION
STARE
SUNDAY
SWAN
TAMARIN
TASER
TEARS
TENFOLD
THRESH
VALKYRIE

(Perhaps the solver’s wordlist will be missing some of these, but it should be possible to fill them in by hand once solvers realize what’s going on.) Here’s where non-coders get to pitch in and help solve! Almost all these words can be organized into groups of four according to some rule, forming a (not necessarily closed) set. There is always a fifth member of this set which was not “allowed” that is the common name of a flower (hey, it’s a bloom filter!). Ordered alphabetically by these missing blooms:

Missing bloom 1 2 3 4 Set
ASTER RATES STARE TASER TEARS Anagrams
BUTTERCUP CORCORAN DEADEYE HEBE PORTER H.M.S. Pinafore characters
CARNATION ALTERNATION DAMNATION INTONATION STAGNATION <word>NATION
DAISY AMBASSADOR BROWNIE JUNIOR SENIOR Girl Scout levels
EDELWEISS ALLEGRO OKLAHOMA SOLILOQUY SUNDAY Rodgers & Hammerstein songs
FREESIA ARIES BRIEFCASE EASIER FIREBASE Transdeletion sequence
GERANIUM ARGO BOON CHORINE ODIUM Elements missing a letter
HIBISCUS BOTSWANA HOLDOVER LAWRENCE MALARKEY Hidden four-letter birds
IRIS CORNEA LENS PUPIL RETINA Parts of the eye
JASMINE ALADDIN GENIE IAGO JAFAR Aladdin characters
KINGCUP ACETONE JACKKNIFE QUEENSIDE TENFOLD <honor card><word>
LOTUS BENTLEY JAGUAR MINI MORGAN British car makes
MAGNOLIA COCKTAIL COLLATERAL OBLIVION VALKYRIE Tom Cruise films
NARCISSUS CHAKRA FUTHARK RHINO TAMARIN Javascript engines
ORCHID ARROW FLAME HYDRA SWAN Dharma Initiative stations
PRIMROSE CORIOLANUS GALE RUE THRESH Hunger Games characters

There are four words left over that do not belong to a set: BIT BLOOM HASH LOWEST. The enumeration below the grid (4 5, 6 3) tells solvers to interpret these leftovers as the instruction “HASH BLOOM, LOWEST BIT.” There are sixteen blooms and forty-three primes used in the hash functions, which is the dimensions of the grid shown below the checker on the web page. The hash function’s two arguments are named “row” and “column,” further reinforcing the idea that the rows of the grid are associated with the blooms, and the columns with the second argument of the hash function (which prime is used as a modulus).

Even though the sixteen blooms are not “allowed,” we can look at the individual results of hashing them using the 43 different primes, and (as suggested by the leftover words instruction) inspect the lowest (least-significant) bits of the result of evaluating the hash function. The rows are the sixteen blooms in alphabetical order (there is one beginning with each letter of the alphabet from A–P), and the columns are the forty-three prime indices in their given order. If we interpret a least significant bit value of 1 as a black pixel and a 0 as a white pixel, we would create the following 16 × 43 image:

This is clearly the Nike “Swoosh” symbol, so SWOOSH is the answer to this puzzle. (Solvers don’t actually need to create an image file/object; it should be clear just using ASCII art.)

Note that even though Bloom filters are probabilistic, this particular one is such that the chance of a false positive is somewhere south of one in a trillion, so it’s super unlikely that anything except the intended “allowed” words will turn up.

Using node.js, here’s how you can easily rip out the bloomfilter.js code from the web site to check a list of words. Install big-integer using npm, and download a list of words into the file /path/to/wordlist. Then run this code using node:

var bigInt = require('big-integer');

// Insert contents of bloomfilter.js here

var filename = '/path/to/wordlist.txt';
      
var fs = require('fs');
var readline = require('readline');

var rd = readline.createInterface({
    input: fs.createReadStream(filename),
    console: false
});

rd.on('line', function(line) {
    if (checkWord(line))
        console.log(line);
});
    

And here is some extra code that you can add to the Javascript to produce the output (as ASCII art) once you have found all the blooms:

const blooms = [
    'ASTER', 'BUTTERCUP', 'CARNATION', 'DAISY', 'EDELWEISS', 'FREESIA',
    'GERANIUM', 'HIBISCUS', 'IRIS', 'JASMINE', 'KINGCUP', 'LOTUS',
    'MAGNOLIA', 'NARCISSUS', 'ORCHID', 'PRIMROSE'
];

for (var i = 0; i < blooms.length; i++) {
    var s = '';
    for (var j = 0; j < primeTable.length; j++)
        s += (hash(blooms[i], j) & 1) ? 'XX' : '  ';
    console.log(s);
}
    

Running this using node outputs:

            XX                                                                    XXXX
          XX                                                                XXXXXX    
        XXXX                                                        XXXXXXXXXX        
      XXXXXX                                                XXXXXXXXXXXXXX            
      XXXX                                          XXXXXXXXXXXXXXXX                  
    XXXXXX                                    XXXXXXXXXXXXXXXXXX                      
    XXXXXXXX                          XXXXXXXXXXXXXXXXXXXXXX                          
  XXXXXXXXXX                  XXXXXXXXXXXXXXXXXXXXXXXXXX                              
  XXXXXXXXXXXXXX      XXXXXXXXXXXXXXXXXXXXXXXXXXXX                                    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                                        
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                                            
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                                                
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                                                      
  XXXXXXXXXXXXXXXXXXXXXXXXXX                                                          
    XXXXXXXXXXXXXXXXXXXX                                                              
      XXXXXXXXXXXX