# 2008 Scripting Games - My solutions

Here are my solutions to the 2008 Scripting Games scripting competition. This is a rather large post but I decided one post would be better than posting them all individually, this way they will all be in one place when I want to find them later. I had fun and I learned a lot of new cool things about Powershell while working through the problems. If you are trying to learn Powershell I would definitely recommend working through them to force yourself to learn the details.

Disclaimer: These script samples are by no means perfect but they are the unedited solutions that I submitted so they do solve the problems. They are provided as-is and you should use them at your own risk.

## Powershell beginner division

Event 1: Pairing Off
Official Solution
My Solution:

```# first char in card is A, 1, 2, 3, 4, 5, 6, 7, 8, 9, T, J, Q, K # second char in card is H - Hearts, S - Spades, C - Clubs, D - Diamonds
\$cards = @("7S", "5H", "7D", "7C", "KC"); \$numPairs = 0;

for (\$i = 0; \$i -lt \$cards.Length; \$i++) {
for (\$j = \$i + 1; \$j -lt \$cards.Length; \$j++)
{
if (\$cards[\$i][0] -eq \$cards[\$j][0])
{
\$numPairs++;
}
}
}

Write-Host \$numPairs;```

Event 2: True Type
Official Solution
My Solution:

```\$fonts = Get-Item "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Fonts"
\$truetypes = \$fonts.Property | where { \$_ -match "\(TrueType\)" }

Write-Host "TrueType Fonts:"
\$truetypes

Write-Host
Write-Host "TrueType: \$(\$trueTypes.Count)"
Write-Host "Total: \$(\$fonts.ValueCount)"```

Event 3: Let’s Get Together
Official Solution
My Solution:

```\$firstLines = dir "C:\Scripts\*.txt" | % { Get-Content \$_ -totalCount 1 }
\$firstlines | Out-File -encoding ASCII "c:\scripts\newfile.txt"```

Event 4: Count Yourself In
Official Solution
My Solution:

```\$c=0
Get-Content \$MyInvocation.MyCommand.Definition | % { \$c += \$_.Length }
\$c```

Event 5: What’s the Difference
Official Solution
My Solution:

```param([DateTime] \$d)

\$t = [DateTime]::Today

\$ts = \$d - \$t
\$ms = (\$d.Month - \$t.Month) + 12 * (\$d.Year - \$t.Year);

Write-Host ("Days: " + \$ts.Days)
Write-Host ("Months: " + \$ms)

if (\$d.Day -lt \$t.Day) { \$ms--; }

Write-Host ("Months/Days: " + \$ms + " / " + \$ts.Days)```

Event 6: Coffee Break
Official Solution
My Solution:

```\$cfile = (Get-Content "C:\Scripts\coffee.txt")

\$orders = @{}
foreach(\$o in \$cfile)
{
\$d, \$c = \$o.Split(" ")
\$orders[\$d] += [int]\$c;
}
\$orders.Remove("Office");

\$orders.GetEnumerator() | % { Write-Host (\$_.Name + " " + \$_.Value) }```

Event 7: Squashing Bugs
Official Solution
My Solution:

```foreach (\$i in Get-ChildItem C:\Scripts -recurse)
{
if ((\$i.CreationTime -lt (\$(Get-Date).AddDays(-10))) -and (\$i.Extension -eq ".txt"))
{
Copy-Item \$i.FullName C:\old
\$i.Name
\$x = \$x + 1
}
}

""
"Total Files: " + \$x```

Event 8: Random Guess
Official Solution
My Solution:

```\$r = New-Object System.Random
\$low = 1
\$high = 50
[int]\$num = \$r.Next(\$low, \$high)
\$guess = \$guesses = 0

while (\$num -ne \$guess)
{
Write-Host -NoNewline "Guess a number between \$low and \$high : "

if (\$guess -lt \$num)
{
Write-Host "Too Low!"
\$low = \$guess
}
else
{
if(\$guess -gt \$num)
{
Write-Host "Too High!"
\$high = \$guess
}
}
\$guesses++
}

Write-Host "Random number was \$num"
Write-Host "Total guesses: \$guesses"```

Event 9: Pool Party
Official Solution
My Solution:

```\$adOpenStatic = 3

\$objConnection.Open("Provider=Microsoft.Jet.OLEDB.4.0; Data Source= c:\scripts\pool.mdb")

do
{
Write-Host ("  Name: " + \$objRecordset.Fields.Item("Customer").Value)
[int]\$length = \$objRecordset.Fields.Item("Length").Value;
[int]\$width = \$objRecordset.Fields.Item("Width").Value;
[int]\$depth = \$objRecordset.Fields.Item("Depth").Value;
[bool]\$slope = \$objRecordset.Fields.Item("Slope").Value;
[int]\$sstart = \$objRecordset.Fields.Item("SStart").Value;
[int]\$send = \$objRecordset.Fields.Item("SEnd").Value;

if (\$slope)
{
Write-Host ("    Volume of Water: " + (\$length * \$width * ((\$send + \$sstart)/2) * 1000))
}
else
{
Write-Host ("    Volume of Water: " + (\$length * \$width * \$depth * 1000))
}
Write-Host
\$objRecordset.MoveNext()
} until (\$objRecordset.EOF -eq \$True)

\$objRecordset.Close()
\$objConnection.Close()```

Event 10: Bowling
Official Solution
My Solution:

```\$arrFrames = 2,5,7,"/",8,1,"X",9,"/",5,3,7,0,4,5,"X",2,0

\$frames = \$arrFrames -replace "X","10"
for(\$i = 0; \$i -lt \$frames.Count; \$i++) { if (\$frames[\$i] -eq "/") { \$frames[\$i] = 10 - [int]\$frames[\$i-1]; } }
[int[]]\$frames = \$frames;

\$score = 0;
\$fc = 0;
for(\$i = 0; \$i -lt \$arrFrames.Count -and \$fc -lt 10;)
{
if (\$frames[\$i] -eq 10)
{
\$score += \$frames[\$i] + \$frames[\$i+1] + \$frames[\$i+2];
\$i++; \$fc++;
}
else
{
\$fs = \$frames[\$i] + \$frames[\$i+1];

\$score += \$fs;
\$i += 2; \$fc++;

if (\$fs -eq 10) { \$score += \$frames[\$i]; }
}
}
\$score```

```function ConvertLetterToNumber([char] \$letter) {
switch -regex (\$letter)
{
"[abc]" { return "2"; }
"[def]" { return "3"; }
"[ghi]" { return "4"; }
"[jkl]" { return "5"; }
"[mno]" { return "6"; }
"[prs]" { return "7"; }
"[tuv]" { return "8"; }
"[wxy]" { return "9"; }
default { return ""; }
}
}

Write-Host -nonewline "Enter phone number (XXXXXXX): "; \$number = (Read-Host);

if (!(\$number -match "^\d{7}\$"))
{
Write-Error "Please enter a number in format XXXXXXX";
exit;
}

\$foundWord = \$null;

\$file = [System.IO.File]::OpenText("c:\scripts\wordlist.txt");

{
if (\$word.Length -eq 7)
{
\$i = 0;
while ((ConvertLetterToNumber \$word[\$i]) -eq \$number[\$i]) { \$i++; }

if (\$i -eq 7)
{
\$foundWord = \$word;
break;
}
}
}

\$file.Close();

if (\$foundWord -eq \$null)
{
Write-Host "Sorry no such word 7 letter word in the list!";
}
else
{
Write-Host "Found 7 letter word: \$foundWord";
}```

Event 2: Skating on Thin Ice
Official Solution
My Solution:

```\$skaterScores = @();
foreach(\$skaterLine in (Get-Content "c:\scripts\skaters.txt")) {
\$name, \$scores = \$skaterLine -split ","

# Sort for easy removal of best and worst score
\$scores = [int[]]\$scores | sort

# Calclate avg skiping first and last
\$total = 0;
\$scores[1..(\$scores.Count - 2)] | foreach { \$total += \$_ }

\$skaterScores += \$name | Add-Member -memberType NoteProperty -Name AvgScore -Value (\$total / (\$scores.Length - 2)) -passthru
}

# Rank the skaters by Avg Score
\$skaterScores = \$skaterScores | sort -descending AvgScore

# Assign medals to the top three
\$i = 0;
foreach(\$medal in ("Gold", "Silver", "Bronze")) {
\$skaterScores[\$i] | Add-Member -MemberType NoteProperty -Name Medal -Value \$medal
# Simple Console output
#"\$medal Medal: \$(\$skaterScores[\$i]), \$(\$skaterScores[\$i].AvgScore)";
\$i++;
}
# Powershell V2 Gridview output
\$skaterScores | Select Medal, @{Name="Name"; Expression={\$_}}, AvgScore | Out-GridView```
```\$voters = new-object System.Collections.ArrayList;

# Collect the votes from the file
\$vote = new-object System.Collections.ArrayList;
}

while(\$true)
{
\$voteCount = @{}

# Tally voting results
\$voters | % { \$voteCount[\$_[0]] = \$voteCount[\$_[0]] + 1; }

# Sort voting results
\$voteCount = \$voteCount.GetEnumerator() | sort -descending Value

# Did the winner get more than 50% of the vote?
if (\$voteCount[0].Value * 2 -gt \$voters.Count)
{
Write-Host ("The winner is {0} with {1:P2} of the vote." -f \$voteCount[0].Name, (\$voteCount[0].Value / \$voters.Count))
break;
}
else
{
# Clear out the candidate with losted number of votes
\$voters | % { \$_.Remove(\$voteCount[\$voteCount.Count - 1].Name); }
}
}```

Event 4: Image is Everything
Official Solution
My Solution:

```Write-Host -nonewline "Enter month (M/YYYY): "

# Ensure we are set to the first day of the month
\$month = \$month.AddDays(-1 * (\$month.Day - 1))

Write-Host \$month.ToString("`n   MMMM yyyy`n")

# Write days of week headers
0..6 | % { Write-Host -nonewline ("{0,6}" -f ([string][DayofWeek]\$_).SubString(0, 3)) }

# Figure out the date of the first monday in a set of weeks
Write-Host
while (\$next.Month -le \$month.Month)
{
# Walk the week and only write the day if the mondy matches
0..6 | foreach {
if (\$next.Month -eq \$month.Month) { \$day = \$next.Day } else { \$day = "" }
Write-Host -nonewline ("{0,6}" -f \$day)
}
Write-Host
}
Write-Host```
```param([string]\$pass)

\$strength = 13

\$wl = (Get-Content "C:\Scripts\wordlist.txt");

if (\$wl -contains \$pass) {
Write-Host -f red "Make sure that the password is not an actual word."; \$strength-- }

if (\$wl -contains \$pass.SubString(0,\$pass.Length-1)) {
Write-Host -f red "Make sure that the password, minus the last letter, is not an actual word."; \$strength-- }

if (\$wl -contains \$pass.SubString(1)) {
Write-Host -f red "Make sure that the password, minus the first letter, is not an actual word."; \$strength-- }

if (\$pass -match "0" -and \$wl -contains (\$pass -replace '0','o')) {
Write-Host -f red "Make sure that the password does not simply substitute 0 (zero) for the letter o (either an uppercase O or a lowercase o)."; \$strength-- }

if (\$pass -match "1" -and \$wl -contains (\$pass -replace '1','l')) {
Write-Host -f red "Make sure that the password does not simply substitute 1 (one) for the letter l (either an uppercase L or a lowercase l)."; \$strength-- }

if (\$pass.Length -lt 10 -or \$pass.Length -gt 20) {
Write-Host -f red "Make sure that the password is at least 10 characters long but no more than 20 characters long."; \$strength-- }

if (\$pass -notmatch "\d") {
Write-Host -f red "Make sure that the password includes at least one number (the digits 0 through 9)."; \$strength-- }

if (\$pass -cnotmatch "[A-Z]") {
Write-Host -f red "Make sure that the password includes at least one uppercase letter."; \$strength-- }

if (\$pass -cnotmatch "[a-z]") {
Write-Host -f red "Make sure that the password includes at least one lowercase letter."; \$strength-- }

if (\$pass -notmatch "[^a-zA-Z0-9]") {
Write-Host -f red "Make sure that the password includes at least one symbol."; \$strength-- }

if (\$pass -cmatch "[a-z]{4}") {
Write-Host -f red "Make sure that the password does not include four (or more) lowercase letters in succession."; \$strength-- }

if (\$pass -cmatch "[A-Z]{4}") {
Write-Host -f red "Make sure that the password does not include four (or more) uppercase letters in succession."; \$strength-- }

if (\$pass -cmatch "(.).*\1") {
Write-Host -f red "Make sure that the password does not include any duplicate characters."; \$strength-- }

\$s = "moderately-strong"; \$c = "yellow"
if (\$strength -le 6) { \$s = "weak"; \$c = "red" }
if (\$strength -ge 11) { \$s = "strong"; \$c = "green" }

Write-Host
Write-Host -nonewline "A password score of "
Write-Host -nonewline -f \$c "\$strength"
Write-Host -nonewline "/13 indicates a "
Write-Host -nonewline -f \$c "\$s"

Event 6: Prime Time
Official Solution
My Solution:

```\$primes = new-object System.Collections.ArrayList
for (\$i = 0; \$i -lt \$primes.Count; \$i++)
{
for (\$j = \$i + 1; \$j -lt \$primes.Count;)
{
if (\$primes[\$j] % \$primes[\$i] -eq 0)
{
\$primes.Remove(\$primes[\$j])
}
else
{
\$j++;
}
}
}
# Simple Console Output
#\$primes

# Powershell V2 GridView
\$primes | select @{ Name="Primes"; Expression={ \$_.ToString() } } | Out-GridView```

Event 7: Play Ball!
Official Solution
My Solution:

```\$a = "A","B","C","D","E","F"
1..(\$a.Length-1) | % { ( 1..(\$a.Length/2) | % { \$a[\$_-1]+" vs. "+\$a[-\$_] } ); write-host; [string[]]\$x,\$y,\$z=\$a;\$a=\$x+\$z+\$y; }
```

Event 8: Making Beautiful Music
Official Solution
My Solution:

```\$songs=@()
\$names=@{}
\$total=0;
foreach (\$songLine in (Get-Content "c:\scripts\songlist.csv"))
{
\$name,\$title,\$length = \$songLine.Split(",");

# Ensure no more than 2 of same artist
if (\$names[\$name] -ge 2) { continue } \$names[\$name]++;

\$mm,\$ss=[int[]]\$length.Split(":");
\$time = \$mm*60+\$ss;

if (\$total + \$time -ge 4800) # 80 Mins
{
if (\$total -gt 4500) { break; } # Done
continue; # Try next song
}
\$total += \$time;

\$song = new-object psobject
\$song | Add-Member NoteProperty "Artist Name" \$name
\$song | Add-Member NoteProperty "Song Title"  \$title
\$song | Add-Member NoteProperty "Song Length" \$length
\$songs += \$song
}

\$songs | sort "Artist Name"

Write-Host ("`nTotal music time: {0}:{1:00}" -f ((\$total-\$total%60)/60), (\$total%60))```
`((Get-Content "c:\scripts\alice.txt") -split "\s" | % { \$s=[char[]]\$_; [System.Array]::Reverse(\$s); \$s -join "" }) -join " "`

Event 10: Blackjack!
Official Solution
My Solution:

```\$suites = "Clubs", "Diamonds", "Hearts", "Spades"
\$numStrings = "Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"

function NextCard { \$c,\$script:cards = \$script:cards; \$c }
function ShowYourCards {
\$script:yourSum = 0;
\$aces = 0;
\$yourCards | % { if (\$_.Value -eq 11) { \$aces++ }; \$script:yourSum += \$_.Value; \$_.Name }
while (\$script:yourSum -gt 21 -and \$aces -gt 0) { \$script:yourSum -= 10; \$aces--; }
Write-Host "Your card total is \$yourSum"
}
function ShowDealerCards {
Write-Host "`nDealer's cards:"
\$script:dealerSum = 0
\$aces = 0;
\$dealerCards  | % { if (\$_.Value -eq 11) { \$aces++ }; \$script:dealerSum += \$_.Value; \$_.Name }
while (\$script:dealerSum -gt 21 -and \$aces -gt 0) { \$script:dealerSum -= 10; \$aces--; }
Write-Host "Dealer card total is \$dealerSum"
}
function Outcome([bool]\$win, [string]\$msg) {
if (\$win) {
Write-Host -ForegroundColor green "`nYou win! \$msg"
}
else {
Write-Host -ForegroundColor red "`nYou lose! \$msg"
}
exit
}

\$cards = @()
foreach(\$suite in \$suites) {
for(\$i = 0; \$i -lt \$numStrings.Count; \$i++)              {
\$numStr = \$numStrings[\$i];
\$c = New-Object PsObject
\$c | Add-Member NoteProperty Name \$(\$numStr + " of " + \$suite)
\$c | Add-Member NoteProperty Value \$(if(\$i -gt 9){10}else{if(\$i -eq 0){11}else{\$i+1}})
\$cards += \$c
}
}

\$rand = New-Object Random
\$cards = \$cards | sort { \$rand.Next() }

\$yourCards = @()
\$dealerCards = @()

\$yourCards += NextCard
\$dealerCards += NextCard
\$yourCards += NextCard

ShowYourCards
if (\$yourSum -eq 21) {
Outcome \$true "Blackjack, you got 21 with two cards."
}
ShowDealerCards
\$dealerCards += NextCard
Write-Host
while (\$yourSum -lt 21) {
Write-Host -NoNewline "Stay (s) or hit (h)? "

if (\$hitOrStay -ieq "s") { break; }
if (\$hitOrStay -ieq "h")
{
\$yourCards += NextCard
ShowYourCards
}
}
if (\$yourSum -gt 21) {
Outcome \$false "Your total \$yourSum is over 21."
}
if (\$yourSum -eq 21) {
Outcome \$true "Your total is exactly 21."
}
ShowDealerCards
# Assumes dealer keeps hitting while he is losing and not over 21, unlike most casinos where the stop at 17
while (\$dealerSum -lt 21 -and \$dealerSum -lt \$yourSum) {
\$dealerCards += NextCard
ShowDealerCards
}
if (\$dealerSum -gt 21) {
Outcome \$true "Dealer busted because dealer's \$dealerSum is over 21."
}
if (\$dealerSum -ge \$yourSum) {
Outcome \$false "The dealer's \$dealerSum is greater than or equal to your \$yourSum."
}
else {
Outcome \$true "Your \$yourSum is geater than the dealer's \$dealerSum."
}```

## Sudden death challenge

```#-1. Enables you to manage the Windows Firewall in Windows XP.
# 2. Enables you to create a remote connection using Windows Script Host.
#-3. Enables you to map printers using Windows Script Host.
# 4. Enables you to perform an XPath query in an XML document.
# 5. Enables you to create an instance of Internet Explorer.
#-6. Enables you to connect to a database.
#-7. Enables you to perform text-to-speech conversions in a script.
# 8. Enables you to create an instance of Microsoft Excel.
#-9. Enables you to send and create SMTP email.
#-10. Enables you to create an instance of Microsoft Outlook.
#-11. Enables you to work with Windows Remote Management.
# 12. Enables you to access type libraries from a script.
#-13. Enables you to manage Microsoft Indexing Server.
#-14. Enables you to manage Windows Media Player.
# 15. Enables you to manage Windows Update Services.
# 16. Enables you to manage TCP/IP and other network settings.
#-17. Enables you to work with XML documents.
# 18. Enables you to manage fonts.
#-19. Enables you to create a GUID.
#-20. Enables you to convert Active Directory names from one format to another.

Write-Host "ProgID              Description"
Write-Host "HNetCfg.FwMgr                 1"
Write-Host "Microsoft.XMLDOM             17"
Write-Host "SAPI.SpVoice                  7"
Write-Host "NameTranslate                20"
Write-Host "Scriptlet.TypeLib            19"
Write-Host "Outlook.Application          10"
Write-Host "WMPlayer.OCX                 14"
Write-Host "WScript.Network               3"
Write-Host "WSMan.Automation             11"
Write-Host "CDO.Application               9"
```\$v = get-content c:\scripts\vertical.txt
\$t = ""
for(\$i=0;\$i -lt \$v[0].Length; \$i++){ for (\$j=0;\$j -lt \$v.Count; \$j++) { \$t += \$v[\$j][\$i] } }
\$t```

Event 3: What's in a Name?
Official Solution
My Solution:

```\$pfile = (Get-Content "c:\Scripts\presidents.txt")
\$longestFirst = ""
\$vowelCount = 0
\$alpha = New-Object System.Collections.ArrayList
0..25 | % { [void]\$alpha.Add([char](\$_ + [int][char]"A")) }

foreach(\$pr in \$pfile)
{
\$last, \$first = \$pr.Split(",")
\$first = \$first.Trim()

if (\$longestFirst.Length -lt \$first.Length)
{
\$longestFirst = \$first;
}
\$alpha.Remove([Char]::ToUpper(\$first[0]))
\$alpha.Remove([Char]::ToUpper(\$last[0]))

\$pr.ToCharArray() | ? { \$_ -match "[aeiouAEIOU]" } | % { \$vowelCount++ }
}

Write-Host "Longest first name: \$longestFirst"
Write-Host "Letters not in initials:"
\$alpha | % { Write-Host \$_ }
Write-Host "Total vowel count: \$vowelCount"```

Event 4: Type By Numbers
Official Solution
My Solution:

```\$a = (Get-Content "C:\scripts\numbers.txt")
\$s = ""
for(\$i = 0; \$i -lt \$a.Length; \$i+=2) { [int]\$c=([string]::Join("",\$a[(\$i)..(\$i+1)])); \$s += [char]\$c }
\$s```
```\$d = @{ }
\$win32Classes = Get-WmiObject -namespace "root\cimv2" -list | ? { \$_.Name -match "^Win32_" }

foreach(\$w in \$win32Classes)
{
foreach(\$p in \$w.psbase.Properties)
{
\$c = [char]::ToUpper(\$p.Name[0]);

if ([char]::IsLetter(\$c) -and !\$d.ContainsKey(\$c))
{
\$p | Add-Member -MemberType NoteProperty -Name Property -Value \$p.Name
\$p | Add-Member -MemberType NoteProperty -Name Win32Class -Value \$w.Name
\$d[\$c] = \$p
break; // Lets only get one property from a given class
}
}
if (\$d.Count -eq 25) { break; }
}

\$d.Values | sort Property | Select Property, Win32Class```
```\$v = get-content c:\scripts\lettercase.txt
\$t=""
\$v.ToCharArray() | % {
if ([char]::IsUpper(\$_)) { \$t += [char]::ToLower(\$_) } else {
if ([char]::IsLower(\$_)) { \$t += [char]::ToUpper(\$_) } else {
if ([char]::IsDigit(\$_)) { \$t += [int][char]\$_-49 } else {
\$t += \$_;
} } }
}
\$t```
```\$val = "{0:F2}" -f ([Math]::Sqrt([Math]::Pow([Math]::Floor((100 * 2) / 30), 5) * 4) / 45)
\$voice = new-object -com SAPI.SpVoice
[void]\$voice.Speak(\$val)```
```Value               LDAP Display Name
Kenneth             givenName
J                   initials
Myer                sn
Redmond             l
WA                  st
98052               postalCode
USA                 c
1-425-555-1234      telephoneNumber
kmyer@fabrikam.com  mail```
`(Get-Content c:\scripts\symbols.txt) -replace "[^\da-z ]",""`

Event 10: Triple Word Scramble
Official Solution
My Solution:

```.CSRIIEHRRTOENSWWPOCTIHPT-T

Wscript.Echo
Write-Host
Print```