« Microsoft to Donate for Employee Volunteer Hours | Main | Tacoma Narrows Bridge Work Continues »

September 26, 2005

Aliens.msh - A Monad Video Game

In my spare time in the Track Lounge at the PDC, I worked on a simple video game written in MSH. It harkens back to a video game I wrote for the original IBM PC back in 1984, when I was in high school, called "Left, Right and Fire" (which I actually sold to a company, but they went out of business...a different story).

I won't include the full version (it's about 300 lines) in this blog entry; it's available here. I'll put it out under the Creative Commons Attribution license; you can use it as you see fit, as long as you attribute the original to me.

Feel free to paw through the code trying to decipher it; if you have any questions just ask. Please note the styly multi-colored explosions. I was originally going to have the aliens move sideways, but down is easier. The game is also missing logic to notice when it should end. It's a quick program, caveat emptor when imitating it.

To run it, just paste the contents into a .MSH file and run it. The controls are z to go left, x to go right, and space to fire. Of course you can change those easily if you want.

I just want to call out a few sections of the code. The first is the one that gets you the raw UI object, which lets you access the console directly:

$rawui = $host.ui.rawui

Run this through get-member if you want to see what is available.

Next we have this bit of code which initializes an enum with the OR of multiple values:

$keyopt = [System.Management.Automation.Host.ReadKeyOptions]"NoEcho,IncludeKeyDown,IncludeKeyUp"

This is the main key processing loop, which actually uses the $keyopt defined above:

while ($rawui.KeyAvailable) {
  $key = $rawui.ReadKey($keyopt)
  if ($key.KeyDown) {
    switch ($key.Character) {
      ([char]"z") {
        # etc.

Then we have the management of a hash table of missiles, which is initialized with:

# hash table of coordinates of active missiles
$missiles = @{}
# current key for $missiles (wraps around at 1000)
$missileindex = 0

and then new entries are inserted with:

$script:missileindex = (($missileindex + 1) % 1000)
$script:missiles[$missileindex] = new-object System.Management.Automation.Host.Coordinates $missilex,$missiley

So $missileindex is just a number that loops around rarely enough that it won't ever hit an existing hash table entry; and the elements in the hash table are Coordinates objects.

There's this little bit:

$keys = new-object object[] $missiles.Count
foreach ($m in $keys) {
  # then later you might call...

which allows you to copy the keys of a hash table, so you can walk it and possibly remove elements (if you try to remove from a live enumerator you get an error).

Finally the main timing loop looks (when simplified a bit) like this:

$t = time-expression {
  $script:done = drain_keys
if ($t.Milliseconds -lt 100) {
  start-sleep -millisecond (100-$t.Milliseconds)

It uses time-expression to time each time through the main loop, and then waits until the next 100 millisecond boundary before continuing.

Posted by AdamBa at September 26, 2005 08:55 PM

Trackback Pings

TrackBack URL for this entry:


Very Very Shweet! Minor annoyance: You could hide the cursor between repaints though.

Posted by: Vivek at September 27, 2005 11:13 AM

What? No screenshots?!?

Posted by: Chris Sells at September 28, 2005 07:47 AM

Here's a partial screen shot:




- adam

Posted by: Adam Barr at September 28, 2005 01:01 PM

In the released version of Monad (now known as PowerShell), the time-expression cmdlet has been renamed measure-command. I'll leave the above blog entry unchanged for historical accuracy, but I changed the linked-to page with the full script to be correct.

- adam

Posted by: Adam Barr at December 5, 2006 11:17 AM