function ss2 { param( [parameter()] [alias("WormLength")] [validaterange(1,40)] [int]$WL=10, [parameter()] [validaterange(10,1000)] [int]$STEP=50, [parameter()] [validatenotnullorempty()] [alias("CC")] [char]$CLEARCHAR=[char]" ", [parameter()] [alias("c")] [switch]$CLEAR, [parameter()] [alias("y")] [switch]$YELLOW, [parameter()] [alias("n","no")] [switch]$NOOVERLAP, [parameter()] [alias("m")] [switch]$MIDDLE ) # WL: Worm Length # STEP: Wait time in milliseconds between STEPs # CLEAR: $false (default): draw the original character back # $true: display $CLEARCHAR instead with as default. # # If $true, CLEAR the path after the worm. # The default is to draw the original character back. # # NOOVERLAP: By default the worm can overlap itself. If this switch # is specified, the worm will reset if it crashes into itself. # # $WL=10 # $STEP=50 # $CLEAR=$false # $CLEARCHAR=[char]" " # WC: Worm Character: the characters the worm consists of # This way the worm will be yellow. # if ($YELLOW) { $ESC=[char][int]27 $WC="$ESC[93m*$ESC[0m" } else { # Use this instead for an uncolored worm. # $WC=[char]"*" } # WINW: WINdow Width # WINH: WINdows Height # BUFW: BUFfer Width (on Linux, same as WINW) # BUFH: BUFfer Height (on Linux, same as WINH) # WINPOSX: WINdow POSition, X coordinate # WINPOSY: WINdow POSition, Y coordinate # $WINW=$host.ui.rawui.windowsize.width $WINH=$host.ui.rawui.windowsize.height $BUFW=$host.ui.rawui.buffersize.width $BUFH=$host.ui.rawui.buffersize.height $WINPOSX=$host.ui.rawui.windowposition.x $WINPOSY=$host.ui.rawui.windowposition.y # Save buffer content and cursor position and size for the restore phase. # Then the cursor will be hidden. # $BUFFER=$host.ui.rawui.getbuffercontents(@{top=0;bottom=$BUFH-1;left=0;right=$BUFW-1}) $CURX=$host.ui.rawui.cursorposition.x $CURY=$host.ui.rawui.cursorposition.y $CURS=$host.ui.rawui.cursorsize $host.ui.rawui.cursorsize=0 # (STARTX,STARTY): Starting coordinates. # Depending of the variable MIDDLE, it is either the coordinate of # the middle of the window, or a random starting point. # STARTC: The original character at the middle of the window # if ($MIDDLE) { $STARTX=[int]($WINPOSX+$WINW/2) $STARTY=[int]($WINPOSY+$WINH/2) } else { $STARTX=$WINPOSX+(get-random -minimum 0 -maximum $WINW+1) $STARTY=$WINPOSY+(get-random -minimum 0 -maximum $WINH+1) } $STARTC=$host.ui.rawui.getbuffercontents(@{top=$STARTY;bottom=$STARTY;left=$STARTX;right=$STARTX}) # Let's define the four move matrices. # $UP=@{X=0;Y=-1} $DOWN=@{X=0;Y=1} $LEFT=@{X=-1;Y=0} $RIGHT=@{X=1;Y=0} # This is where the execution jumps back on worm reset. # It will happen if: # - either the worm can't move any further, # - or if button "r" is pressed. # :label1 while ($true) { # PA: Positional Array # This way dimension of PA can vary according to Worm Length. # PA is an array of hash tables, where the values of the hash table are: # X: position X; Y: position Y; C: original Character at (X,Y). # $PA=[System.Collections.Generic.List[hashtable]]::new() # Fill the Positional Array with its initial value(s). # All segments start in the middle of the screen. # for ($n=0;$n -le $WL-1;$n++) { $PA.add(@{X=$STARTX;Y=$STARTY;C=$STARTC}) } # PREVIOUS: the previous direction. We need this for the random direction # generation later so that the worm continues its original direction # rather than change it. # $PREVIOUS=$null # Display the worm's head (i.e. the first character). # $host.ui.rawui.cursorposition=@{X=$PA[0].X;Y=$PA[0].Y} [console]::write($WC) # Here starts the block performing the actual move. # :label2 while ($true) { # Check for key press. # if (test-path variable:KEYPRESSED) { remove-variable KEYPRESSED } if ([console]::keyavailable) { $KEYPRESSED=[console]::readkey($true).keychar } # PM: Possible Moves # PM is an array containing the possible moves (if any). # $PM=[System.Collections.Generic.List[hashtable]]::new() # Can we go up? # If we are at the top row, we remain there, there is no wrap. # $CANGOUP=$true $NEWX=$PA[0].X+$UP.X if ($PA[0].Y -eq $WINPOSY) { $CANGOUP=$false } else { $NEWY=$PA[0].Y+$UP.Y } if ($NOOVERLAP) { for ($n=1;$n -le $WL-1;$n++) { if ($PA[$n].X -eq $NEWX -and $PA[$n].Y -eq $NEWY) { $CANGOUP=$false } } } if ($CANGOUP) { $PM.add(@{X=$NEWX;Y=$NEWY}) if ($PREVIOUS -eq "UP") { $PM.add(@{X=$NEWX;Y=$NEWY}) } } # Can we go down? # If we are at the bottom row, we remain there, there is no wrap. # $CANGODOWN=$true $NEWX=$PA[0].X+$DOWN.X if ($PA[0].Y -eq $WINPOSY+$WINH-1) { $CANGODOWN=$false } else { $NEWY=$PA[0].Y+$DOWN.Y } if ($NOOVERLAP) { for ($n=1;$n -le $WL-1;$n++) { if ($PA[$n].X -eq $NEWX -and $PA[$n].Y -eq $NEWY) { $CANGODOWN=$false } } } if ($CANGODOWN) { $PM.add(@{X=$NEWX;Y=$NEWY}) if ($PREVIOUS -eq "DOWN") { $PM.add(@{X=$NEWX;Y=$NEWY}) } } # Can we go left? # If we are at the leftmost column, we remain there, there is no wrap. # $CANGOLEFT=$true if ($PA[0].X -eq $WINPOSX) { $CANGOLEFT=$false } else { $NEWX=$PA[0].X+$LEFT.X } $NEWY=$PA[0].Y+$LEFT.Y if ($NOOVERLAP) { for ($n=1;$n -le $WL-1;$n++) { if ($PA[$n].X -eq $NEWX -and $PA[$n].Y -eq $NEWY) { $CANGOLEFT=$false } } } if ($CANGOLEFT) { $PM.add(@{X=$NEWX;Y=$NEWY}) if ($PREVIOUS -eq "LEFT") { $PM.add(@{X=$NEWX;Y=$NEWY}) } } # Can we go right? # If we are at the rightmost column, we remain there, there is no wrap. # $CANGORIGHT=$true if ($PA[0].X -eq $WINPOSX+$WINW-1) { $CANGORIGHT=$false } else { $NEWX=$PA[0].X+$RIGHT.X } $NEWY=$PA[0].Y+$RIGHT.Y if ($NOOVERLAP) { for ($n=1;$n -le $WL-1;$n++) { if ($PA[$n].X -eq $NEWX -and $PA[$n].Y -eq $NEWY) { $CANGORIGHT=$false } } } if ($CANGORIGHT) { $PM.add(@{X=$NEWX;Y=$NEWY}) if ($PREVIOUS -eq "RIGHT") { $PM.add(@{X=$NEWX;Y=$NEWY}) } } if (-not ($CANGOUP -or $CANGODOWN -or $CANGOLEFT -or $CANGORIGHT)) { $PM=$null } # If we can't go anywhere (PM array is empty) or button 'r' was # pressed, the worm starts over from the middle of the window. # if ($PM -eq $null -or $KEYPRESSED -eq "r" -or $KEYPRESSED -eq "R") { # We either clear the path underneath the worm to be destroyed, # or redraw the original characters back depending of CLEAR. # if ($CLEAR) { for ($n=0;$n -le $WL-1;$n++) { $host.ui.rawui.cursorposition=@{x=$PA[$n].X;y=$PA[$n].Y} [console]::write($CLEARCHAR) } } else { for ($n=0;$n -le $WL-1;$n++) { $host.ui.rawui.setbuffercontents(@{x=$PA[$n].X;y=$PA[$n].Y},$PA[$n].C) } } if (test-path variable:KEYPRESSED) { remove-variable KEYPRESSED } break label2 } # If any other key than "r" or "R" was pressed, then exit. # if (test-path variable:KEYPRESSED) { break label1 } # Draw the worm with the new coordinates. # # First we remove the last segment. # if ($CLEAR) { $host.ui.rawui.cursorposition=@{x=$PA[$WL-1].X;y=$PA[$WL-1].Y} [console]::write($CLEARCHAR) } else { $host.ui.rawui.setbuffercontents(@{x=$PA[$WL-1].X;y=$PA[$WL-1].Y},$PA[$WL-1].C) } # As PM is not empty, let's pick one direction randomly. # $RNDDIR=($PM | get-random) # Then we shift the segments in the PA, and save the position of the # first segment and the original character at the given coordinates. # for ($n=$WL-1;$n -ge 1;$n--) { $PA[$n].X=$PA[$n-1].X $PA[$n].Y=$PA[$n-1].Y $PA[$n].C=$PA[$n-1].C } $PA[0].X=$RNDDIR.X $PA[0].Y=$RNDDIR.Y # Set PREVIOUS to the value according to where the new coordinates are. # if ($PA[0].X -lt $PA[1].X) { $PREVIOUS="LEFT" } elseif ($PA[0].X -gt $PA[1].X) { $PREVIOUS="RIGHT" } elseif ($PA[0].Y -lt $PA[1].Y) { $PREVIOUS="UP" } elseif ($PA[0].Y -gt $PA[1].Y) { $PREVIOUS="DOWN" } # If it is a non-overlapping worm, we simply need the original character # of the new coordinates. # if ($NOOVERLAP) { $PA[0].C=$host.ui.rawui.getbuffercontents(@{top=$PA[0].Y;bottom=$PA[0].Y;left=$PA[0].X;right=$PA[0].X}) } else { # If it is an overlapping worm, the original character might not be the # one in the new coordinates, but already a worm segment, in which case # we need the previously stored character. # for ($n=1;$n -le $WL-1;$n++) { if ($PA[$n].X -eq $PA[0].X -and $PA[$n].Y -eq $PA[0].Y) { $PA[0].C=$PA[$n].C # break label3 } } } # All but the first segment should still be in place, so it's enough # to draw the first character. (WC: Worm Character) # $host.ui.rawui.cursorposition=@{x=$PA[0].X;y=$PA[0].Y} [console]::write($WC) start-sleep -milliseconds $STEP # Here ends the block performing the actual move. # } # Here ends the first while loop. # } # Reset the terminal back to its original content # $host.ui.rawui.setbuffercontents(@{x=0;y=0},$BUFFER) $host.ui.rawui.cursorposition=@{x=$CURX;y=$CURY} $host.ui.rawui.cursorsize=$CURS }