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();
random/freerice.txt · Last modified: 2008/01/05 04:03 by grant