Skip to content

Instantly share code, notes, and snippets.

@baronfel
Created May 15, 2019 14:05
Show Gist options
  • Save baronfel/f05b6fc5845bbaeff761a54c92b53e55 to your computer and use it in GitHub Desktop.
Save baronfel/f05b6fc5845bbaeff761a54c92b53e55 to your computer and use it in GitHub Desktop.
Greed Kata
(*
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