Command Line Quizzer for freerice.com
I've gotten quite addicted to the online site Freerice.com. As I've played more and more, my level has increased. My goal this year is to reach level fifty. In order to practice the words I don't know, I've written an F# program which can quiz me.
The program is designed to open, read, and parse a file before it queries the user about words. The format of the file should be as:
query word/phrase - answer word/phrase query word/phrase - answer word/phrase ...
An example excerpt from my file is:
scurrilous - vulgarly abusive repletion - surfeit laconic - terse sniffy - disdainful
By placing this text in a file called “uw.txt” the below F# program will query a user about unknown words until all the queries are correctly answered. The interface looks something like:
Enter the file name: uw.txt Opened "uw.txt" successfully. Read lines from "uw.txt" successfully. Parsed lines in "uw.txt" successfully. Generated options array successfully. Beginning queries: scurrilous means: 1. multi-colored 2. biscuit 3. spittoon 4. vulgarly abusive 4 Correct! - vulgarly abusive repletion means: 1. flood 2. surfeit 3. habitual 4. clothing 1 Wrong: surfeit laconic means: 1. unavoidable 2. take exception 3. terse 4. tool handle
The code for this is:
// quizzer program for freerice.com // Use light syntax. #light // Will need this for file IO. open System.IO // Random numbers are for queries. (seed based on current time) let rand = new System.Random(int32 System.DateTime.Now.Ticks) // Get the file name. printf "Enter the file name: " let filename = System.Console.ReadLine() // Open the file. let fStream = File.OpenText(filename) printfn "Opened \"%s\" successfully." filename // Read the file. let rec readFile (fileStream:StreamReader) = match fileStream.ReadLine() with // End of file. | null ->  // Prepend the line and continue reading. | line -> line::(readFile fileStream) let lines = readFile fStream printfn "Read lines from \"%s\" successfully." filename // Parse the file. let rec mapper (sList:string list) = match sList with | hd::tl -> // Assumes lines are of the form: "query - answer" let start = hd.IndexOf(" - "); let first = hd.Substring(0,start); let secnd = hd.Substring(start + 3); // Prepend the pair and continue parsing. (first, secnd)::(mapper tl) |  ->  let mappings = mapper lines printfn "Parsed lines in \"%s\" successfully." filename // Generate options array. // Use genOpts to produce a list of answers. let rec genOpts (maps:(string * string) list) = match maps with | (one,two)::tl -> two::(genOpts tl) |  ->  // Convert the list from genOpts to an array. let options = List.to_array (genOpts mappings) printfn "Generated options array successfully." // Perform a single freerice query. let rec genQuery lhs ans = try printfn "%s means:" lhs let frst = rand.Next(4) // Print wrong answers. for i = 1 to frst do let mutable opttemp = (options.[(rand.Next())%(options.Length)]) // Don't print duplicate correct answers. if opttemp.Equals((ans:string)) then opttemp <- "---" printfn "%i. %s" i opttemp // Print correct answer. printfn "%i. %s" (frst+1) ans // Print remaining wrong answers. for i = (frst+1) to 3 do let mutable opttemp = (options.[(rand.Next())%(options.Length)]) // Don't print duplicate correct answers. if opttemp.Equals((ans:string)) then opttemp <- "---" printfn "%i. %s" (i+1) opttemp // Read the user's answer. let ansString = System.Console.ReadLine() let ansString = ansString.Substring(0,1) // User uses "q" to exit. if ansString.Equals("q") then // Confirm user exiting. printfn "Really exit? (enter \"y\" to confirm)" let temp = System.Console.ReadLine() let temp = temp.Substring(0,1) if temp.Equals("y") then System.Environment.Exit(0); true else // If anything other than "y" is entered then re-prompt query. raise(new System.Exception()) else // Convert input to number. Exception will cause re-prompt. let inp = Int32.of_string ansString // Return true if correct and false otherwise. if inp = frst+1 then true else false with // Catch all exceptions. | _ -> // Signal error occurred and re-prompt query. printfn "Error occurred!" genQuery lhs ans printfn "\nBeginning queries:\n" let rec query mappings = match mappings with | (one,two)::tl -> // Perform query. match (genQuery one two) with | true -> printfn "Correct! - %s\n" two // Remove pair and continue querying. query tl | false -> printfn "Wrong: %s\n" two // Append pair to end of list and continue querying. query (List.append tl ((one,two)::)) |  -> printfn "Success! You're done." query mappings // Pause before exiting. System.Console.In.ReadLine();