Usage:
// Usage: dndgenerate /e [emhd] [0-9+]
// dndgenerate /e d 4 4 5 4
// /e Generate Encounter. Parameter can be one of
// e - Easy
// m - Medium
// h - Hard
// d - Deadly
// As well as a list of heroes by their levels
//
// Example: dndgenerate /e m 4 4 5 4
// Generate a medium encounter for three level 4 and one level 5 adventurers.
The code to make it happen:
type DifficultyOption = Easy | Medium | Hard | Deadly
type EncounterOptions = {
difficulty: DifficultyOption option
heroes: int list
}
let (|Level|_|) (str: string) =
let mutable intValue = 0
if System.Int32.TryParse(str, &intValue)
then match intValue with
| x when x < 0 -> None
| x when x > 20 -> None
| x -> Some x
else None
let (|Difficulty|_|) (str: string) =
match str with
| "e" | "E" -> Some(Easy)
| "m" | "M" -> Some(Medium)
| "h" | "H" -> Some(Hard)
| "d" | "D" -> Some(Deadly)
| x -> None
let isComplete (options:EncounterOptions) =
not <| options.heroes.IsEmpty && options.difficulty.IsSome
type VerboseOption = VerboseOutput | TerseOutput
type MiscOptions = {
verbose: VerboseOption
}
type GeneratorType =
| E of EncounterOptions
| Unknown
type ParseMode = TopLevel | Encounter | Plot | Dungeon | Settlement | Error
type CommandLineOptions = {
misc: MiscOptions
parseMode: ParseMode
generatorType: GeneratorType
}
let parseTopLevel arg miscSoFar =
match arg with
| "/v" ->
let newMiscSoFar = { miscSoFar with verbose = VerboseOutput }
{ misc = newMiscSoFar; parseMode = TopLevel; generatorType = Unknown }
| "/e" ->
{ misc = miscSoFar; parseMode = Encounter; generatorType = E { difficulty = None; heroes = [] } }
| x ->
{ misc = miscSoFar; parseMode = Error; generatorType = Unknown }
let parseEncounter arg miscSoFar encounterGenerator =
match arg with
| Level x ->
let builder' = { encounterGenerator with heroes = x :: encounterGenerator.heroes }
{ misc = miscSoFar; parseMode = Encounter; generatorType = E builder' }
| Difficulty x ->
let builder' = { encounterGenerator with difficulty = Some x }
{ misc = miscSoFar; parseMode = Encounter; generatorType = E builder' }
| _ ->
{ misc = miscSoFar; parseMode = Error; generatorType = E encounterGenerator }
let foldFunction state element =
match state with
| { misc = m; parseMode = TopLevel } ->
parseTopLevel element m
| { misc = m; parseMode = Encounter; generatorType = E(g) } ->
parseEncounter element m g
| { parseMode = Error } ->
state
| { misc = m; parseMode = p; generatorType = g } ->
printfn "Unexpected constellation of %A %A %A" m p g
state
let disableIfIncomplete commandLineOptions =
match commandLineOptions.parseMode with
| Error ->
commandLineOptions
| _ ->
match commandLineOptions.generatorType with
| E(encounter) when isComplete encounter ->
commandLineOptions
| Unknown -> commandLineOptions
| _ -> { commandLineOptions with parseMode = Error }
let parseCommandLine args =
let defaultOptions = {
verbose = TerseOutput
}
let initialFoldState =
{ misc = defaultOptions; parseMode = TopLevel; generatorType = Unknown }
let finalFoldState = args |> List.fold foldFunction initialFoldState
disableIfIncomplete finalFoldState
Based on code from Scott Wlaschin's Parsing command line arguments but extended to include argument groups and error handling. Purposefully avoided FParsec because I'm new to F# and wanted to solve it vanilla-style first.