The Duckshoot assignment was set back in my first year at university. It was the first time I’d met the C language. In order to complete this assignment I had to get used to using Linux and an Editor called EMACS in which the C code would take shape.
The task was to write a game using eight LEDs, eight toggle switches and an Analogue to Digital converter / Digital to Analogue converter card, namely the Amplicon PCI320. The LEDs and switches were mounted on a rack. The rack had more functionality but we didn’t need to use that until later on in the year.
The thinking behind the game was to have a rotating pattern of ducks; the ducks were represented by alternately lit LEDs. The player was able to kill a duck by flicking up the bottom switch at the right time. If they successfully killed a duck the LED that represented that duck would go out leaving a gap in the rotating pattern.
Having never written a program that was capable of controlling an input output device this seemed like quite a daunting task to begin with. After a few failed attempts and some careful studying of the worksheets I had managed to get some lights flashing.
Most operating systems, Linux included prevents users from directly accessing the hardware they are running on. Access to the hardware is granted by the operating system and provided through a device driver.
The device driver for the Amplicon card does not make the cards ports directly available. Instead it provides a higher level of access, namely the Comedi API.
Comedi stands for Linux Control and Measurement Device Interface. It’s effectively a collection of drivers for a variety of common data acquisition cards. The driver is implemented as a core Linux Kernel module.
With the use of the Comedi API the device can be treated as a file in the sense that it can be opened, closed, read and written to. In this way it was possible to read and write to the rack and check the positions of the switches and manipulate the LED pattern.
By the end of the project I’d managed to write a fully functional C program that met the specification for the assignment. The program was fairly complex in the sense that it had user-defined functions which passed parameters in and out of them so that other functions could make use of the data to perform certain operations.
Here is an excerpt from the assignement specification:
You should start the program with alternate LEDs lit (10101010) on the MARCO RACK, and rotate this bit pattern, inverting LEDs with a trigger up, then down, then up sequence on the bottom switch. Not "machine gunning" implies that the up, down to fire, up again sequence has to be repeated in full to fire again. Penalty for leaving the switch down means that unless the switch is returned to up in a given time, the duck reappears to penalise the player.
Deliverables:
The marks shown are the maximum available in each category:
A diagrammatic design of your program using any method with which you are familiar |
(10) |
Source Code |
|
Well commented (-5% for C++ comments) |
(10) |
Program Banner |
(5) |
Program uses functions |
(5) |
Function banners |
(5) |
Functions use arguments |
(15) |
No global variables |
(10) |
Program is laid out in either classic C or Pascal style |
(10) |
Together with the following requirements signed off by my tutor:
Continuously rotating bit pattern on a suitable time base, not using a software delay loop |
(5) |
Player penalty for leaving switch down (duck reappears) |
(5) |
Not machine-gunning |
(10) |
Direction variable by switch within game |
(5) |
Using 2 switches to implement 4 speeds or levels of difficulty within the game |
(5) |
Thought Process:
Using the information above I came up with the following list of things to do:
- The program should start with alternately lit LEDs using the pattern (1010 1010 or 0xAA)
- Rotate the bit pattern using shift and logic tests to check for wrap around bits using the current duck pattern AND 0x80 and the current duck pattern AND 0x01
- Switch movement to kill a duck, up, down, up. If the player missed bring a duck back to life
- To stop machine gunning remember if the gun has been “re-cocked”
- If all the LEDs are on then the player has lost, if they are all off then they have won
More Detail:
- Depending on the position of the direction switch start the program with one of the following LED patterns, 1010 1010 or 0101 0101 in binary or 0xAA or 0x55 in hexidecimal.
- The use of logic operations, its possible to use logic operations to mask out and set bits in binary patterns:
- The AND operator can be used to check if a bit is set or not ie 1 or 0. Below is an example to test if the most significant bit (MSB) is a 1.
|
1010 1010 0xAA |
AND |
1000 0000 0x80 |
= |
1000 0000 0x80 |
- This example demonstrates how to set the least significant bit (LSB) to 0, again it uses the AND operator
|
0101 0101 0x55 |
AND |
1111 1110 0x01 |
= |
0101 0100 0x54 |
- To set a bit to 1 the OR operator can be used
|
0101 0100 0x54 |
OR |
0000 0001 0x01 |
= |
0101 0101 0x55 |
- When shifting the bit pattern one place to the left test the MSB with the current duck pattern AND 0x80. If this returns true the result should be stored for use later. Shift the bits left one step. If the test was true then set the LSB to 1 using the current duck pattern and the OR operator with 0x01. This stops ducks from disappearing as the bit pattern is rotated.
- When shifting the bit pattern one place to the right test the LSB with the current duck pattern AND 0x01. Again if this returns true the result should be stored for use later. Shift the bits right one step. If the test was true then set the MSB to 1 using the current duck pattern and the OR operator with 0x80.
- One very easy method of killing or reviving a duck is to just invert the bit for example 1 to 0 or 0 to 1. This can be achieved using the XOR operator.
|
0101 0101 0x55 |
XOR |
0000 0001 0x01 |
= |
0101 0100 0x54 |
- To stop players from simply holding down the gun switch the program needs to remember the following, each time the duck pattern is rotated:
- If the gun has been re-cocked
- If the trigger was pulled or released
- If the player has already been penalised for breaking the rules
Coding Process:
- Work out how to initialise and interface with the COMEDI API so that it can read and write data
- Make a function to output the duck pattern on the LED's
- Make a function to check the position of the switches
- Make a function to check if the gun has been fired
- Make a function to kill or revive a duck
- Make a function to rotate the duck pattern on place to either the left or right depending on the position of the direction switch
- Make a function control the speed at switch the ducks are moving. The speed is determined by the position of the two speed switches
- Make a function which gives the user some information about the current game settings and the number of ducks remaining
- Make a function to display the splash screen when the game is started