bingo-sim-0.0.5.0: A small playground to learn about profiling Haskell.

Safe HaskellNone
LanguageHaskell2010

BingoSim.Simulation

Contents

Description

Simulates the likelyhood of winning a children's carnival bingo game.

The game itself works like this:

  1. There's a 6 x 6 grid, each with it's own special character identifying it.
  2. There are 36 tiles, one for each grid space.
  3. Initially, all tiles are face down.
  4. To play, a contestant chooses 15 of the 36 tiles and flips them over.
  5. The contestant places the flipped tiles onto the correct spots.
  6. If placing the 15 tiles forms a bingo in any row, column, or full diagonal, it's a win. Otherwise, it's a loss.

Our question is: if one of our friends wins this game, how lucky should they consider themself? Rather than compute the probability exactly, here we run a simulation to approximate the exact probability of a win.

To represent a bingo board and the operations on them, we've created the Board type, which is a bit vector representing the grid in row major order where a 1 means that a tile was placed on that grid space. There is also a hasBingo helper to figure out whether a Board has a bingo.

This module has the logic to actually carry out the simulation: runSimulation

(Note: This module is basically the Main module, except I couldn't figure out how to generate Haddock documentation for an executable target, not a library target.)

Synopsis

Documentation

runSimulation Source #

Arguments

:: Int

trials: The number of trials to run.

-> IO () 

Run the entire simulation, consisting of trials trials.

Prints the results to stdout when done.

>>> runSimulation 100000
Trials:   100000
Bingos:   3615
Hit rate: 0.03615

This function is called directly by the bingo-sim executable's main method, so you can get the same effect by running bingo-sim at the command line, instead of the Haskell REPL:

❯ bingo-sim 100000
Trials:   100000
Bingos:   3615
Hit rate: 0.03615

Simulation helpers

randomBoard :: State -> IO (Board, State) Source #

Generate a random board.

Uses a somewhat contrived strategy:

  1. Start with a bit sequence with fifteen 1's (0x7fff).
  2. Use Fisher-Yates to shuffle the individual bits among the lower 36 bits of the sequence.

This is much faster than the naive strategy of:

  1. Generate the numbers 0 to 35 in a list and shuffle them.
  2. Take the first 15, to represent picking 15 random tiles.
  3. Flip on the bits corresponding to each tile we picked.

The Fisher-Yates on bits approach is faster because we don't have to generate a linked list of thunks and instead can operate on a single 64-bit word.

The sacrifice is that the naive strategy nearly exactly matches our intuition for how this game works in the real world.

shuffleBits Source #

Arguments

:: State 
-> Board 
-> Int

n: The current bit we're considering swapping or leaving alone (1-indexed).

-> (Board, State) 

Implements Fisher-Yates at the bit level for a Board.

Uses recursion to swap the current bit into place, from most to least significant.

swapBits Source #

Arguments

:: Word64

Input bits

-> Int

i: Index of one bit to swap

-> Int

j: Index of the other bit to swap

-> Word64

Swapped result

Helper for swapping two specific bits.

Graciously taken from Sean Eron Anderson's Bit Twiddling Hacks, specialized to the case of a length 1 range of bits.