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--; } $ts = $d - $t.AddMonths($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 : " [int]$guess = Read-Host 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 $adLockOptimistic = 3 $objConnection = New-Object -comobject ADODB.Connection $objRecordset = New-Object -comobject ADODB.Recordset $objConnection.Open("Provider=Microsoft.Jet.OLEDB.4.0; Data Source= c:\scripts\pool.mdb") $objRecordset.Open("Select * from SwimmingPool", $objConnection,$adOpenStatic,$adLockOptimistic) 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
Powershell advanced division
Event 1: Could I Get Your Phone Number?
Official Solution
My Solution:
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"); while ($word = $file.ReadLine()) { 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
Event 3: Instant (Runoff) Winner
Official Solution
My Solution:
$voters = new-object System.Collections.ArrayList; # Collect the votes from the file foreach($voteLine in (Get-Content "c:\scripts\votes.txt"))
{ $vote = new-object System.Collections.ArrayList; $vote.AddRange($voteLine -split ",") [void]$voters.Add($vote) } 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): " [DateTime]$month = (Read-Host) # Ensure we are set to the first day of the month $month = $month.AddDays(-1 * ($month.Day - 1)) # Write month header 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 $next = $month.AddDays(-1 * [int]$month.DayofWeek) 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) $next = $next.AddDays(1) } Write-Host } Write-Host
Event 5: You Call That a Strong Password?
Official Solution
My Solution:
param([string]$pass) $strength = 13 Write-Host "Testing password [$pass]..." $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" Write-Host " password."
Event 6: Prime Time
Official Solution
My Solution:
$primes = new-object System.Collections.ArrayList $primes.AddRange(2..199) 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))
Event 9: You’re Twisting My Words
Official Solution
My Solution:
((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 { Write-Host "`nYour cards:" $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)? " $hitOrStay = Read-Host 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
Event 1: Could I See Some ProgID, Please?
Official Solution
My Solution:
#-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 "ADODB.Connection 6" 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" Write-Host "Microsoft.ISAdm 13"
Event 2: Typing Isn’t Really Our Thing
Official Solution
My Solution:
$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
Event 5: WMI From A to Z – er, A to Y
Official Solution
My Solution:
$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
Event 6: If at First You Don’t Succeed...
Official Solution
My Solution:
$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
Event 7: Simon Says "Multiply By 4"
Official Solution
My Solution:
$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)
Event 8: The Display’s the Thing
Official Solution
My Solution:
Value LDAP Display Name Kenneth givenName J initials Myer sn 712 NE 40th Way streetAddress Redmond l WA st 98052 postalCode USA c 1-425-555-1234 telephoneNumber kmyer@fabrikam.com mail
Event 9: The Third Time Was Not a Charm
Official Solution
My Solution:
(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