Created
December 30, 2021 22:02
-
-
Save ImaginaryDevelopment/6472d790f97934eaa27ccdb9d650abaf to your computer and use it in GitHub Desktop.
CrabHelper Guessing game
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
let inline tryParse f x = | |
match f x with | |
| true, v -> Some v | |
| _ -> None | |
// this version does not compile: | |
//let (|Parse|_|) (str: string) : int option = tryParse Int32.TryParse str | |
let inline tryParseInt (x:string) = x |> tryParse Int32.TryParse | |
let (|Negative|Zero|Positive|) x = | |
if x < LanguagePrimitives.GenericZero then | |
Negative x | |
elif x > LanguagePrimitives.GenericZero then | |
Positive x | |
else Zero | |
let (|Parse|_|) (str: string) : int option = str |> tryParse Int32.TryParse | |
module Init = | |
let parseOrDie = | |
function | |
| Parse v -> v | |
| v -> failwithf "Argument provided was not an int: '%s'" v | |
let clampOrDie (lower,upper) x = | |
if x < lower then failwithf "Argument must be greater than or equal to %i but was %i" lower x | |
elif x > upper then failwithf "Argument must be less than or equal to %i but was %i" upper x | |
else x | |
let parseRange args = | |
Array.tryHead args | |
|> Option.map (parseOrDie>>clampOrDie (1,100)) | |
|> function | |
| None -> failwith "Range was not provided" | |
| Some x -> x | |
() | |
type GameTermination = | |
| Won | |
| GaveUp | |
type GameState = { | |
Guesses:Set<int> | |
Target:int | |
Count:int | |
Termination: GameTermination option | |
} | |
let (|Finished|Running|) = | |
function | |
| {Termination=None} as x -> Running x | |
| x -> Finished x | |
let (|Guess|Quit|BadInput|) = | |
function | |
| Parse v -> Guess v | |
| "give up" -> Quit | |
| _ -> BadInput | |
let runGame (maxValueInclusive:int) = | |
let random = Random() | |
let number = random.Next(1, maxValueInclusive+1 ) | |
printfn $"OK, let's start. I am thinking of a number between 1 to {maxValueInclusive}" | |
{ | |
Guesses= Set.empty | |
Target= number | |
Count= 0 | |
Termination= None | |
} | |
|> Seq.unfold( | |
function | |
| Finished _ -> None | |
| Running state -> | |
Console.WriteLine "Enter your guess: " | |
match Console.ReadLine() with | |
| Quit -> | |
let next = {state with Termination = Some GaveUp} | |
Some(next,next) | |
| BadInput -> | |
printfn "Sorry the value must be a number or type 'give up' without quotes" | |
Some(state,state) | |
| Guess guess -> | |
if state.Guesses |> Set.contains guess then | |
printfn "You already guessed that" | |
Some(state,state) | |
else | |
match guess with | |
| Negative _ -> | |
printfn "Sorry the value cannot be negative" | |
Some(state,state) | |
| Zero -> | |
printfn "Sorry the value cannot be zero" | |
Some(state,state) | |
| x when x > maxValueInclusive -> | |
printfn "Sorry the value cannot be greater than %i" maxValueInclusive | |
Some(state,state) | |
| x when x = state.Target -> | |
let next = { state with Termination = Some GameTermination.Won} | |
Some (next,next) | |
| x -> | |
if x < state.Target then | |
printfn $"{x} is too low!" | |
else | |
printfn $"{x} is too high!" | |
let next = {state with Guesses = state.Guesses |> Set.add x; Count = state.Count + 1} | |
Some (next,next) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment