
Windows PowerShell solution to Event 1 in the 2008 Winter Scripting Games.
Solutions are also available for Windows PowerShell and Perl.
As it turned out, it wasn’t too hard to write a script that could convert a phone number to a word. What was hard, at least for the Scripting Guys, was realizing that they had written a script that could convert a phone number to a word. That’s because, once he had finished the script, one of the Scripting Guys (Greg? No, it couldn’t possibly have been Greg!) tried testing the script using his home phone; he thought it would be cool to see if his home phone number could be converted to a word. The only problem? His home phone number includes a 1 and two 0s, numbers that aren’t associated with any letters.
But did he realize that? Heavens no; in fact, he must have run the script a dozen times before it dawned on him. Each time he would run the script the script would fail. Each time he would throw in a few debugging commands and see that the script was returning only 4 characters. Each time he would look at the code and make some superfluous change. And each time he would run the script and the script would fail.
After nearly half an hour of this it suddenly occurred to him why the script was returning only 4 characters. Once he entered the phone number 9737853 – in which all 7 digits are associated with a set of letters – the script almost instantly supplied him with an answer:
wrestle
Oh, well; live and learn. Which would be good advice if he ever did learn from his mistakes.
At any rate, here’s the script that the Scripting Guys came up with:
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\WordList.txt", ForReading)
strWordList = objFile.ReadAll
objFile.Close
strWordList = Replace(strWordList, vbCrLf, " ")
Set objDictionary = CreateObject("Scripting.Dictionary")
objDictionary.Add "2", "ABC"
objDictionary.Add "3", "DEF"
objDictionary.Add "4", "GHI"
objDictionary.Add "5", "JKL"
objDictionary.Add "6", "MNO"
objDictionary.Add "7", "PRS"
objDictionary.Add "8", "TUV"
objDictionary.Add "9", "WXY"
intHighNumber = 3
intLowNumber = 1
strPhoneNumber = InputBox("Please enter the phone number: ", "Phone Number")
Do While True
strCharacters = ""
For i = 1 to 7
intValue = Mid(strPhoneNumber, i, 1)
strLetters = LCase(objDictionary.Item(intValue))
Randomize
intNumber = Int((intHighNumber - intLowNumber + 1) * Rnd + intLowNumber)
strCharacters = strCharacters & Mid(strLetters, intNumber, 1)
Next
strCharacters = " " & strCharacters & " "
intWordFound = InStr(strWordList, strCharacters)
If intWordFound Then
Wscript.Echo Trim(strCharacters)
Exit Do
End If
Loop
Our solution to Event 1 starts out by defining a constant named ForReading and setting the value to 1; we’ll need this constant in just a second or so, when we open the list of acceptable words. After defining the constant we create an instance of the Scripting.FileSystemObject, then use the following line of code to open the file C:\Scripts\WordList.txt for reading:
Set objFile = objFSO.OpenTextFile("C:\Scripts\WordList.txt", ForReading)
Needless to say, it’s no fun to open a file for reading unless you go ahead and read from it. With that in mind, our next step is to use the ReadAll method to read the entire contents of the file into memory, storing those contents in a variable named strWordList:
strWordList = objFile.ReadAll
Once the contents of WordList.txt are in memory we have no more use for the actual file itself; therefore we use this line of code to close the text file:
objFile.Close
We then replace all the carriage return-linefeeds in strWordList with blank spaces:
strWordList = Replace(strWordList, vbCrLf, " ")
What’s the point of that? We’ll explain later.
Promise.
Our next task is to associate the numbers 2 through 9 with the corresponding letters on the telephone dial. (Remember, neither the 1 nor the 0 have any letters associated with them.) We decided to do that by using a Dictionary object; with that in mind, we created an instance of the Scripting.Dictionary object, then populated that object with our telephone information, using the number as the Dictionary key and the corresponding letters as the Dictionary value. For example, this line of code adds the number 2 and the value ABC to the Dictionary:
objDictionary.Add "2", "ABC"
Why did we decide to use the Dictionary object rather than, say, a multi-dimensional array? Two reasons. For one, we find the Dictionary object much easier to use than a multi-dimensional array. That might not be true for everyone, but we tend to lose our place in a multi-dimensional array. On top of that, we needed an easy way to locate a specific number. For example, if the phone number includes a 4 we need to be able to quickly and easily locate the number 4 and, not incidentally, retrieve the letters associated with that number. That’s very easy to do using the Dictionary object, less easy to do using some other data structures. Like, say, a multi-dimensional array.
At any rate, after populating our Dictionary object we next assign values to a pair of variables:
intHighNumber = 3 intLowNumber = 1
What are these variables for? Well, as you know, each number on the telephone dial is associated with 3 letters; for example, the number 2 is associated with the letters A, B, and C. If our telephone number includes a 2 we’re going to randomly pick either the letter A, the letter B, or the letter C. Randomly picking one of three letters means picking a number between 1 and 3, inclusive. Hence the two variables, which represent the low end (1) and the high end (3) of our range.
At this point we’re ready to get the phone number, something we do by calling the InputBox function:
strPhoneNumber = InputBox("Please enter the phone number: ", "Phone Number")
That simply pops up an InputBox that looks something like this:

After the user enters the phone number and clicks OK, the number gets stored in a variable named strPhoneNumber.
Now it’s time to get down to business. Our first step is to set up a Do While Loop that continues to run as long as True is equal to True:
Do While True
And you’re right, that does sound like an endless loop, doesn’t it? And there’s a good reason for that: it is an endless loop. That’s because we want the script to continue generating string values until it manages to produce a string that represents a real word. If that takes forever, well, so be it.
But don’t worry: we’ll provide a way out of this “endless” loop as soon as we do find a match.
Inside the loop the first thing we do is set the value of a variable named strCharacters to an empty string (""); we’re going to use strCharacters to hold the string representation of our phone number. After that we set up a For Next loop that runs from 1 to 7. Why 1 to 7? You got it: because we have seven digits in our phone number, and this loop is designed to grab those digits one-by-one.
And just how do we grab those digits one-by-one? Why, by using this line of code, of course:
intValue = Mid(strPhoneNumber, i, 1)
What we’re doing here is using the Mid function to grab the ith character in the phone number. The first time through the loop i will be equal to 1; hence this line of code will grab the first character in the phone number. (And only the first character; that’s what the 1 is for.) The second time through the loop i will be equal to 2, which means that the script will grab the second character in the phone number. And so on.
So what exactly do we do with one of those characters after we grab it? Well, let’s say the first number in the phone number is a 4; that means that intValue will also equal 4. In turn, that means we can use this line of code to find the Dictionary item with a key equal to 4 and, while we’re at it, store the lowercase value of that item in a variable named strLetters:
strLetters = LCase(objDictionary.Item(intValue))
What does all that mean? That means that strLetters will be equal to ghi, the three letters associated with the number 4. Of course, we want only 1 of those 3 letters. Which letter do we want? To tell you the truth, we don’t know. Therefore we randomly choose a number between 1 and 3; that’s what this block of code is for:
Randomize intNumber = Int((intHighNumber - intLowNumber + 1) * Rnd + intLowNumber)
And once we have a random number we can then use this line of code to randomly choose one of the three letters in ghi, adding the selected letter to the string value strCharacters:
strCharacters = strCharacters & Mid(strLetters, intNumber, 1)
In other words, suppose our phone number is 4732837. The first time through the loop we’ll locate item 4 in the Dictionary, then randomly select one of the three letters associated with that number. Let’s say we select letter 2 (h). That means that, at the moment, strCharacters will be equal to this:
h
OK, that’s not much, but it’s a start. We then repeat this process with the second digit in the phone number: 7. The 7 is associated with the letters prs; if we randomly choose the s that means that strCharacters will be equal to this:
hs
We simply continue this process until we’ve constructed a seven-character string. At that point we tack blank spaces onto the beginning and the end of our string value:
strCharacters = " " & strCharacters & " "
Why do we do that? Well, remember, back at the beginning of the script we replaced all the carriage return-linefeeds in strWordList with blank spaces. We said we’d explain why we did that later and, at long last, it’s later: we did that to help guard against matching partial words. For example, suppose our script generated the following string value:
criptio
Is that a word? We don’t think so. But it is a string value that can be found in the word description:
description
To help guard against that we’ve separated each word in strWordList with a blank space before and after. That means that, to be a match, the string value must also have a space before and after it; that ensures that we match actual words and not just substrings. The string value criptio will no longer generate a match because it has to match an entire word and not just a set of letters within a word.
Or so we think, anyway.
At that point we use the InStr function to see if our string value can be found anywhere in the variable strWordList:
intWordFound = InStr(strWordList, strCharacters)
Suppose the value isn’t found. That means our string isn’t an actual word; consequently, we go back to the top of loop, reset strCharacters to an empty string, and try again. If the value is found we execute this block of code:
Wscript.Echo Trim(strCharacters) Exit Do
In the first line we’re simply echoing back the value of strCharacters, the word we found. (We use the Trim function to remove the blank spaces from the beginning and end of the string.) And then we call the Exit Do statement and exit the endless loop. At that point, the script ends, we collect our points, and we move on to the next event.
By the way, here’s what we got when we ran this script using 7876473 as the phone number:
surmise
Good enough!