HiveBrain v1.2.0
Get Started
← Back to all entries
patternshellMinor

Advent of Code Day 6: toggling lights in a grid with Powershell

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
adventgridwithtogglingpowershelllightscodeday

Problem

I am using the "Advent of Code" series to help with my PowerShell education. The Day 6 puzzle has a 1000 × 1000 grid of lights. After processing instructions to turn on, turn off, or toggle rectangles of lights in the grid, how many lights are on at the end?

I am confident that this script will produce the right answer. However, it is going way too slow. I am interested in optimizing this script for performance. I am not against coming up with an alternative way to solve this problem with PowerShell.

``
$lightson = New-Object Collections.Generic.HashSet[string]
$strings = Get-Content C:\test\lights.txt

foreach ($string in $strings){
$numbers = [regex]::matches($string,"\d+")
$minX = [convert]::ToInt32($numbers[0],10)
$minY = [convert]::ToInt32($numbers[1],10)
$maxX = [convert]::ToInt32($numbers[2],10)
$maxY = [convert]::ToInt32($numbers[3],10)
$x = $minX
$y = $miny

write-host $string
write-host $x $y

if ($string -like "turn on*"){
write-host "turn on"
while ($x -le $maxX){
while ($y -le $maxY){
$light = "$x,$y"
if ($light -notin $lightson){
[void]$lightson.Add("$x,$y")
#write-host $lightson.Count
}
$y +=1
}
$x += 1
$y = $miny
}
write-host $lightson.Count "
n"
}

if ($string -like "turn off*"){
write-host "turn off"
while ($x -le $maxX){
while ($y -le $maxY){
$light = "$x,$y"
if ($light -in $lightson){
[void]$lightson.Remove("$x,$y")
#write-host $lightson.Count
}
$y +=1
}
$x += 1
$y = $miny
}
write-host $lightson.Count "n"
}

if ($string -like "toggle*"){
write-host "toggle
n"

Solution

I assume the Write-Host commands are to view progress/status of the script? I would remove these if you are confident the script is reliable and accurate. Maybe one status (write-host $lightson.Count) after every instruction line.

When you add a light to the list, you don't need to check if it's already in the list. Add will return True/False for its success. Either way, you won't need to check before or after. Just know that if it was on the list already, it will simply return False and carry on. Likewise when removing it from the list.

while ($x -le $maxX){
    while ($y -le $maxY){
        $light = "$x,$y"
        [void]$lightson.Add("$x,$y")
        $y +=1
    }
    $x += 1
    $y = $miny
}


You will also no longer need $light.

When toggling a light, you can again use the return value of Add or Remove:

while ($x -le $maxX){
    while ($y -le $maxY){
        If ($lightson.Remove("$x,$y")) { }
        Else {[void]$lightson.Add("$x,$y")}
        $y +=1
    }
    $x += 1
    $y = $miny
}


Every loop of ForEach is evaluating all three If conditions. You can combine these into an If/ElseIf/ElseIf so subsequent conditions are not evaluated when the first has already succeeded.

For slightly less verbose loops, you could change While loops to For loops. This didn't make a large difference in times though.

I entered the number into the website you linked, and your algorithm did give the correct answer.

$lightson = New-Object Collections.Generic.HashSet[string]
$strings = Get-Content C:\test\lights.txt

foreach ($string in $strings){
    $numbers = [regex]::matches($string,"\d+")
    $minX = [convert]::ToInt32($numbers[0],10)
    $minY = [convert]::ToInt32($numbers[1],10)
    $maxX = [convert]::ToInt32($numbers[2],10)
    $maxY = [convert]::ToInt32($numbers[3],10)

    if ($string -like "turn on*"){
        For ($x = $minX; $x -le $maxX; $x++) {
            For ($y = $minY; $y -le $maxY; $y++) {
                [void]$lightson.Add("$x,$y")
            }
        }
    } ElseIf ($string -like "turn off*"){
        For ($x = $minX; $x -le $maxX; $x++) {
            For ($y = $minY; $y -le $maxY; $y++) {
                [void]$lightson.Remove("$x,$y")
            }
        }
    } ElseIf ($string -like "toggle*"){
        For ($x = $minX; $x -le $maxX; $x++) {
            For ($y = $minY; $y -le $maxY; $y++) {
                If ($lightson.Remove("$x,$y")) { }
                Else {[void]$lightson.Add("$x,$y")}
            }
        }
    }
    write-host $lightson.Count
}

Code Snippets

while ($x -le $maxX){
    while ($y -le $maxY){
        $light = "$x,$y"
        [void]$lightson.Add("$x,$y")
        $y +=1
    }
    $x += 1
    $y = $miny
}
while ($x -le $maxX){
    while ($y -le $maxY){
        If ($lightson.Remove("$x,$y")) { }
        Else {[void]$lightson.Add("$x,$y")}
        $y +=1
    }
    $x += 1
    $y = $miny
}
$lightson = New-Object Collections.Generic.HashSet[string]
$strings = Get-Content C:\test\lights.txt

foreach ($string in $strings){
    $numbers = [regex]::matches($string,"\d+")
    $minX = [convert]::ToInt32($numbers[0],10)
    $minY = [convert]::ToInt32($numbers[1],10)
    $maxX = [convert]::ToInt32($numbers[2],10)
    $maxY = [convert]::ToInt32($numbers[3],10)

    if ($string -like "turn on*"){
        For ($x = $minX; $x -le $maxX; $x++) {
            For ($y = $minY; $y -le $maxY; $y++) {
                [void]$lightson.Add("$x,$y")
            }
        }
    } ElseIf ($string -like "turn off*"){
        For ($x = $minX; $x -le $maxX; $x++) {
            For ($y = $minY; $y -le $maxY; $y++) {
                [void]$lightson.Remove("$x,$y")
            }
        }
    } ElseIf ($string -like "toggle*"){
        For ($x = $minX; $x -le $maxX; $x++) {
            For ($y = $minY; $y -le $maxY; $y++) {
                If ($lightson.Remove("$x,$y")) { }
                Else {[void]$lightson.Add("$x,$y")}
            }
        }
    }
    write-host $lightson.Count
}

Context

StackExchange Code Review Q#126796, answer score: 3

Revisions (0)

No revisions yet.