Created
August 24, 2010 02:00
-
-
Save sbenhaim/546755 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns bike-bingo | |
(:require | |
;; We'll use cl-format to draw the board. | |
[clojure.contrib.pprint :as pp])) | |
;; Here's our 5x5 bingo board. We're counting horizontal, vertical, | |
;; diagonal, four corners, and blackout. The numbers in each square | |
;; represent how many bingos that square is involved in (excluding | |
;; blackout). | |
;; | |
;; It's been predetermined that the middle square, [2 2], will be "Ride | |
;; more than 1 mile", so I'm leaving it out. | |
;; | |
;; | 0 | 1 | 2 | 3 | 4 | | |
;; ---+---+---+---+---+---| | |
;; 0 | 4 | 2 | 2 | 2 | 4 | | |
;; ---+---+---+---+---+---| | |
;; 1 | 2 | 3 | 2 | 3 | 2 | | |
;; ---+---+---+---+---+---| | |
;; 2 | 2 | 2 | x | 2 | 2 | | |
;; ---+---+---+---+---+---| | |
;; 3 | 2 | 3 | 2 | 3 | 2 | | |
;; ---+---+---+---+---+---| | |
;; 4 | 4 | 2 | 2 | 2 | 4 | | |
;; +---+---+---+---+---| | |
;; | |
;; Given the illustration above, we're going to fill the most valuable | |
;; slots first using the most difficult tiles. Here's our board | |
;; filling order: | |
(def *board-priority* | |
[[0 0] [0 4] [4 0] [4 4] | |
[1 1] [1 3] [3 1] [3 3] | |
[0 1] [0 2] [0 3] [1 0] | |
[1 2] [1 4] [2 0] [2 1] | |
[2 3] [2 4] [3 0] [3 2] | |
[3 4] [4 1] [4 2] [4 3]]) | |
;; To determine which tiles are the hardest, we'll take a look at how | |
;; many people earned them. Not perfect, but it'll do. Here's a map of | |
;; tiles to times completed. | |
(def *freq-map* | |
{"Ride more than 10 miles" 7 | |
"Ride more than 20 miles" 3 | |
"Ride more than 30 miles" 3 | |
"Ride more than 40 miles" 2 | |
"Ride more than 50 miles" 4 | |
"Ride more than 100 miles" 1 | |
"Ride between 12:00AM and 5:00AM" 2 | |
"Ride in inclement weather" 5 | |
"Get yelled at by a stranger" 5 | |
"Hit 30mph (downhill OK)" 8 | |
"Ride to work every day one week" 5 | |
"Go mountain biking" 2 | |
"Bike home from the bar" 5 | |
"Bike up Summit (Ramsey) hill" 1 | |
"Bike on the Greenway" 5 | |
"Ride the Minneapolis Grand Rounds" 2 | |
"Ride from DT Minneapolis to DT St. Paul or vice versa" 1 | |
"Ride outside the 494-694 loop" 5 | |
"Follow all traffic laws (yes, even stop signs)" 8 | |
"Get a flat tire" 5 | |
"Change a flat tire" 3 | |
"Get honked at by a stranger" 5 | |
"Bike in a group of 5 or more people" 4 | |
"Pass someone on a Segway" 2 | |
"Pass someone on a tall bike" 2 | |
"Participate in a public bike event" 4 | |
"Watch a public bike event" 1 | |
"Track stand for 30 seconds" 2}) | |
;; Here's a simple HTML board template with some `cl-format' | |
;; directives in it. `~{' and `~}' start and end loops, `~a' is | |
;; replaced with some value. (Todo: It might be interesting to use | |
;; Enlive for this instead (http://github.com/cgrand/enlive).) | |
(def *template* | |
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"> | |
<head> | |
<style type=\"text/css\" media=\"screen\"> | |
td { | |
border: 2px solid black; | |
padding: 10px; | |
width: 150px; | |
height: 150px; | |
text-align: center; | |
font-weight: bold; | |
} | |
</style> | |
</head> | |
<body> | |
<table> | |
~{<tr> | |
~{<td>~a</td> | |
~} | |
</tr> | |
~} | |
</table> | |
</body> | |
</html> | |
") | |
;; Each tile will be selected at random, but I'd really like to give | |
;; the hardest tiles a good chance to hit the most valuable | |
;; squares. This function will transform their weight into the number | |
;; of entries they'll be getting in the hat. | |
;; | |
;; If someone has a more mathematically clever solution for this | |
;; transformation, I'm all ears. | |
(defn weight-transform [x] | |
(* x x x)) | |
;; Weight is inversely proportional to frequency from our freq-map, so | |
;; we subtract the times seen from 1 + most frequent to determine | |
;; it. | |
;; | |
;; Though, would it make more sense to make this an inverse of the | |
;; actually frequency divided by the number of people in the | |
;; contest (greatest possible frequency)? | |
(defn freq-to-weight [freq-map] | |
(let [biggest (inc (apply max (vals freq-map)))] | |
(apply hash-map (flatten | |
(for [pair freq-map] (list (pair 0) (- biggest (pair 1)))))))) | |
;; Given the weight map and our transforming function, fill the hat | |
;; with an appropriate number of entries for each tile. | |
(defn fill-hat [weight-map weight-transform] | |
(flatten (for [pair weight-map] (repeat (weight-transform (pair 1)) (pair 0))))) | |
;; Now we build up our board in order of priority. | |
(defn build-board [hat priority] | |
(loop [board {} hat hat priority priority] | |
;; Once we've filled all our tiles from the priority list, we'll | |
;; place "Bike more than 1 mile" at the center. | |
(if (= (count priority) 0) | |
(assoc board [2 2] "Bike more than 1 mile") | |
;; Pull a random item from the hat. More heavily rated items | |
;; have more entries in the hat and are more likely to be drawn. | |
(let [value (nth hat (rand-int (count hat)))] | |
(recur | |
;; Place the tile on the board. | |
(assoc board (first priority) value) | |
;; Remove all entries for that tile from the hat. (Good thing | |
;; this isn't a real hat.) | |
(remove #(= % value) hat) | |
;; Continue with the remaining priority list. | |
(rest priority)))))) | |
;; `cl-format' will really like our board if it's nested vectors. Hash | |
;; table, not so much. | |
(defn board-to-nested-vector [board] | |
(vec (for [row (range 5)] | |
(vec (for [col (range 5)] | |
(board [row col])))))) | |
;; A simple bit of business | |
(defn print-board [board template] | |
(pp/cl-format true template (board-to-nested-vector board))) | |
;; And finally, we do the work. | |
(-> *freq-map* | |
(freq-to-weight) | |
(fill-hat weight-transform) | |
(build-board *board-priority*) | |
(print-board *template*)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment