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

No Comments