module CalculatorWords open System.Net open System.Collections.Generic // Basic mappings: let substitutions = [ ('O', '0') ('I', '1') ('Z', '2') ('E', '3') ('H', '4') ('S', '5') ('L', '7') ('B', '8') ('G', '9') ] // Map of mappings: let subsMap = substitutions |> Map.ofList // Letters which are in the mappings: let numberLetters = substitutions |> List.map (fun subs -> fst(subs)) // Read a file from a url but as if it's local for performance: let urlReader (url : string) = let req = WebRequest.Create(url, Timeout = 1000 * 60 * 20) try let resp = req.GetResponse() let stream = resp.GetResponseStream() let tempFileName = System.IO.Path.GetTempFileName() let tempFileStream = new System.IO.FileStream(tempFileName, System.IO.FileMode.Truncate) stream.CopyTo(tempFileStream) tempFileStream.Seek(int64(0), System.IO.SeekOrigin.Begin) |> ignore new System.IO.StreamReader(tempFileStream) with | _ as ex -> failwith ex.Message // Read a word list and break it up into non-trivial, uppercase words: let words() = let reader = urlReader "http://unix-tree.huihoo.org/V7/usr/dict/words.html" seq { while not (reader.EndOfStream) do yield (reader.ReadLine().ToUpper()) } |> Seq.skip 17 // Skip HTML |> Seq.filter (fun word -> word.Length > 2) |> Seq.cache // Check if a word consists of calculator-letters: let goodWord (word : string) = // The word isn't good if it has any letters which aren't one of our 'number' letters: word |> String.exists (fun wordLetter -> ( numberLetters |> List.tryFindIndex (fun numberLetter -> numberLetter = wordLetter ) = None)) |> not // Translate a word into its calculator-letters equivalent: let translate (word : string) = word |> String.map (fun letter -> subsMap.Item letter) |> Array.ofSeq |> Array.rev |> Array.fold (fun acc elem -> sprintf "%s%c" acc elem) "" // Get all the words which can be spelt on a calculator: let calculatorWords = words() |> Seq.filter (fun word -> goodWord word) // Count the calculator words: let countWords = calculatorWords |> Seq.length // 179 // Print calculator words in plain and calculator versions: let printWords() = calculatorWords |> Seq.iter (fun word -> printfn "%s (%s)" word (translate word)) // BEE (338) // BEEBE (38338) // BEG (938) // BEIGE (39138) // BEL (738) // BELIE (31738) // BELL (7738) // ... // ZIG (912) // ZOE (302) // ZOO (002)