Created
May 15, 2019 14:05
-
-
Save baronfel/f05b6fc5845bbaeff761a54c92b53e55 to your computer and use it in GitHub Desktop.
Greed Kata
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
(* | |
Greed is a press-your-luck dice rolling game. In the game, the player rolls the dice and tries to earn as many points as possible from the result. | |
For the purposes of this kata, we will just be scoring a single roll of five dice (but see Extra Credit below). | |
Write a scoring method that calculates the best score based on a given roll using the following set of scoring rules. | |
Each die can only be scored once (so single die scores cannot be combined with triple die scores for the same individual die, but for instance four 5s could count as 1 Triple (500) and 1 Single (50) for a total of 550. | |
A single one (100 points) | |
A single five (50 points) | |
Triple ones [1,1,1] (1000 points) | |
Triple twos [2,2,2] (200 points) | |
Triple threes [3,3,3] (300 points) | |
Triple fours [4,4,4] (400 points) | |
Triple fives [5,5,5] (500 points) | |
Triple sixes [6,6,6] (600 points) | |
*) | |
// TODO actual bounds checks :-/ | |
type Die = int | |
type Roll = Die list | |
type Score = int | |
type ScoreRule = { Score: Score; Action: Roll -> Roll option } | |
let genDie (r: System.Random): Die = r.Next(1, 7) | |
let genRoll length (r: System.Random): Roll = | |
[ for i in 0..length do | |
yield genDie r ] |> List.ofSeq | |
let removeAt index list = | |
list | |
|> List.mapi (fun i item -> i, item) | |
|> List.choose (fun (i, item) -> if i <> index then Some item else None) | |
let scoreSingle i points: ScoreRule = { | |
Score = points; | |
Action = fun dice -> | |
let die = List.tryFindIndex (fun die -> die = i) dice | |
match die with | |
| Some index -> Some(removeAt index dice) | |
| None -> None | |
} | |
let scoreRepeated repeats value scoreToAdd: ScoreRule = { | |
Score = scoreToAdd; | |
Action = fun dice -> | |
let matches, non_matches = List.partition ((=) value) dice | |
match List.length matches with | |
| n when n < repeats -> None | |
| n when n = repeats -> Some non_matches | |
| n -> | |
let rest = List.take (n - repeats) matches | |
Some (rest @ non_matches) | |
} | |
let scoreTriple = scoreRepeated 3 | |
let scoreMultiplier repeats (multiplier : int) value (baseRule: ScoreRule): ScoreRule = | |
scoreRepeated repeats value (baseRule.Score * multiplier) | |
let scoreQuad = scoreMultiplier 4 2 | |
let tripleOnes = scoreTriple 1 1000 | |
let quadOnes = scoreQuad 1 tripleOnes | |
let rules = | |
[ quadOnes | |
tripleOnes | |
scoreSingle 5 50 ] | |
//1200, scoreQuad 6 1200 | |
//1000, scoreQuad 5 1000 | |
//800, scoreQuad 4 800 | |
//600, scoreQuad 3 600 | |
//400, scoreQuad 2 400 | |
//1000, scoreTriple 1 1000 | |
//600, scoreTriple 6 600 | |
//500, scoreTriple 5 500 | |
//400, scoreTriple 4 400 | |
//300, scoreTriple 3 300 | |
//200, scoreTriple 2 200 | |
//100, scoreSingle 1 100 | |
//50, scoreSingle 5 50 ] |> List.sortByDescending fst |> List.map snd | |
let runRules roll = | |
rules | |
|> List.sortByDescending (fun r -> r.Score) | |
|> List.fold (fun (dice, score) rule -> | |
match rule.Action dice with | |
| Some remaining -> remaining, score + rule.Score | |
| None -> dice, score | |
) (roll, 0) | |
let scoreRoll roll = | |
runRules roll |> snd | |
let testInputsAndOutputs = [ | |
[1;1;1;5;1], 2050 | |
[2;3;4;6;2], 0 | |
[3;4;5;3;3], 350 | |
] | |
let test () = | |
testInputsAndOutputs | |
|> List.iter (fun (roll, expected) -> | |
let score = scoreRoll roll | |
if score <> expected then failwithf "Roll %A scored %d, not the expected %d" roll score expected | |
) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment