Sunday, October 25, 2015

pencilWars using PyGame in Python

 About two years ago, Congress gave me some forced time off so I decided to learn some Python in addition to working on the house. Ever since learning BASIC on my Atari 400 (with the Atari 410 cassette drive!), I’ve liked interpreted languages. I probably inherited that from my dad who was an APL and SAS programmer back in the day. I still only program in Matlab for work. When I (tried) learning C++ and Java, I felt the same frustration recently expressed by Wil Wheaton. So Python had its appeal - interpreted, seemingly widespread, and free. I wrote a game because I didn’t have a particular problem I wanted to solve other than having fun and learning something new - just like when I banged away on my Atari, except now I don’t have to use a membrane keyboard and black and white television.

PencilWars is based on a paper-and-pencil game of the same name detailed in Tom Angleberger's Oragami Yoda. It is built using the pygame 1.9 package and runs in either Python 2.7 or 3.4. You can download the source code here.

Screen shot of pencilWars. Player 2 just moved indicated by the red pencil stroke and it's now Player 1's turn.
The architecture of the game is very similar to my last blog post Arduino Pong, although pencilWars has more states. In pencilWars, the user clicks the mouse - depending upon the state of the game this either selects a ship or moves the ship. To move the ship, the user "flicks" the mouse while holding down the mouse button in the desired direction of motion. The distance the ship moves is based on the velocity of the mouse during the click and not the distance moved. This action is the challenge.

The users take turns. If one hits an asteroid, they lose a turn. If one hits the space station in the middle of the page, they lose their ship. The first to destroy their opponent's ships wins.

One of the more interesting things I had to figure out was a function to determine the distance between the location of an sprite (ship, asteroid, station) and a line segment (the path taken by another ship). If the distance is less than sum of the two sprites' radii, there was a collision. 

I hope you enjoy playing. 

Thursday, October 15, 2015

Arduino Pong

While on summer vacation I ordered an Arduino kit when my oldest nephew expressed some interest in programming and electronics. We worked our way up to wiring the 8x8 LED matrix display. He had a fun time plugging wires into the solderless breadboard. Since he’s a gamer, I wrote up a single-player pong game to show him the basics of video game programming. A demo is on YouTube and the sketch is available on GitHub. Read on for a description of the software.

The sketch’s loop() contains three sections. The first updates the ball's direction based on its position. For example, if it hits the left wall, the horizontal direction is reversed. When the ball hits the paddle, the vertical direction is reversed upwards. To add some chance to the game (and make it nontrivial), the horizontal direction is randomized when the ball strikes the paddle. On a second variation, nephew suggested the ball direction depend on where it hits the paddle. In that version, the ball goes up if it hits the middle and left and right at 45 degree angles when it hits either end. Finally, if the ball misses the paddle, it’s position is reset to above the play field with a random direction so it enters the screen in a surprise location.

The second section in loop() updates the ball’s position based on the direction settings. To make it simple, the ball has one speed and can only move in 8 directions (up, up & right, right, down & right, and so on), The ball and paddle geometry are also matched to the 8x8 display, i.e. they are constrained from 0 to 7 in x and y coordinates. After the ball position is updated, the paddle position is read from analog pin 0. Arduino C’s handy map() function is used to determine the location of the paddle along the bottom row. Because the paddle is 3 pixels wide, the middle pixel position can vary from 1 to 6.  

The third part of loop() updates the bitmap and then pushes the bitmap to the display. The bitmap is an array of 8 unsigned ints inherited from the demo code for the 8x8 LED dot-matrix. In hindsight, it should strictly be bytes to fully realize the 64-bit video buffer. Fortunately, the shiftOut() function outputs the lower byte of an int. But first, the ball and paddle are inserted into the bit map. The video memory is arranged with each byte representing a column on the display. To draw the paddle, a for-loop iterates through each column of the memory and zeroes out the display memory. At the loop iteration where the paddle is located, the bit 0’s of the three columns for the paddle are set to 1. To draw the ball, a 1 is XOR’d into the horizontal position of the ball’s column byte. The XOR operator is used so the ball will light up an LED when it’s in the air and will blank out the LED if it hits the paddle. (Interestingly, the use of XOR on CRTs for this purpose was patented.) You can see this effect in the video -- it adds a little visual interest and increases the play area by an entire row (which is 12.5% of the available area).

Finally, the video memory is pushed to the LED matrix using shiftOut(). The functional purpose of this code is equivalent to the video card on a modern machine, but of course is much simpler. The LED matrix is controlled by scanning through the columns and selecting the rows to be lit. The code loops through each column of the display and uses two shiftOut() calls to select the column and the push the rows of that column. The shiftOut() sends the data serially to a pair of 8-bit serial to parallel converters, which latch the outputs to drive the LED array. Each column is held on for 1 millisecond and the display loop is repeated 10 times, for an elapsed time of about 80 milliseconds per frame or 12.5 frames per second. Then it starts all over again.

We made a second version of the game that uses a rotary encoder to control the paddle instead of the potentiometer. That's interesting because the encoder provides relative motion rather than absolute position and doesn’t have physical stops to limit the turning. By using modulo arithmetic, the paddle can then wrap around the display. A third variation adds a row of dots to the top of the display and becomes a breakout style game.