Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
type Pins = int
let [<Literal>] MaxPins : Pins = 10
type Frame =
| Strike of Pins
| Spare of Pins * Pins
| Open of Pins * Pins
| Pending of Pins
| Final of Pins * Pins option * Pins option
override this.ToString() =
let strikeOrNormal i = if i = MaxPins then "X" else string i
let opt = Option.map strikeOrNormal >> Option.defaultValue ""
match this with
| Strike _ -> "X -"
| Spare (first, _) -> $"{first} /"
| Open (first, seconde) -> $"{first} {seconde}"
| Pending first -> $"{first}"
| Final (MaxPins, Some s, Some t) when s + t = MaxPins -> $"X {s} /"
| Final (f, Some s, Some t) when f + s = MaxPins -> $"{f} / {strikeOrNormal t}"
| Final (first, seconde, third) -> $"{strikeOrNormal first} {opt seconde} {opt third}"
module Frame =
let start num pins =
match num, pins with
| 10, _ -> Final (pins, None, None)
| _, MaxPins -> Strike MaxPins
| _ -> Pending pins
let close first seconde =
(first, seconde)
|> if first + seconde = MaxPins then Spare else Open
let final pins = function
| (f, None, None) -> Final (f, Some pins, None)
| (f, Some s, None) when f + s >= MaxPins -> Final (f, Some s, Some pins)
| _ -> failwith "Final frame already closed."
module Game =
let roll pins frames =
match List.rev frames with
| Pending f :: tail -> Frame.close f pins :: tail
| Final (f, s, t) :: tail -> Frame.final pins (f, s, t) :: tail
| tail -> Frame.start (frames.Length + 1) pins :: tail
|> List.rev
let rollSeq pinsSeq =
let mutable frames = []
for pins in pinsSeq do
frames <- roll pins frames
frames
let spareBonus = function
| Strike f | Spare (f, _) | Open (f, _) | Pending f | Final (f, _, _) -> f
let strikeBonus = function
| Spare (f, s) | Open (f, s) | Final (f, Some s, _) -> f + s
| _ -> 0
let scoring frames =
let rec loop total scores rest =
match rest with
| [] ->
List.rev scores
| Open (f, s) :: tail ->
let score = total + f + s
loop score (score :: scores) tail
| Spare (f, s) :: (next :: _ as tail) ->
let score = total + f + s + (spareBonus next)
loop score (score :: scores) tail
| Strike f1 :: (Strike f2 :: (next :: _) as tail) ->
let score = total + f1 + f2 + (spareBonus next)
loop score (score :: scores) tail
| Strike f :: (next :: _ as tail) ->
let score = total + f + (strikeBonus next)
loop score (score :: scores) tail
| Final (f, s, t) :: _ ->
let opt v = defaultArg v 0
let score = total + f + (opt s) + (opt t)
loop score (score :: scores) []
| Pending f :: _ ->
loop (total + f) (f :: scores) []
| _ :: tail ->
loop total scores tail
loop 0 [] frames
module Printer =
let concat s = String.concat " " s
let frames frames =
frames |> List.map string |> concat |> printfn "%s"
let scoring scoring =
scoring |> List.map (sprintf "%3i") |> concat |> printfn "%s"
let frames =
[ 5; 4; 4; 6; 7; 0; 10; 10; 10; 5; 3; 6; 4; 4; 6; 10; 10; 10 ]
|> Game.rollSeq
frames |> Printer.frames
frames |> Game.scoring |> Printer.scoring
// 5 4 4 / 7 0 X - X - X - 5 3 6 / 4 / X X X
// 9 26 33 63 88 106 114 128 148 178
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment