functions/MiniGameSnake/Start-MiniGameSnake.ps1
|
function Start-MiniGameSnake { <# .SYNOPSIS Draws a snake game. Use UP,DOWN,LEFT,RIGHT Arrows or WASD for movement. .DESCRIPTION Draws a snake game. Use UP,DOWN,LEFT,RIGHT Arrows or WASD for movement. .OUTPUTS Moderate Snacks eaten: 5 Snake: 8 +------------------------------+ | | | O | | O | | O | | OOOOO@ * | | | | | | | | | | | +------------------------------+ NOTE: Requires Powershell 7 or higher .EXAMPLE .LINK #> [CmdletBinding( DefaultParameterSetName = "difficultyLevels" )] param ( [Parameter( Position = 0, Mandatory = $false, ParameterSetName = "difficultyLevels" )] [System.String] [ArgumentCompleter( { return @('Beginner', 'Moderate', 'Difficult', 'Challenge', 'Hardcore', 'Unbeatable') } )] [ValidateScript( { $_ -in @('Beginner', 'Moderate', 'Difficult', 'Challenge', 'Hardcore', 'Unbeatable') }, ErrorMessage = "Valid diffculites: 'Beginner', 'Moderate', 'Difficult', 'Challenge', 'Hardcore', 'Unbeatable" )] $Difficulty = "Moderate", [Parameter( Position = 1, Mandatory = $false )] [switch] $Mute, [Parameter( Position = 2, Mandatory = $false )] [ValidateScript( { $_ -match '\d+x\d+' }, ErrorMessage = "Must match format '<Widht>x<Height>' e.g. 30x10." )] [System.String] $Dimension = "30x10", [Parameter( Position = 3, Mandatory = $false )] [switch] $MaxDimension, # The initial snake length. [Parameter( Position = 4, Mandatory = $false )] [System.Int32] $SnakeLength = 3, [Parameter( Position = 5, ParameterSetName = 'customTicks' )] [System.Int32] [ValidateRange(1, 1000)] $CustomTicks ) $GameHeight = [System.Int32]::Parse($Dimension.split('x')[1]) $GameWidth = [System.Int32]::Parse($Dimension.split('x')[0]) $WindowHeight = $Host.UI.RawUI.BufferSize.Height $WindowWidth = $Host.UI.RawUI.BufferSize.Width if ($GameWidth -LT 2) { throw [System.Exception]::new('The GameWidth must be at least 2') } if ($GameHeight -LT 2) { throw [System.Exception]::new('The GameHeight must be at least 2') } # Game Height # + 1 empty line above # + 2 lines of text above # + 2 lines (Upper/Lower Wall) # + 2 lines of text below $RequiredHeight = $GameHeight + 1 + 2 + 2 + 2 # Game Width # + 2 Walls $RequiredWidth = $GameWidth + 2 if ($MaxDimension.IsPresent) { $RequiredHeight -= $GameHeight $RequiredWidth -= $GameWidth $GameHeight = $WindowHeight - $RequiredHeight - 2 $GameWidth = $WindowWidth - $RequiredWidth - 2 $RequiredHeight += $GameHeight $RequiredWidth += $GameWidth } if ($WindowWidth -LT $RequiredWidth) { throw [System.Exception]::new("Terminal Width must be at least: $RequiredWidth (Currently: $WindowWidth)") } if ($WindowHeight -LT $RequiredHeight) { throw [System.Exception]::new("Terminal Height must be at least: $RequiredHeight (Currently: $WindowHeight)") } ######################################################## ###### Setting up the game loop $GameOffsetLength = [System.Math]::floor($WindowWidth / 2 - $GameWidth / 2) $gameSettings = @{ MinimumX = $GameOffsetLength MinimumY = 0 MaximumX = $RequiredWidth + $GameOffsetLength MaximumY = $RequiredHeight Mute = $Mute.IsPresent Load = $PSScriptRoot Vars = @{ isWindows = $IsWindows gameHeight = $gameHeight gameWidth = $gameWidth # Initial snake position is the center of the screen. snake = $null snakeLength = [System.Int32]$SnakeLength snakeSnack = $null snackedSnakeSnacks = 0 # Velocity is overwritte to next velocity on every loop. # This is to avoid invalid velocities when the user presses the arrows to fast, # before the call of the loop function. velocity = [System.Numerics.Vector2]::new(0, 1) nextVelocity = [System.Numerics.Vector2]::new(0, 1) collisionMap = [System.Collections.Hashtable]::new() } } $game = New-MiniGame @gameSettings $game.Set('difficulty', $game.Get('difficultyMapping')[$Difficulty]) $game.ticks($game.Get('difficulty').ticks ?? $CustomTicks) $game.alias([System.ConsoleKey]::A, [System.ConsoleKey]::LeftArrow) $game.alias([System.ConsoleKey]::D, [System.ConsoleKey]::RightArrow) $game.alias([System.ConsoleKey]::W, [System.ConsoleKey]::UpArrow) $game.alias([System.ConsoleKey]::S, [System.ConsoleKey]::DownArrow) $game.OnKey([System.ConsoleKey]::A, { param($key, $var, $func, $canvas, $sound, $object, $stop) if ($var.velocity.X -EQ 0) { # Only accept if the snake is not moving to the right $var.nextVelocity = [System.Numerics.Vector2]::new(-1, 0) } }) $game.OnKey([System.ConsoleKey]::D, { param($key, $var, $func, $canvas, $sound, $object, $stop) if ($var.velocity.X -EQ 0) { # Only accept if the snake is not moving to the left $var.nextVelocity = [System.Numerics.Vector2]::new(1, 0) } }) $game.OnKey([System.ConsoleKey]::W, { param($key, $var, $func, $canvas, $sound, $object, $stop) if ($var.velocity.Y -EQ 0) { # Only accept if the snake is not moving down $var.nextVelocity = [System.Numerics.Vector2]::new(0, -1) } }) $game.OnKey([System.ConsoleKey]::S, { param($key, $var, $func, $canvas, $sound, $object, $stop) if ($var.velocity.Y -EQ 0) { # Only accept if the snake is not moving up $var.nextVelocity = [System.Numerics.Vector2]::new(0, 1) } }) $game.Start() } |