How To Create Video Games Using PowerShell


Just in case you thought PowerShell was just for automating administrative tasks, Jeff Adkin, a Microsoft Premier Field Engineer, shows us how to have some fun with PowerShell by providing us with a detailed walkthrough of creating a video game. Here’s a short video showing the game in action:

Enjoy Jeff’s article!


Part 1: Designing the Game

Where it started:

I was teaching Windows PowerShell courses in for SharePoint and needed a way to show how impressive PowerShell was as well as ensure that the knowledge I shared was retained. I also wanted everyone to have a little fun at the end and get excited about what they can do with PowerShell. When I added all of these up I came up with the idea of creating a game like ‘Who Wants to be a Millionaire’. Now I wanted this to function exactly like a regular graphical game not some text game from the 80’s. So I grabbed a pen and paper and began designing the game.

Design Phase:

I needed to know what pieces I would need to do this so I drew up what I wanted it to look like.

Game Mockup

That made it easier to understand and it split the GUI into 4 sections for me. Now what did I need in each section…

BackGround:

This is the easiest section. It is just a jpg background that everything else was built on.

Needs:

1 Form, 1 JPG

Assistance Section:

Assistance Section

Here we had 4 selections: 50/50, Call, Audience, and Walk Away. When one of these is selected I knew that a red X should be placed over it and that it could not be selected anymore. I decided to leave the graphics of the selections as part of the background and use 4 transparent picture boxes as hot spot over these. The reasoning was that if a picture box was selected I would fill in the red X and then use that as my determining factor if it could be selected (i.e. if there's no red X then you can click, if picturebox has red X then no clicking permitted).

Needs:

4 Pictureboxes, 1 red x jpg

Money Section:

Money Section

We all need to see how much we are winning. I decided that I can manipulate things better if I use a label for each $ amount. This way I could highlight the amount of money you were playing to win.

Needs:

15 labels

Question/Answer section:

Question and Answer Section

In this section we will use labels again. 4 for the answers and 1 for the question. We would also need a way to import the questions and answers. I decided that the best way for this was to use a CSV file. I decided to setup the CSV sections as follows:

  • Question, Answer A, Answer B, Answer C, Answer D, The Answer, Take Away 1, Take Away 2

This gave me the ability to import the questions, possible answers, the real answer and even what to take away if the player clicks 50/50.

One additional thing needed was how to setup the questions so it would ask easy ones first and then hard ones at the end. I decided that I would format the CSV to have the first 30 questions as easy, the next 30 as medium, and the last 30 as hard. I would then set the RND values in the game to pull only from the section.

  • 0-30 Easy; 31-60 medium; 61-90 hard

The hard part here was finding SharePoint PowerShell questions to ask.

Conclusion:

That finished the design phase of the main area of the game, so now that I knew what I needed it was time to sit down, open notepad and start typing. In Part 2 we will start by creating the form and background image needed to have our GUI.


Part 2: Creating the GUI in PowerShell

 

Intro:

With the design phase out of the way it was time to start creating the GUI. The first thing I had to do was create the background image and decide what size it was going to be so we could create the form to host it. The initial background image was 1065X800 so this would have to be the size of my form.

Creating the Form:

 

First things first. Before we can create a form we need to add a couple assemblies right at the beginning of our script.

Windows Forms: Allows us to create the form.

 [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

System Drawing: Allows us to draw it to screen.

 [void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

Next we need to create our form and properties.

 

 $Main = New-Object System.Windows.Forms.Form 
 
 $Main.ClientSize = New-Object System.Drawing.Size(1065, 800) 
 
 $Main.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedToolWindow 
 
 $Main.Text = "Who Wants to be a Millionaire - PowerShell Edition" 
 

We create our object, set the drawing size, set the border to fixed so it cannot be re-sized and lastly add the text of the window. We will not set the form be to shown until the end after we have added all the controls to it. Now we need to add the background image to the form.

Background Image:

One of the things I wanted was a self-contained script. This means that everything will be in the PS1 file including images. This means that everything has to be text based. Email has the exact same issue where everything has to be text based and to bypass this email uses Base64 encoding to convert bytes to text. This seemed like a great answer to ensuring everything is in the script.

Converting the image to Base64:

I created a quick function to do the conversion for me:

 Function ConvertJPG($picture){ 
 
 [byte[]]$Pic = Get-Content $Picture -Encoding Byte 
 
 [system.convert]::ToBase64String($Pic)} 
 

This function is not used in the script, it was used only to do the conversion of the picture.

 PS C:\>ConvertJPG Background.jpg > BackGround.txt

The output in the text file will look like the following:

 OHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuXMGPKnElQQgAFNHPq1DgggE8IO 
 
 lOMNrE6Rcq1LMOrTQ1c7CkWK06zcAkuaEuhYtG2bbXG5eoV7wCLCPC2JbsXKlrBDi4WECz2bWGh 
 
 dEsFpwMMmOARQuwAmF2jrBw7cUjQoUfrDkmBLWuTqzk7Hu7xQGzhJG3H1ss849TYuVVGDl4dY/H 
 
 5XvL++wOhZ5l9OfEWH4ACbRdaBFvRx5177J3GAFz9MfafaxUKllpc0h3omoOh0ZYZiANmZmBoEw 
 
 8rtlWeTt/pN2RBM3JGHU3O1bckQjwy5uNKQHJW45QFFdlYTPmFdiSXBjVp2ZMpCdgjmQ9VKdiVI 
 
 Gd0FmrRnU6NadGfjPHpaEaUChboRJCmdWlHN5YIWGw5fvpRp0ZJupADt1VlKkgnOl8J0YuWHfAq 
 
 KtCgbz4rXmwE3LaltSklexteqnKbUrTfHiVuTsEK1ui5M3m7J7tCkctYjPDqJK9gCdS7E6vl/v+ 
 
 phkvxRHLSWJC7mm4M1m2q3ivWwCIfZPFgDWWcMkSLWrbuQiDjtenLA2WKl6URmYwVygmnS69ELu 
 
 3HPh2aqyVpbS67VONlq0pFP2uwZX+15DVWYLO5MGPGvib1pRArSVPbda58clA6N01mx7IOZbZoU 
 
 FyHtsU1c5lXa5blTZHdXbotDpV2W2sDaLpl2+r0tmS+Ltn6xULdjViftOu8FCZntU9ZuFibtzS5 
 
 L27yIY3vFNb/wm5U74rFNnTC6Ku/0PViERHO4DepVhmNIPbzF0XSlbsDEgR9+1vI7xgTPAcaBFU 
 
 qbIiZqucQsbEohhUB0URu5z8cXvApEqGAzXy4tIr8jn5EZImnkhiTqQxAaUyUCQyjSMUpBQQAOw 
 

Now we add this to the script as a variable.

 $BackGround = “OHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuXMGPKnElQQgAFNHPq1DgggE8IOlOMNrE6Rcq1LMOrTQ1c7CkWK06zcAkuaEuhYtG2bbXG5eoV7wCLCPC2JbsXKlrBDi4WECz2bWGhdEsFpwMMmOARQuwAmF2jrBw7cUjQoUfrDkmBLWuTqzk7Hu7xQGzhJG3H1ss849TYuVVGD…” 
 

*NOTE- This is not the full Base64 of the image, just an example.

Using this we add the $BackGround picture to our Form.

 $Main.Image = ([System.Drawing.Image]([System.Drawing.Image]::FromStream((New-Object System.IO.MemoryStream(($$ = [System.Convert]::FromBase64String($BackGround)),0,$$.Length)))))

Note: This method can add quite a few additional lines to your script but does ensure everything is self-contained.

Now we have the Form and Background added we need to add some controls as per our design.

Assistance Section Controls:

In our design we decided that we needed 4 Picture Boxes for this section. One for each of the selections a user can make. We did not want a picture in the box until such time as the user clicked it and then a Red X would appear.

Setting up the picture boxes:

 

Fifty/Fifty

 $50X = New-Object System.Windows.Forms.PictureBox 
 
 $50X.Location = New-Object System.Drawing.Point(32, 20) 
 
 $50X.Size = New-Object System.Drawing.Size(195, 117) 
 
 $50X.TabIndex = 5 
 
 $50X.TabStop = $false 
 
 $50X.Text = "" 
 
 $50X.BackColor = [System.Drawing.Color]::Transparent 
 
 $50x.add_Click({FiftyXClick($50X)}) 
 

Phone

 $PHX = New-Object System.Windows.Forms.PictureBox 
 
 $PHX.Location = New-Object System.Drawing.Point(32, 139) 
 
 $PHX.Size = New-Object System.Drawing.Size(194, 121) 
 
 $PHX.TabIndex = 6 
 
 $PHX.TabStop = $false 
 
 $PHX.Text = "" 
 
 $PHX.BackColor = [System.Drawing.Color]::Transparent 
 
 $PHx.add_Click({PHXClick($PHX)}) 
 

Audience

 $Audience = New-Object System.Windows.Forms.PictureBox 
 
 $Audience.Location = New-Object System.Drawing.Point(33, 261) 
 
 $Audience.Size = New-Object System.Drawing.Size(193, 116) 
 
 $Audience.TabIndex = 27 
 
 $Audience.TabStop = $false 
 
 $Audience.Text = "" 
 
 $Audience.BackColor = [System.Drawing.Color]::Transparent 
 
 $Audience.add_Click({AudienceClick($Audience)}) 
 

WalkAway

 $WalkAway = New-Object System.Windows.Forms.PictureBox 
 
 $WalkAway.Location = New-Object System.Drawing.Point(31, 382) 
 
 $WalkAway.Size = New-Object System.Drawing.Size(193, 115) 
 
 $WalkAway.TabIndex = 28 
 
 $WalkAway.TabStop = $false 
 
 $WalkAway.Text = "" 
 
 $WalkAway.BackColor = [System.Drawing.Color]::Transparent 
 
 $WalkAway.add_Click({WalkAwayClick($WalkAway)}) 
 

The size and location of each of the picture boxes corresponds to the same location as the buttons on the background. These picture boxes are transparent and used as a hotspot as well as holding the Red X. You will notice each of the picture boxes are clickable and call a function. Instead of putting the code directly in the add_click this allows us to put it in a function to be a little cleaner.

Question and Answers Controls:

In our design we used 1 Question label and 4 Answer labels. The question label does not need to be clickable but all the Answer labels do need to be.

Adding our Labels:

Answer D

 $D = New-Object System.Windows.Forms.Label 
 
 $D.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 18.0, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $D.Location = New-Object System.Drawing.Point(640, 722) 
 
 $D.Size = New-Object System.Drawing.Size(293, 24) 
 
 $D.TabIndex = 4 
 
 $D.Text = "" 
 
 $D.BackColor = [System.Drawing.Color]::Transparent 
 
 $D.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 
 $D.add_Click({DClick($D)}) 
 

Answer C

 $C = New-Object System.Windows.Forms.Label 
 
 $C.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 18.0, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C.Location = New-Object System.Drawing.Point(173, 722) 
 
 $C.Size = New-Object System.Drawing.Size(307, 25) 
 
 $C.TabIndex = 3 
 
 $C.Text = "" 
 
 $C.BackColor = [System.Drawing.Color]::Transparent 
 
 $C.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 
 $C.add_Click({CClick($C)}) 
 

Answer B

 $B = New-Object System.Windows.Forms.Label 
 
 $B.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 18.0, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $B.Location = New-Object System.Drawing.Point(640, 669) 
 
 $B.Size = New-Object System.Drawing.Size(293, 25) 
 
 $B.TabIndex = 2 
 
 $B.Text = "" 
 
 $B.BackColor = [System.Drawing.Color]::Transparent 
 
 $B.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 
 $B.add_Click({BClick($B)}) 
 

Answer A

 $A = New-Object System.Windows.Forms.Label 
 
 $A.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 18.0, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $A.Location = New-Object System.Drawing.Point(173, 669) 
 
 $A.Size = New-Object System.Drawing.Size(307, 26) 
 
 $A.TabIndex = 1 
 
 $A.Text = "" 
 
 $A.BackColor = [System.Drawing.Color]::Transparent 
 
 $A.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 
 $A.add_Click({AClick($A)}) 
 

Question

 $Question = New-Object System.Windows.Forms.Label 
 
 $Question.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 18.0, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $Question.Location = New-Object System.Drawing.Point(143, 573) 
 
 $Question.Size = New-Object System.Drawing.Size(767, 64) 
 
 $Question.TabIndex = 0 
 
 $Question.Text = "” 
 
 $Question.BackColor = [System.Drawing.Color]::Transparent 
 
 $Question.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

Each label Location and Size matches the background image design. Since the answers will need to be clickable we have added add_click to each selection.

Money Section:

The money section will consist of 15 labels (yes, that's a lot). One label for each $$ amount. This section will show how well the player is progressing forward. For reference I will be listing all 15 below. You will notice that $C5, $C10 and $C15 have a different Forecolor to show users where the rungs in the ladder are (i.e.when you pass $1000 you are guaranteed $1000 even if you answer wrong).

#C1

 $C1 = New-Object System.Windows.Forms.Label 
 
 $C1.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C1.Location = New-Object System.Drawing.Point(830, 443) 
 
 $C1.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C1.TabIndex = 20 
 
 $C1.Text = " 1 $st"+"100" 
 
 $C1.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](128)))), ([System.Int32](([System.Byte](0))))) 
 
 $C1.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C2

 $C2 = New-Object System.Windows.Forms.Label 
 
 $C2.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C2.Location = New-Object System.Drawing.Point(830, 416) 
 
 $C2.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C2.TabIndex = 19 
 
 $C2.Text = " 2 $st"+"200" 
 
 $C2.BackColor = [System.Drawing.Color]::Transparent 
 
 $C2.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C3

 $C3 = New-Object System.Windows.Forms.Label 
 
 $C3.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C3.Location = New-Object System.Drawing.Point(830, 389) 
 
 $C3.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C3.TabIndex = 18 
 
 $C3.Text = " 3 $st"+"300" 
 
 $C3.BackColor = [System.Drawing.Color]::Transparent 
 
 $C3.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C4

 $C4 = New-Object System.Windows.Forms.Label 
 
 $C4.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C4.Location = New-Object System.Drawing.Point(830, 362) 
 
 $C4.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C4.TabIndex = 17 
 
 $C4.Text = " 4 $st"+"500" 
 
 $C4.BackColor = [System.Drawing.Color]::Transparent 
 
 $C4.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C5

 $C5 = New-Object System.Windows.Forms.Label 
 
 $C5.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C5.Location = New-Object System.Drawing.Point(830, 335) 
 
 $C5.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C5.TabIndex = 16 
 
 $C5.Text = " 5 $st"+"1,000" 
 
 $C5.BackColor = [System.Drawing.Color]::Transparent 
 
 $C5.ForeColor = [System.Drawing.Color]::DarkOrange 
 

#C6

 $C6 = New-Object System.Windows.Forms.Label 
 
 $C6.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C6.Location = New-Object System.Drawing.Point(830, 299) 
 
 $C6.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C6.TabIndex = 14 
 
 $C6.Text = " 6 $st"+"2,000" 
 
 $C6.BackColor = [System.Drawing.Color]::Transparent 
 
 $C6.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C7

 $C7 = New-Object System.Windows.Forms.Label 
 
 $C7.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C7.Location = New-Object System.Drawing.Point(830, 272) 
 
 $C7.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C7.TabIndex = 15 
 
 $C7.Text = " 7 $st"+"4,000" 
 
 $C7.BackColor = [System.Drawing.Color]::Transparent 
 
 $C7.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C8

 $C8 = New-Object System.Windows.Forms.Label 
 
 $C8.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C8.Location = New-Object System.Drawing.Point(830, 245) 
 
 $C8.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C8.TabIndex = 14 
 
 $C8.Text = " 8 $st"+"8,000" 
 
 $C8.BackColor = [System.Drawing.Color]::Transparent 
 
 $C8.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C9

 $C9 = New-Object System.Windows.Forms.Label 
 
 $C9.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C9.Location = New-Object System.Drawing.Point(830, 222) 
 
 $C9.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C9.TabIndex = 13 
 
 $C9.Text = " 9 $st"+"16,000" 
 
 $C9.BackColor = [System.Drawing.Color]::Transparent 
 
 $C9.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C10

 $C10 = New-Object System.Windows.Forms.Label 
 
 $C10.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C10.Location = New-Object System.Drawing.Point(830, 195) 
 
 $C10.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C10.TabIndex = 12 
 
 $C10.Text = "10 $st"+"32,000" 
 
 $C10.BackColor = [System.Drawing.Color]::Transparent 
 
 $C10.ForeColor = [System.Drawing.Color]::DarkOrange 
 

#C11

 $C11 = New-Object System.Windows.Forms.Label 
 
 $C11.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C11.Location = New-Object System.Drawing.Point(830, 155) 
 
 $C11.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C11.TabIndex = 11 
 
 $C11.Text = "11 $st"+"64,000" 
 
 $C11.BackColor = [System.Drawing.Color]::Transparent 
 
 $C11.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C12

 $C12 = New-Object System.Windows.Forms.Label 
 
 $C12.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C12.Location = New-Object System.Drawing.Point(830, 128) 
 
 $C12.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C12.TabIndex = 10 
 
 $C12.Text = "12 $st"+"125,000" 
 
 $C12.BackColor = [System.Drawing.Color]::Transparent 
 
 $C12.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C13

 $C13 = New-Object System.Windows.Forms.Label 
 
 $C13.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C13.Location = New-Object System.Drawing.Point(830, 101) 
 
 $C13.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C13.TabIndex = 9 
 
 $C13.Text = "13 $st"+"250,000" 
 
 $C13.BackColor = [System.Drawing.Color]::Transparent 
 
 $C13.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C14

 $C14 = New-Object System.Windows.Forms.Label 
 
 $C14.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C14.Location = New-Object System.Drawing.Point(830, 74) 
 
 $C14.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C14.TabIndex = 8 
 
 $C14.Text = "14 $st"+"500,000" 
 
 $C14.BackColor = [System.Drawing.Color]::Transparent 
 
 $C14.ForeColor = [System.Drawing.SystemColors]::ButtonFace 
 

#C15

 $C15 = New-Object System.Windows.Forms.Label 
 
 $C15.Font = New-Object System.Drawing.Font("Microsoft Sans Serif", 15.75, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Point, ([System.Byte](0))) 
 
 $C15.Location = New-Object System.Drawing.Point(830, 47) 
 
 $C15.Size = New-Object System.Drawing.Size(181, 27) 
 
 $C15.TabIndex = 7 
 
 $C15.Text = "15 $st"+"1 Million" 
 
 $C15.BackColor = [System.Drawing.Color]::Transparent 
 
 $C15.ForeColor = [System.Drawing.Color]::DarkOrange 
 

Adding Controls to Form:

Now that we have all of our controls created we need to add these to the form.

 $Main.Controls.Add($WalkAway) 
 
 $Main.Controls.Add($Audience) 
 
 $Main.Controls.Add($PHX) 
 
 $Main.Controls.Add($50X) 
 
 $Main.Controls.Add($C1) 
 
 $Main.Controls.Add($C2) 
 
 $Main.Controls.Add($C3) 
 
 $Main.Controls.Add($C4) 
 
 $Main.Controls.Add($C5) 
 
 $Main.Controls.Add($C7) 
 
 $Main.Controls.Add($C8) 
 
 $Main.Controls.Add($C6) 
 
 $Main.Controls.Add($C9) 
 
 $Main.Controls.Add($C10) 
 
 $Main.Controls.Add($C11) 
 
 $Main.Controls.Add($C12) 
 
 $Main.Controls.Add($C13) 
 
 $Main.Controls.Add($C14) 
 
 $Main.Controls.Add($C15) 
 
 $Main.Controls.Add($D) 
 
 $Main.Controls.Add($C) 
 
 $Main.Controls.Add($B) 
 
 $Main.Controls.Add($A) 
 
 $Main.Controls.Add($Question) 
 

And at the bottom of our entire script we will need to add the code to actually let us see the created form:

  
 [System.Windows.Forms.Application]::EnableVisualStyles() 
 
 [System.Windows.Forms.Application]::Run($Main) 
 

The resulting render of all this is the following:

The Primary GUI for the Game

 

Conclusion:

The above gives us everything we need to create the form except the entire Base64 encoded JPG as that would be in the hundreds to thousands of lines. Not that bad if you are cutting and pasting but horrible if I drop it in the middle of a blog. I would suggest using your own JPG with the listed function and testing it. Then simply move the Controls around to suit your image.

Now that we have a fully created form we have some programming ahead of us. In part 3 we will go over the Functions of the game.


Part 3: Creating the Questions and Answers

Intro:

At this point our design has turned into an actual form that renders for us. However we have no functions to run the game and without those there really is no game at all. So without further ado let us jump right in to creating our functions.

Pre-work:

Before we could build our functions I needed to build up how the questions would be asked, what information was needed and how to format this so I could edit it easily without going back into the code. Originally I was going to use XML because of how clean it is but finally settled on using a CSV to hold the information in columns and simply called it Test.csv. Now here's what did I needed to put in it…

Question – To hold the full question

A,B,C,D – To hold the answers for each sections

Answer – What the actually answer is

T1, T2 – These are so I can control take away 1 and take away 2

Doing it this way the form looked like the following:

  • question,a,b,c,d,answer,t1,t2

For example:

  • What was the original name of PowerShell?, Micronad,Macroid,Monad,Monarch,c,b,d

To break this down a little better for all of us.

Question = What was the original Name of PowerShell

A = Micronad

B = Macroid

C = Monad

D = Monarch

Answer = C = Monad

Take Away 1 = B = Macroid

Take Away 2 = D = Monarch

This gives us everything we need to know exactly what to do with each question. Now we need to actually create the functions to use this CSV file.

Next Question:

Back inside the script, we need to use the CSV we created. At this point I needed to decide if I wanted to create 2 functions (one for the first question, and one for every next question) or create just one function that does the workload before every next question. I went with option number 2, only 1 function:

 Function NextQ{ 
 
 #Import the CSV we need to use 
 
 $qu = import-csv .\test.csv 
 
 #Create a randomization for which question to ask 
 
 $rand = New-Object System.Random 
 
 $rnd = $rand.next(0,32) 
 
 $info = $qu.GetValue($rnd) 
 
 #Turn each column into a variable and set the variable for Script 
 
 foreach ($m in $info){ 
 
 $script:q = $m.question 
 
 $script:ab = $m.a 
 
 $script:bb = $m.b 
 
 $script:cb = $m.c 
 
 $script:db = $m.d 
 
 $script:answer = $m.answer 
 
 $script:t1 = $m.t1 
 
 $script:t2 = $m.t2} 
 
 #Make all the answers visible 
 
 $a.visible=$true 
 
 $b.visible=$true 
 
 $c.visible=$true 
 
 $d.visible=$true 
 
 #Reset the highlighter we are going to use 
 
 $A.BackColor = [System.Drawing.Color]::Transparent 
 
 $B.BackColor = [System.Drawing.Color]::Transparent 
 
 $C.BackColor = [System.Drawing.Color]::Transparent 
 
 $D.BackColor = [System.Drawing.Color]::Transparent 
 
 $A.Forecolor = [System.Drawing.Color]::White 
 
 $B.Forecolor = [System.Drawing.Color]::White 
 
 $C.Forecolor = [System.Drawing.Color]::White 
 
 $D.Forecolor = [System.Drawing.Color]::White 
 
 #Set the Question and answers text on the screen 
 
 $question.text = $q 
 
 $a.text = $ab 
 
 $b.text=$bb 
 
 $c.text=$cb 
 
 $d.text = $db 
 
 } 
 

Now we have our question and answers turned to variables and added to our soon-to-be-clickable buttons (Answers). With that above CSV imported and parsed and written to the screen the Q&A section will now look like this:

Q&A Screen

Clicking our Answers

Well a user has to actually be able to select an answer. So in this section we will make each one of the buttons selectable and send the Answer off to our function Final (for Final Answer).

 function DClick( $object ){ 
 
 if($pause -eq $false){ 
 
 Final d 
 
 }} 
 
 function CClick( $object ){ 
 
 if($pause -eq $false){ 
 
 Final c 
 
 }} 
 
 function BClick( $object ){ 
 
 if($pause -eq $false){ 
 
 Final b 
 
 }} 
 
 function AClick( $object ){ 
 
 if($pause -eq $false){ 
 
 Final a 
 
 }} 
 

This simply creates the function for when we click an answer button. All it will do is see what you selected and then send that response to our Final function.

Is that you final Answer?

First thing we need to do is make sure that when you select an answer we clean up anything left on the screen. For this I created a function called SMClean.

 Function SMClean { 
 
 $Audass.visible = $false 
 
 $Audth.visible = $false 
 
 $AUA.visible = $false 
 
 $AUB.visible = $false 
 
 $AUC.visible = $false 
 
 $AUD.visible = $false 
 
 } 
 

This just turns off the Audience part that we will create shortly. This way when a user selects Audience help it gets cleaned up when they select an answer.

 Function Final($Script:AN){ 
 
 Smclean 
 
 #Setup Clippy’s response and users response 
 
 $script:type = "Final" 
 
 $Btext.text = "I see you have picked an answer. Would you like me to accept this answer?" 
 
 $Q2.text = "Heck no!" 
 
 $Q1.text = "Of course!" 
 
 # Make the balloon and information visible for selection 
 
 $Btext.visible = $true 
 
 $Quit.visible = $true 
 
 $Quit.visible = $false 
 
 $Q2.visible = $true 
 
 $Q1.Visible = $true 
 
 $balloon.visible = $true 
 
 $script:pause = $true 
 
 } 
 

Once executed, the end user will see the following:

Confirmation of Question Acceptance

Balloon:

The balloon is re-usable depending on type. In this case we are using it to determine if this is the user's final answer. So the Type we use is Final. Both Q1 and Q2 are your selections and are clickable.

 function Q2Click( $object ){ 
 
 If ($script:type -eq "Final"){ 
 
 #Turn off the balloon and just continue no answer selected 
 
 $balloon.visible = $false 
 
 $Q1.visible = $false 
 
 $Q2.visible = $false 
 
 $BText.visible = $false 
 
 $script:type -eq "" 
 
 $script:pause = $false} 
 
 } 
 
 function Q1Click( $object ){ 
 
 If ($script:type -eq "Final"){ 
 
 #Turn off the balloon and check the Answer 
 
 $balloon.visible = $false 
 
 $Q1.visible = $false 
 
 $Q2.visible = $false 
 
 $BText.visible = $false 
 
 $script:type -eq "" 
 
 CheckQ $an} 
 
 } 
 

Did you get it right?

Now all we need to do is check the answer and set what happens depending on if it is correct or not.

 Function CheckQ($ans){ 
 
 #If you got the answer correct 
 
 if ($ans -eq $answer){ 
 
 $script:pause = $false 
 
 $t = $r.getvalue($o) 
 
 $t.BackColor = [System.Drawing.Color]::Transparent 
 
 $script:o+=1 
 
 #If you got the answer right and you just reach Million 
 
 if($o -eq 15){ 
 
 $pause = $true 
 
 #Turn on the balloon and message 
 
 $BText.text = "WOW, you actually did it with out my help! You are the Million Dollar WINNER!!!" 
 
 $Quit.text = "Take the money and run!" 
 
 $BText.visible = $true 
 
 $Quit.visible = $true 
 
 $balloon.visible = $true 
 
 }Else{ 
 
 #If you got it right but it wasn’t for a Million set the new value and go to Next Question 
 
 $t1 = $r.getvalue($o) 
 
 $t1.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](128)))), ([System.Int32](([System.Byte](0))))) 
 
 NextQ} 
 
 } else { 
 
 #If you got it wrong turn the right answer Green and the choice Red 
 
 if($ans -eq "a"){ 
 
 $RedX.Location = New-Object System.Drawing.Point(118, 653) 
 
 $RedX.Visible = $true 
 
 } 
 
 if($ans -eq "b"){ 
 
 $RedX.Location = New-Object System.Drawing.Point(580, 654) 
 
 $RedX.Visible = $true} 
 
 if($ans -eq "c"){ 
 
 $RedX.Location = New-Object System.Drawing.Point(118, 713) 
 
 $RedX.Visible = $true} 
 
 if($ans -eq "d"){ 
 
 $RedX.Location = New-Object System.Drawing.Point(580, 714) 
 
 $RedX.Visible = $true} 
 
 if($answer -eq "a"){ 
 
 $A.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $A.ForeColor = [System.Drawing.Color]::Green 
 
 $CheckM.Location = New-Object System.Drawing.Point(118, 653) 
 
 $CheckM.Visible = $true} 
 
 if($answer -eq "b"){ 
 
 $B.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $B.ForeColor = [System.Drawing.Color]::Blue 
 
 $CheckM.Location = New-Object System.Drawing.Point(580, 654) 
 
 $CheckM.Visible = $true} 
 
 if($answer -eq "c"){ 
 
 $C.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $C.ForeColor = [System.Drawing.Color]::Red 
 
 $CheckM.Location = New-Object System.Drawing.Point(118, 713) 
 
 $CheckM.Visible = $true} 
 
 if($answer -eq "d"){ 
 
 $D.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $D.ForeColor = [System.Drawing.Color]::Orange 
 
 $CheckM.Location = New-Object System.Drawing.Point(580, 714) 
 
 $CheckM.Visible = $true} 
 
 $pause = $true 
 
 #Turn on the balloon and play music 
 
 $BText.text = "That was an incorrect answer. You have earned $st"+ $win +". Congratulations!" 
 
 SoundSync $incorrect 
 
 $Quit.text = "Run Away from Clippy" 
 
 $BText.visible = $true 
 
 $Quit.visible = $true 
 
 $balloon.visible = $true 
 
 } 
 
 } 
 

If the question was answered correctly we loop back to the beginning or congratulate you on winning the Million dollars; if you failed then we play music and end the game.

Conclusion:

In this part we created the functions need to run the core part of the game, Questions and Answers. We allowed these functions to loop so the game continues through our predefined path. User interaction has now been created for selecting the answers and functions to check the answers. All in all this is the core component of the game we are creating.

In the next part we will go over adding the 4 helper functions for the user: 50/50, Phone a friend, Ask the Audience and Walk Away.


Part 4: Adding the Helper Functions

Intro:

We have our GUI and our Functions setup and the game can be runnable at this point. But we didn’t add any of the Helpers for our user to make the game more interesting. In this part we are going to focus on giving the user some help at winning a Million dollars from Clippy.

Game Helpers GUI

50/50 Helper:

Fifty – Fifty. This will allow the user to remove 2 options from the answers section to narrow it down for them. This is where the Takeaways from our CSV (Discussed in last section) will come into play.

If the user clicks this helper we will remove 2 Answers, place a big Red X over the button so the user cannot click it more than once.

 function FiftyXClick( $object ){ 
 
 if($pause -eq $false){ 
 
 #Place the Red X over the 50/50 
 
 if($50x.image -eq $null){ 
 
 $50X.Image = ([System.Drawing.Image]([System.Drawing.Image]::FromStream((New-Object System.IO.MemoryStream(($$ = [System.Convert]::FromBase64String($Red)),0,$$.Length))))) 
 
 #Make 2 Answers invisible 
 
 if (($t1 -eq "a")-or($t2 -eq "a")){$a.visible=$false} 
 
 if (($t1 -eq "b")-or($t2 -eq "b")){$b.visible=$false} 
 
 if (($t1 -eq "c")-or($t2 -eq "c")){$c.visible=$false} 
 
 if (($t1 -eq "d")-or($t2 -eq "d")){$d.visible=$false}} 
 
 }} 
 

Turning off visibility will not just make the Answer be invisible it will also make it non clickable. This way we have no fear of the user being able to select something just because they cannot see it. This will leave only 2 selections left on the screen that the user can choose.

Phone a Friend (Helper):

This was a fun one to mimic. What we need here is a way to be able to phone a friend who knows the answer. In this case I decided you can only phone PoSH to eliminate the need for multiple voices. I decided to use the Microsoft Voice SAPI to do the phone call.

Right above Main we needed to add the SAPI.

 $phv = new-object -com SAPI.SpVoice

Now that we have that we can use the voice needed for the Phone a Friend function.

 function PHXClick( $object ){ 
 
 if($pause -eq $false){ 
 
 if($phx.image -eq $null){ 
 
 $PHX.Image = ([System.Drawing.Image]([System.Drawing.Image]::FromStream((New-Object System.IO.MemoryStream(($$ = [System.Convert]::FromBase64String($Red)),0,$$.Length))))) 
 
 if($answer -eq "a"){$PHAns = $a.text} 
 
 if($answer -eq "b"){$PHAns = $b.text} 
 
 if($answer -eq "c"){$PHAns = $c.text} 
 
 if($answer -eq "d"){$PHAns = $d.text} 
 
 $phv.Speak("Hi, PoSH here to help you. $q That is a tough one, but I am sure the answer is $answer, $phans. Talk to you later and good luck.",1)} 
 
 } 
 
 } 
 

Just like before we add the Red X over it so the user cannot click it more than once. Then we let the SAPI take over and give the user the answer. In this coding the answer given will always be correct.

Ask the Audience Helper

Audiences are notoriously random in their answers and I decided to use a randomization to ensure this stayed random. We needed the audience to always add up to 100% and also display this in a readable graph. And let’s not forget the always used Red X

Ask the Audience Helper

 function AudienceClick( $object ){ 
 
 if($pause -eq $false){ 
 
 if($Audience.image -eq $null){ 
 
 $Audience.Image = ([System.Drawing.Image]([System.Drawing.Image]::FromStream((New-Object System.IO.MemoryStream(($$ = [System.Convert]::FromBase64String($Red)),0,$$.Length))))) 
 
 $AURND = New-Object System.Random 
 
 $Total = 100 
 
 $AudA = $AURND.next(1,$total) 
 
 $Total -= $Auda 
 
 $Audb = $AURND.next(1,$total) 
 
 $Total -= $Audb 
 
 $Audc = $AURND.next(1,$total) 
 
 $Total -= $Audc 
 
 $AudD = $Total 
 
 $AUDTH.Text = "$auda" + "% of the Audience thinks the answer is $ab `r`n $audb" + "% of the Audience thinks the answer is $bb `r`n $audc" + "% of the Audience thinks the answer is $cb `r`n $audd" + "% of the Audience thinks the answer is $db" 
 
 $A1 = 305 - ($AUDA *= 2) 
 
 $A2 = 305 - ($AUDB *= 2) 
 
 $A3 = 305 - ($AUDC *= 2) 
 
 $A4 = 305 - ($AUDD *= 2) 
 
 $AUA.Location = New-Object System.Drawing.Point(356, $A1) 
 
 $AUB.Location = New-Object System.Drawing.Point(395, $A2) 
 
 $AUC.Location = New-Object System.Drawing.Point(434, $A3) 
 
 $AUD.Location = New-Object System.Drawing.Point(473, $A4) 
 
 $AUA.Size = New-Object System.Drawing.Size(38, $AUDA) 
 
 $AUB.Size = New-Object System.Drawing.Size(38, $AUDB) 
 
 $AUC.Size = New-Object System.Drawing.Size(38, $AUDC) 
 
 $AUD.Size = New-Object System.Drawing.Size(38, $AUDD) 
 
 $Audass.visible = $true 
 
 $Audth.visible = $true 
 
 $AUA.visible = $true 
 
 $AUB.visible = $true 
 
 $AUC.visible = $true 
 
 $AUD.visible = $true 
 
 } 
 
 } 
 
 } 
 

What happens here is that we randomize A to 100 then randomize B to 100 – A. We continue this down to D. So if A = 43 then B is randomized from 57. This gives us everything added up to 100. We also use these values to decide the size of the bars in the graph as well.

WalkAway:

Every once in a while you just need to grab the money and run. So let's give this option to our players as well. What we will do here is allow the Walk Away button to be hit and then send it to the Balloon.

 function WalkAwayClick( $object ){ 
 
 $BText.text = "I knew you would run. You have earned $st"+ $win +"." 
 
 $Quit.text = "Run Away from Clippy" 
 
 $BText.visible = $true 
 
 $Quit.visible = $true 
 
 $balloon.visible = $true 
 
 } 

We do not need to use a Red X for this one as the game ends after the selections are made. So it can only be used once.

Conclusion:

We have now added all the helpers a user can use to get them to the Million dollar pot. We used Graphs, Take away and even the Microsoft SAPI to create a phone call. All in all these work exactly like we expected. Now our user can get some much needed help.

 


Part 5:  Adding some Bells and Whistles

Intro :

Well, we have done it. The game is up and running but it lacks some of the refinement we are going to need to call it a true game. So let’s go add some bells and whistles to what we have already done.

In this part we are going to add some sound effects, some highlighting, and extras that just make it feel more interactive. So without further ado…

Count your money:

We all like to see our money go up, and we already created the numbers for it, so what we need now is to increase the money section to look a lot better and more informative.

Right off the bat we are going to need some numbers for all the labels we creates. Above MAIN but not in a function let's add the following values:

 $script:o = 0 
 
 $r = $C1,$C2,$C3,$C4,$C5,$C6,$C7,$C8,$C9,$C10,$c11,$c12,$c13,$c14,$c15 
 
 $r2 = 100,200,300,500,1000,2000,4000,8000,16000,32000,64000,125000,250000,500000,"1 Million" 
 
 Now we have our values and variables for the Money. In the previous Part we had the following code in the CheckQ function if you got the question right 
 
 $script:o+=1 
 
 $t1 = $r.getvalue($o) 
 
 $t1.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](128)))), ([System.Int32](([System.Byte](0))))) 
 

What we did here was increase the count by 1 and then highlighted the new value that you have reached. And what does this look like?

Money Screen Segment

Now we can count to 1 Million as the player keeps getting the correct answers!

Some sound please:

 

Really, what is a game without sound?  Let’s setup the sound to be used for our buttons, our intro and for when you get a question wrong. We need a good wah-wah-waaah when there is an incorrect answer.

Above Main lets add the information for the Sound effect.

 $so1 = Get-Item .\button7.wav 
 
 $so2 = get-item .\incorrect.wav 
 
 $Script:audiobutton = $so1.DirectoryName + "\" + $so1.Name 
 
 $Script:incorrect = $so2.DirectoryName + "\" + $so2.Name 
 
 $so3 = get-item .\intro1.wav 
 
 $Script:intro = $so3.DirectoryName + "\" + $so3.Name 
 

Now we have the sounds ready we still need a function to play them. Lets just call it simply Sound.

 Function Sound($loc){ 
 
 $sound = new-Object System.Media.SoundPlayer; 
 
 $sound.SoundLocation= $loc; 
 
 $sound.Play(); 
 
 } 
 

That was easy. Now we can send any sound file we want to this function and have it play for us. Good news is that now that we have our sounds in place we can work on our buttons (Answers).

Answers:

We cannot leave the selection point that people choose from to be so plain and boring. We need some sound and color to liven up this dull space. For this we are first going to add a highlight when the mouse is over the selection. To do this we are going to create Mouse Over to highlight the selection and Mouse Exit to revert the highlight. We will also add a sound when we go over a button.

 Function DMouseO( $object){ 
 
 if($pause -eq $false){ 
 
 $D.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $d.forecolor = [System.Drawing.Color]::Orange 
 
 }} 
 
 Function DMouseE( $object){ 
 
 if($pause -eq $false){ 
 
 $D.BackColor = [System.Drawing.Color]::Transparent 
 
 $d.forecolor = [System.Drawing.Color]::White 
 
 }} 
 
 
 Function CMouseO( $object){ 
 
 if($pause -eq $false){ 
 
 $C.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $C.ForeColor = [System.Drawing.Color]::Red 
 
 }} 
 
 
 Function CMouseE( $object){ 
 
 if($pause -eq $false){ 
 
 $C.BackColor = [System.Drawing.Color]::Transparent 
 
 $C.ForeColor = [System.Drawing.Color]::White 
 
 }} 
 
 
 Function BMouseO( $object){ 
 
 if($pause -eq $false){ 
 
 $B.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $B.ForeColor = [System.Drawing.Color]::Blue 
 
 }} 
 
 
 Function BMouseE( $object){ 
 
 if($pause -eq $false){ 
 
 $B.BackColor = [System.Drawing.Color]::Transparent 
 
 $B.ForeColor = [System.Drawing.Color]::White 
 
 }} 
 
 Function AMouseO( $object){ 
 
 if($pause -eq $false){ 
 
 $A.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $A.ForeColor = [System.Drawing.Color]::Green 
 
 }} 

 
 Function AMouseE( $object){ 
 
 if($pause -eq $false){ 
 
 $A.BackColor = [System.Drawing.Color]::Transparent 
 
 $A.ForeColor = [System.Drawing.Color]::White 
 
 }} 
 

Now we can see and hear when we go over a Button.

Answer Selection Section

See how Answer C is highlighted when our cursor goes over it. This is exactly what we want. We set the Text to change to be the same color as the background image when it is highlighted to make it easier to read.

Answers (Right and Wrong):

Now what about when you get it wrong. For this part we will switch the highlighting for the Wrong answer to Red and what the correct answer was to Green. We will also add a Checkmark and Red X icon beside them just to make it prettier. The following lines were added to the CheckQ function:

 if($ans -eq "a"){ 
 
 $RedX.Location = New-Object System.Drawing.Point(118, 653) 
 
 $RedX.Visible = $true 
 
 } 
 
 if($ans -eq "b"){ 
 
 $RedX.Location = New-Object System.Drawing.Point(580, 654) 
 
 $RedX.Visible = $true} 
 
 if($ans -eq "c"){ 
 
 $RedX.Location = New-Object System.Drawing.Point(118, 713) 
 
 $RedX.Visible = $true} 
 
 if($ans -eq "d"){ 
 
 $RedX.Location = New-Object System.Drawing.Point(580, 714) 
 
 $RedX.Visible = $true} 
 
 if($answer -eq "a"){ 
 
 $A.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32] 
 
 
 
 
 (([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $A.ForeColor = [System.Drawing.Color]::Green 
 
 $CheckM.Location = New-Object System.Drawing.Point(118, 653) 
 
 $CheckM.Visible = $true} 
 
 if($answer -eq "b"){ 
 
 $B.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32] 
 
 
 
 
 (([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $B.ForeColor = [System.Drawing.Color]::Blue 
 
 $CheckM.Location = New-Object System.Drawing.Point(580, 654) 
 
 $CheckM.Visible = $true} 
 
 if($answer -eq "c"){ 
 
 $C.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32] 
 
 
 
 
 (([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $C.ForeColor = [System.Drawing.Color]::Red 
 
 $CheckM.Location = New-Object System.Drawing.Point(118, 713) 
 
 $CheckM.Visible = $true} 
 
 if($answer -eq "d"){ 
 
 $D.BackColor = [System.Drawing.Color]::FromArgb(([System.Int32](([System.Byte](255)))), ([System.Int32] 
 
 
 
 
 (([System.Byte](255)))), ([System.Int32](([System.Byte](0)))));Sound $audiobutton 
 
 $D.ForeColor = [System.Drawing.Color]::Orange 
 

 

 

And voila… Right and Wrong

Right and Wrong Answers

Tool Tips:

 

Admit it, Tooltips are cool. Really they are and they make it very easy to figure out what a Helper button will do. With that in mind I went and added ToolTips to each of the helper buttons.

In each of the objects I began adding the ToolTips as follows.

 #50/50 
 
 $ToolTip50 = New-Object System.Windows.Forms.ToolTip 
 
 $ToolTip50.IsBalloon = $false 
 
 $ToolTip50.InitialDelay = 300 
 
 $ToolTip50.ReshowDelay = 300 
 
 $ToolTip50.SetToolTip($50x, "50/50 - Remove 2 Answers") 
 

 #Audience 
 
 $ToolTipau = New-Object System.Windows.Forms.ToolTip 
 
 $ToolTipau.IsBalloon = $false 
 
 $ToolTipau.InitialDelay = 300 
 
 $ToolTipau.ReshowDelay = 300 
 
 $ToolTipau.SetToolTip($Audience, "Ask the Audience for assistance") 
 
 
 
 
 #Phone PoSh 
 
 $ToolTipph = New-Object System.Windows.Forms.ToolTip 
 
 $ToolTipph.IsBalloon = $false 
 
 $ToolTipph.InitialDelay = 300 
 
 $ToolTipph.ReshowDelay = 300 
 
 $ToolTipph.SetToolTip($phx, "Phone PoSh for assistance") 
 
 
 
 
 #Walk Away 
 
 $ToolTipwa = New-Object System.Windows.Forms.ToolTip 
 
 $ToolTipwa.IsBalloon = $false 
 
 $ToolTipwa.InitialDelay = 300 
 
 $ToolTipwa.ReshowDelay = 300 
 
 $ToolTipwa.SetToolTip($walkaway, "Walk Away - Take the money and run") 
 

We turned off Balloon tooltip because it looked a little too intrusive for the screen and this is what we get.

Tool Tips

Simple, easy and effective. Yes, I do like it.

Conclusion:

With some bells and whistles done all we need to do now is add some Help and a Splash screen and we have a completed game on our hands. Getting closer by the minute to having a final product.

In this part we learned how to add some Tool tips, Create Highlighting and add sounds as we need them. All in all not a bad day’s worth of work. In the next part we will finalize the game with Splash Screen/Menu and Help so players know how to play the Game.

 


Part 6: Creating the Main Menus and Help Screens

Intro:

We are finally here, the last steps in making this a full-fledged educational game, and all in PowerShell to boot. Now all we need are some Menus, a Help section and we can finalize this game. So let’s just jump right in.

Start Menu:

We need a start Menu. So first things first, we need to design one quickly:

Start Screen

Given this, we need 1 Background JPG and 3 Labels. Each Label will be clickable and will load the appropriate form.

So, just like before, we need to create a New Form:

 [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
 
 [void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
 
 
 # Splash Form 
 
 $Splash = New-Object System.Windows.Forms.Form 
 
 $Splash.ClientSize = New-Object System.Drawing.Size(1059, 798) 
 
 $Splash.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedToolWindow 
 
 $Splash.Text = "Who Wants to be a Millionaire - PowerShell Edition" 
 
 #region$Splash.BackgroundImage = ([System.Drawing.Image](...) 
 
 $Splash.BackgroundImage = ([System.Drawing.Image]([System.Drawing.Image]::FromStream((New-Object System.IO.MemoryStream(($$ = [System.Convert]::FromBase64String( 
 
 "/9j/4AAQSkZJRgABAQEAYABgAAD/4QBoRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAASAAAATgAAAAAAAABgAAAAAQAAAGAAAAABUGFpbnQu 
 
 "TkVUIHYzLjUuMTAA/9sAQwAQCwwODAoQDg0OEhEQExgoGhgWFhgxIyUdKDozPTw5Mzg3QEhcTkBE 
 
 "V0U3OFBtUVdfYmdoZz5NcXlwZHhcZWdj/9sAQwEREhIYFRgvGhovY0I4QmNjY2NjY2NjY2NjY2Nj 
 
 "Y2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Nj/8AAEQgDIAQpAwEiAAIRAQMRAf/E 
 
 "AB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAE… 
 

Now that the Form is created we still need to add in the Labels.

 # Start Label 
 
 $Start = New-Object System.Windows.Forms.Label 
 
 $Start.Location = New-Object System.Drawing.Point(311, 663) 
 
 $Start.Size = New-Object System.Drawing.Size(451, 72) 
 
 $Start.TabIndex = 0 
 
 $Start.Text = "" 
 
 $Start.BackColor = [System.Drawing.Color]::Transparent 
 
 $Start.add_Click({StartClick($Start)}) 
 
 $Splash.Controls.Add($Start) 
 
 
 
 #Help Label 
 
 $Help = New-Object System.Windows.Forms.PictureBox 
 
 $Help.Location = New-Object System.Drawing.Point(82, 498) 
 
 $Help.Size = New-Object System.Drawing.Size(244, 102) 
 
 $Help.TabIndex = 1 
 
 $Help.TabStop = $false 
 
 $Help.Text = "" 
 
 $Help.BackColor = [System.Drawing.Color]::Transparent 
 
 $Help.add_Click({HelpClick($Help)}) 
 

With the Clickable Labels in place all we need now is the quick Functions to call when clicked that open a new form.

 #Help Button Clicked 
 
 function HelpClick( $object ){ 
 
 $main.visible=$false 
 
 $form1.visible = $true 
 
 } 

 
 #Start button Clicked 
 
 function StartClick( $object ){ 
 
 $main.visible = $false 
 
 $quiz.visible = $true 
 
 } 
 

So the Main Menu screen turns off visibility and then open the new form. Form1 being the Help form and Quiz form being the main game area. And we have our very own nice start screen. Let's see how it looks:

Start Screen

Now all we need is a Help Form created to assist the user.

Help Screens:

We all need a little help and what kind of game would this be if we did not add that? This time though I decided that I would do pages for the Help File and just change the background image every time the player clicked the next button. So only 1 label on this form.

Help JPG’s:

Help ScreenHelp Screen

Help Screen

Help Screen

Help Screen

This gives us JPGs for every page. All we needed now was a function to run though them.

 function NextClick( $object ){ 
 
 $script:nex = $nex + 1 
 
 write-host $a 
 
 if ($nex -eq 1){$Form1.BackgroundImage = ([System.Drawing.Image]([System.Drawing.Image]::FromStream((New-Object 
 
 System.IO.MemoryStream(($$ = [System.Convert]::FromBase64String($one)),0,$$.Length)))))} 
 
 if ($nex -eq 2){$Form1.BackgroundImage = ([System.Drawing.Image]([System.Drawing.Image]::FromStream((New-Object 
 
 
 System.IO.MemoryStream(($$ = [System.Convert]::FromBase64String($two)),0,$$.Length)))))} 
 
 if ($nex -eq 3){$Form1.BackgroundImage = ([System.Drawing.Image]([System.Drawing.Image]::FromStream((New-Object 
 
 
 System.IO.MemoryStream(($$ = [System.Convert]::FromBase64String($three)),0,$$.Length)))))} 
 
 if ($nex -eq 4){$Form1.BackgroundImage = ([System.Drawing.Image]([System.Drawing.Image]::FromStream((New-Object 
 
 System.IO.MemoryStream(($$ = [System.Convert]::FromBase64String($four)),0,$$.Length)))))} 
 
 if ($nex -eq 5){ 
 
 $form1.visible = $false 
 
 $main.visible = $true 
 
 } 
 
 } 
 

So every time they Click the Next arrow at the bottom right the screen changes to the next page. When it hits the final page it closes the form and re-opens the Start menu back up.

Conclusion:

It was a fun ride. From design to implemention this project was a great experience and I am glad you shared that experience with me. We have now created everything except the actually questions. We created the CSV file and set the columns, but when it comes to the Questions I will let each person decide on what they want to put in it.

Again thank you for joining me on this journey. If you are out there making some interesting things please share with the rest of us as we would love to join you on your journey as well and with PowerShell the sky's is the limit.

 

Posted by Frank Battiston, MSPFE Editor.