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.
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
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."
}
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