module Kerbal.Names open Argu let readLinesFrom file = use reader = new System.IO.StreamReader(path=file) reader.ReadToEnd().Split('\n') |> List.ofArray type CommandLineArgument = | [] Quantity of int | [] Separator of string | [] DataDir of string with interface IArgParserTemplate with member this.Usage = match this with | Quantity _ -> "How many names to generate (defaults to 1)" | Separator _ -> "What to separate generated names with (defaults to newline)" | DataDir _ -> "Where the program's datafiles can be found (defaults to KerbalNameData under working directory)" module RNG = let seedGenerator = System.Random() let localGenerator = new System.Threading.ThreadLocal(fun _ -> lock seedGenerator (fun _ -> let seed = seedGenerator.Next() new System.Random(seed))) // Returns a version of System.Random using the threadlocal RNG. // NOTE: Most functions are NOT thread-safe in this version. let getRand() = {new System.Random() with member this.Next(lim) = localGenerator.Value.Next(lim)} let inline randomChoice (rand : System.Random) (list:'a list) = list.[rand.Next(list.Length)] let inline generateHybridName rand prefixList suffixList = (randomChoice rand prefixList) + (randomChoice rand suffixList) let genName (rand : System.Random) properNames namePrefixes nameSuffixes = if rand.Next(20) = 20 then randomChoice rand properNames else generateHybridName rand namePrefixes nameSuffixes [] let main argv = // Construct CLI parser with programname taken from env variables let argParser = ArgumentParser.Create(programName = (Array.head <| System.Environment.GetCommandLineArgs())) let parseResults = argParser.Parse argv let dataDir = parseResults.GetResult(<@ DataDir @>, defaultValue = "KerbalNameData") + "/" // Append a slash in case missing let quantity = parseResults.GetResult(<@ Quantity @>, defaultValue = 1) let separator = parseResults.GetResult(<@ Separator @>, defaultValue = "\n") // Access name datafiles let properNames = readLinesFrom (dataDir + "Proper.txt") let namePrefixes = readLinesFrom (dataDir + "Prefixes.txt") let nameSuffixes = readLinesFrom (dataDir + "Suffixes.txt") let rand = RNG.getRand() let printingMailbox = MailboxProcessor.Start(fun inbox -> let rec loop () = async { let! msg = inbox.Receive() printf "%s" msg return! loop() } loop() ) System.Threading.Tasks.Parallel.For(0, quantity, fun idx -> genName rand properNames namePrefixes nameSuffixes |> (fun name -> printingMailbox.Post(name + separator)) ) |> ignore // We do not care about the result which came from the parallel loop. 0