Skip to content

Instantly share code, notes, and snippets.

@ImaginaryDevelopment
Created December 30, 2021 22:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ImaginaryDevelopment/6472d790f97934eaa27ccdb9d650abaf to your computer and use it in GitHub Desktop.
Save ImaginaryDevelopment/6472d790f97934eaa27ccdb9d650abaf to your computer and use it in GitHub Desktop.
CrabHelper Guessing game
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