For last week's Friday Fun, I posted a PowerShell script to create a traditional Bingo card. I hoped you would also learn a few PowerShell concepts along the way. This week I've taken this to the next level, and have a complete module that not only creates the card but also let's you play the game. There's a lot going on here with regular expressions, custom view PS1XML files, variable scope and more.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
There's so much here I'm not even sure where to begin. I hope when you download the module you'll take some time to go through the different script files to understand how all the pieces work. I've tried to comment alot to help, as I recommend you do in your scripts. But let me hit a few key PowerShell concepts and techniques.
First off, I now have a function to generate a Bingo number, using the traditional Bingo values. The Get-BingoNumber function breaks a few rules which is why I want you to understand. The challenge was to ensure that every number drawn was unique and had not been played before which meant I needed some mechanism to keep track of the draw history. Normally I would use a variable array. But everytime I call the function, I would end up with a new scope and new variables. This is a situation where specifying a scope comes in handy.
The Get-BingoNumber function creates a variable, $Master, in the global scope of all possible values.
[cc lang="PowerShell"]
#if no global variable for $master is found then create it
if (-Not $global:master)
{
#generate unique arrays of numbers for each letter,
#create a letter/number combination
#and then randomize again for good measure
$B=1..15 | Get-Random -count 15 | foreach {write "B$_"} | Get-Random -count 15
$I=16..30 | Get-Random -count 15 | foreach {write "I$_"} | Get-Random -count 15
$N=31..45 | Get-Random -count 15 | foreach {write "N$_"} | Get-Random -count 15
$G=46..60 | Get-Random -count 15 | foreach {write "G$_"} | Get-Random -count 15
$O=61..75 | Get-Random -count 15 | foreach {write "O$_"} | Get-Random -count 15
#build a master array of all possible numbers
$global:master=$B+$I+$N+$G+$O
}
[/cc]
The function then draws a random element from this variable.
[cc lang="PowerShell"]
#create a randomized array of $master if it doesn't already exist
if (-not $global:draw)
{
$global:draw= $global:master | Get-Random -count $global:master.count
}
#Draw each number from $draw starting at the beginning of the array
Write-Output $global:draw[0]
#remove the first element from $draw
$global:draw=$global:draw[1..$($global:draw.count)]
[/cc]
This is the value that is written to the pipeline. Without specifying the global scope I would get new variables every time. Using a variable with the scope prefix let's PowerShell know I understand what I am doing.
For the Bingo card object, I realized I didn't have to rely on Format-Table or tweaking the output. I could use a custom format file.
[cc lang="PowerShell"]
[/cc]
When this file is loaded from the module, or using Update-FormatData, my bingo card objects are properly formatted. But there was one extra trick I needed for this to work. Originally I was creating the bingo card using New-Object.
[cc lang="PowerShell"]
$obj=New-Object -TypeName PSObject -Property @{
B=$b[$x]
I=$i[$x]
N=$n[$x]
G=$g[$x]
O=$o[$x]
}
[/cc]
But this meant the object type was a PSCustomObject so my original version of my format ps1xml file applied to all custom objects as long as the module was loaded. The trick was to assign a new object type to this object.
[cc lang="PowerShell"]
#add a custom type name to this object for formatting
$obj.psobject.Typenames[0]="Bingo.Card"
[/cc]
You'll notice that this type name matches the type name in the ps1xml file. Now whenever I write a card to the pipeline it is automatically formatted the way I want.
When it comes to playing the game, I wrote a new function called Invoke-Bingo which has an alias of Play-Bingo (since Play is not a valid verb but it is OK for an alias). The function uses a number of parameter sets because you can play 3 different ways. In the default or Normal manner a new card is written to your console and a prompt awaits your first number. At the same time a second PowerShell window is open that loads the module and launches the Get-PowerShellNumber function in Prompt mode. You get numbers from one session and enter them in the other.
Because the game displays winning numbers (look at the Test-Bingo.ps1 file to see how), I wanted to make sure only valid numbers were entered. So I used a set of regular expressions to validate.
[cc lang="PowerShell"]
#use regular expression to make sure a valid call was made
Switch -regex ($call) {
"^[bB]([1-9]|[1][0-5])$" {$valid=$True}
"^[iI]([1][5-9]|[2][0-9]|[3][0])$" {$valid=$True}
"^[nN]([3][1-9]|[4][0-5])$" {$valid=$True}
"^[gG]([4][6-9]|[5][0-9]|[6][0])$" {$valid=$True}
"^[oO]([6][1-9]|[7][0-5])$" {$valid=$True}
Default {
$valid=$False
Write-Warning "$Call is an invalid option. Please try again."
}
} #Switch
[/cc]
You can also use -CardOnly to skip the draw session. Use this if you are playing along with your co-workers and someone else is drawing numbers. By the way, the free space is now randomly inserted and you can have up to 3. Use -Free when playing.
Lastly, I also created an AutoPlay mode. that automatically draws a number and updates the card whenever there is a match. Play continues until a Bingo is made. You can specify a sleep interval between draws. The default is 3 with a minimum of 1. Here's a short 30 second clip of an automatic game. The game itself took a little over a minute.
[kml_flashembed publishmethod="static" fversion="8.0.0" movie="https://jdhitsolutions.com/blog/wp-content/uploads/2011/02/PowerShellBingo.swf" width="640" height="374" targetclass="flashmovie" play="false" quality="high" base="https://jdhitsolutions.com/blog/wp-content/uploads/2011/02/" fvars="autostart=false;thumb=FirstFrame.png;thumbscale=45;color=0x1A1A1A,0x1A1A1A"]
[/kml_flashembed]
Download thePowerShellBingo module. Extract the folder to Documents\WindowsPowerShell\Modules and then import the PowerShellBingo module. Enjoy, and I hope you learn a few things along the way.
1 thought on “Friday Fun – More PowerShell Bingo”
Comments are closed.