Category Archives: Arduino

Arduino programming, tips and research.

Adafruit Bluefruit EZ-Key

Last year, my friend Mike tipped me off to a new Bluetooth product that had been released by Adafruit. The tiny EZ-Key device…

http://www.adafruit.com/products/1535

…was a $19.95 “ready to go” switches-to-Bluetooth board. All you had to do was feed it power (3V-16V) and then hook switches (say, joysticks or arcade buttons) to the 12 input pins and ground and you were set. Power it up, hold down the pair button for a few moments, and let it connect to your computer. After that, any press of those switches would send a preprogrammed character. By default, they were:

  • #0 – Up Arrow
  • #1 – Down Arrow
  • #2 – Left Arrow
  • #3 – Right Arrow
  • #4 – Return
  • #5 – Space
  • #6 – the number ‘1’
  • #7 – the number ‘2’
  • #8 – lowercase ‘w’
  • #9 – lowercase ‘a’
  • #10 – lowercase ‘s’
  • #11 – lowercase ‘d’

You can find a full tutorial here:

http://learn.adafruit.com/introducing-bluefruit-ez-key-diy-bluetooth-hid-keyboard/overview

And the online user manual (with pinouts, wiring examples, etc.) here:

http://learn.adafruit.com/introducing-bluefruit-ez-key-diy-bluetooth-hid-keyboard/user-manual

If the default keys are not good for you, the device can be reprogrammed to send a different single character when a switch is pressed. This seems to involve using an adapter to hook the EZ-Key’s TX/RX pins to the PC, then running a special program that sends sequences to the EZ-Key to remap it.

The device can also be remapped over Bluetooth, which sounds like an easier option since you probably have Bluetooth (if you are using a Bluetooth adapter like this) and probably don’t have a TTL-to-Serial USB adapter. (Okay, some of you reading this probably do, but I don’t…) ((Actually, maybe I do. That may be the thing I use to program BASIC Stamps, though I didn’t know that at the time I bought it.)) (((But I digress…)))

For simple projects, the EZ-Key and a power supply is all you would need, provided whatever you are hooking to (like the MAME emulator) uses simple key presses. One emulator I have on my Mac uses the CTRL key for the FIRE button, so I would at the very least have to remap the EZ-Key to send a CTRL press.

For things like the iCade, which uses dual keypresses (one key for “press button down” and another for “release button”), this will not work. The EZ-Key only does single key presses.

Fortunately, the EZ-Key can also act as a Bluetooth gateway. You can hook it up to a serial port on an Arduino (or Teensy, or anything else with a UART) and write data to it at 9600 baud. This data is either sent out as a simple key down/key up press, or you can do more advanced things like send modifier keys (CTR+ALT+DEL) or even mouse movement and button clicks.

Using four wires, I connected an Arduino up to the EZ-Key:

Arduino hooked to EZ-Key
Arduino hooked to EZ-Key

I was using the Software Serial Arduino library to turn pins 10 and 11 in to TX and RX:

http://arduino.cc/en/Reference/SoftwareSerial

This let me open the EZ-Key just like I would open the console and read/write data to it:


#define RX_PIN         10
#define TX_PIN         11

// Initialize the Software Serial port.
SoftwareSerial EZKey(RX_PIN, TX_PIN); // RX, TX

// We talk to the EZ-Key at 9600 baud.
EZKey.begin(9600);

EZKey.write('x'); // send 'x'

It would be very easy to make a sketch that read data through the Arduino serial console port, and sent that out via Bluetooth:


#include <SoftwareSerial.h>

#define RX_PIN         10
#define TX_PIN         11

// Initialize the Software Serial port.
SoftwareSerial EZKey(RX_PIN, TX_PIN); // RX, TX

void setup()
{
Serial.begin(9600);

// We talk to the EZ-Key at 9600 baud.
EZKey.begin(9600);
}

void loop()
{
char ch;

// If data is available from the USB serial console...
while(Serial.available()>0)
{
// Read a character from the Serial console.
ch = Serial.read();

// If nothing was read, it returns -1...
if (ch>=0)
{
// Write that character out to the EZKey via UART.
EZKey.write(ch);
// Echo back to the serial console.
Serial.write(ch);
}
}
}

I paired the EZ-Key to my iPad, then opened the Serial Monitor on the Arduino IDE and was able to type things there and see them show up on the iPad (inside of Notepad, or anything else that accepted keyboard input).

The next step, for me, will be to integrate EZ-Key support in my iCade interface. Right now, I support reading digital inputs and writing out iCade commands as USB keyboard presses. With just a few lines of code, I should be able to expand this to use the EZ-Key and support Bluetooth as well.

Eventually, I will roll this in to my experimental USB Host Shield version, so it can also read USB joysticks (rather than just digital input switches) and output them as iCade USB keyboard or Bluetooth messages.

My EZ-Key sample program is now on GitHub:

https://github.com/allenhuffman/EZKeyTest

Stay tuned…

Simple scrolling LED Sign for NeoPixel (WS2811) or LPN8806

  • 2014/03/16 Update: The source code to this is now on GitHub. Check the Arduino link at the top of each page of this site.

Yesterday evening, I coded up a simple scrolling message sign that uses addressable LED strips like the Adafruit NeoPixels (WS2811) or LPN8806. The code I created is built for NeoPixels, since those were the ones I had access to, but it would be trivial to make it work with the Adafruit LPN8806 library. Future versions will make this simpler.

First, let’s talk about LED signs.

The BetaBrite is a commercially available scrolling message sign that’s been around for ages. I bought one at SAM’S CLUB back in the late 1990s. The BetaBrite that I have uses an 80×7 array of LEDs. This is what I will be trying to replicate.

If you shop around (ahem, e-Bay), you can find 1 meter long WS2811 LED strips with 60 RGB LEDs for around $8-$9. If you had seven of those, you could make a 60×7 LED sign. It wouldn’t be able to show as many characters at the same time as a BetaBrite does, but it would be good enough to experiment with. (There are strips with 144 pixels per meter, but they are very expensive. And, when you get past 500 or so LEDs, you start running out of memory on the Arduino. I plan to fix this with some updates to the LED library, eventually.)

Consider this wonderful drawing as I discuss a few possible ways to present a sign made out of LED strips:

LED Sign ideas

A. At the top is an example of one of these LED strips with LED number 0 to “n”. One end hooks to the Arduino and power, and the other end can be used to daisy chain multiple strips together. The first LED will be 0, and they count up to the end of the last strip. If you have three 60 LED strips, you have LEDs 0 to 179. The green arrow shows the direction of the data (the LEDs count up in that direction).

B. Next is an example of how you might arrange multiple strips so they could make up an LED sign. Each strip is shown running left-to-right, so at the end of the first strip the cables go all the way back to the left to connect to the start of the next strip. Wiring them like this makes it real easy to do things with. Notice that the green arrow runs left-to-right on each row.

C. However, it would be much much easier to just connect them like this, without all the extra wires running around. But, this causes every other row to run in the opposite direction (again, see the green arrows). This means the software has to be smart enough to know how to reverse drawing the pixels for every other row.

ALSO, based on where you decide to make LED 0, that changes everything. In these drawings, we are hooking the Arduino up at the top left. But, if it was easier to hook up at the bottom right, the entire numbering system would be backwards.

I decided to write a simple LED message program that could handle all of this. It’s not pretty, but it (maybe) works. I configure it with the number of LEDs in use, and how many are in each row, then I set where the start pixel is (TOPLEFT, TOPRIGHT, BOTTOMLEFT or BOTTOMRIGHT). I support running the rows STRAIGHT (A) or ZIGZAG (B). It can even do something fun…

D. This is the only thing I have actually done. I had two 1 meter strips, so I decided to spiral them with 20 LEDs in each spiral before the next row starts. These 120 LEDs can be split up in my program as six rows of 20 LEDs each, and then (with a small enough font), a message can rotate around it.

If you’d like to try out my code, I have posted it to GitHub:

https://github.com/allenhuffman/LEDSign

I have only tested it in the D configuration, but I have done some debug prints that make me think it should be handling all the other variations. Until I have access to more LED strips, I won’t know for sure.

Anyone want to try it out and let me know how it works for you?

Poor documentation, and the code could be cleaned up and optimized quite a bit. Perhaps I will have that done when I reach version 1.0.

Here’s a video of my first working version:

Cheap Arduino Wiznet 5100 ethernet shield

e-Bay seller kbellenterprises has a Wiznet 5100 ethernet shield for the Arduino for just $11.49 with free shipping. And, it ships from Missouri (fast). I have not used this shield, but it’s a great price if it works:

http://www.ebay.com/itm/Wiznet-W5100-Ethernet-Shield-for-Arduino-MicroSD-UNO-MEGA2560-FAST-US-SHIP-/171264625782?pt=LH_DefaultDomain_0&hash=item27e02acc76

This seller also has various Arduino clones (a Leonardo clone for $12.75 and a Leonardo mini for $9.99) with free shipping as well.

Arduino WS2811 scrolling message sign

I have two 1m 60 LED WS2811 strips, and expect to receive several more in coming weeks from Chinese suppliers. Once I have seven of them, I plan to use them as a scrolling message sign.

However, with two 1m strips (120 LEDs total), you can spiral them loosely (not tight enough to break the strip) and end up with six rows… Sorta. And, with a quick Arduino sketch (making use of some TVout fonts)… You can create a mini-circular scrolling message sign.

Here is “HELLO WORLD”:

Just for fun…

2014 Halloween project update

I have been sent a few things I will be using to make a prototype for the Halloween project. The main features will be playing audio on demand, and switching lights (or other 120V items) on and off in time to the audio. Here are the items I will be evaluating:

  • MP3 Shield ($19.99) from CutiDigi.com. This shield has its own flash storage and lets you load MP3 files over from SD memory cards or USB thumb drives. In addition to control buttons on the shield itself (vol+/-, pref, next, play), it also has buttons to start up the copy operation and put the unit to sleep. There is a 1/8″ headphone style jack for getting audio out. It is controlled via serial (tx/rx) and can be made to play a specific track number off the memory.
  • Relay Shield ($7.59) from e-Bay seller happyvalley009. This shield has four relays that can handle 120V up to 3amps, which is a small amount but enough for our needs. It is dangerous to run 120V in to a shield like this, as a short could really cause some problems. A better solution might be to use a separate relay board ($8.49 with Amazon Prime shipping) that is controlled without being attached to the Arduino itself. That would let it be physically separate and still be controlled the same way (and, this one can handle more amperage).

As soon as my funding source returns from vacation, we will order more items and begin working on a prototype.

Pac-Man source code now on GitHub

For those who wish to follow along with my Pac-Man project without having to copy/paste source code from these articles, I have posted my current work-in-progress source (which includes many changes I have not discussed, yet).

https://github.com/allenhuffman/PacMan

I have broken the program up in to multiple files, but since this makes quite a mess in the IDE, I am probably going to combine all the bitmaps in to one file instead of separate ones.

If you want to play with it, you can modify the Joystick code to read whatever you have for input (buttons, analog joystick, whatever) and at least move the Pac-Man around the screen. There is no ghost collision detection, no scoring, and no dot handling yet… But it’s a fun demo.

Arduino ethernet for $10

Here is a heck of a deal. This Arduino Ethernet Shield uses the WizPro 5100 like the official Arduino shield:

http://www.icstation.com/product_info.php?products_id=1494#.UxXX3TK9KSM

If this $10 shield works the same, it is a very cheap way to get internet connectivity out of an Arduino. If you get one, let me know how it works for you.

The shield I used for my Ethernet experiments was made by Sainsmart and it runs about $20:

And for those who don’t want to wait for mail order (from China?), you can get a $20 Seeed ethernet shield at your local Radio Shack:

http://www.radioshack.com/product/index.jsp?productId=13356964

Pretty cool.

Arduino Pac-Man part 10 – the story so far

See also: part 1, part 2, part 3, part 4, part 5, part 6, part 7, part 8, part 9 and part 10.

So far, there have been nine installments to this rambling series on creating a Pac-Man style game for an Arduino using the TVout video library. During the course of this discussion, many small things have changed so I thought it might be a good time to reboot the series. Here is a brief updated overview of the process so far.

0. Inspiration and Evolution

One night, two weekends ago, I decided to try the TVout video library for Arduino. I cut up an old A/V RCA cable and used two resistors to connect it to my UNO and soon was running the TVout demo. I next created a small program to draw a line, square, and circle. Then I decided to make the circle bounce around the screen like a ball, followed by bouncing multiple balls. I soon replaced the circle with a bitmap graphic so I could bounce a different shape, and ended up making that shape a Pac-Man which I could steer around the screen, avoiding the balls. Somehow this led me to decide to create a full-on Pac-Man style game.

1. Creating the Maze

Pac-Man arcade screen
Pac-Man

I would be using the default TVout video resolution of 120×96. The original Pac-Man resolution is 224×288 and uses a monitor that is turned sideways (portrait mode) so it is taller than it is wide. If I just scaled that screen size down proportionally, it would become 75×96 and make for a very small maze.

I decided to take the approach used by most home versions that would be played on traditional monitors (landscape) and crop off the top and bottom area (score, lives left, level) so only the maze would be left. The maze alone was 224×248.

I began the process of drawing out the maze, pixel by pixel, on top of this scaled down graphic. As I did this, I had to adjust my maze size a bit so everything would line up. Some later research revealed that the original Pac-Man screen was divided up in to 8×8 tiles of 28 across and 36 down. (Or, when the top and bottom where cropped, 28×31.) In order to get 31 tiles vertically out of the TVout’s 96 pixel tall resolution, I did some simple math and divided the height of the TVout screen (96 pixels) by the number of tiles I wanted to display (31). 96/31=3.096. It seemed I would need to round to 3×3 tiles.

pacman-arduino-270x297
Pac-Man Arduino screen.

Going across the screen (28 tiles) at three pixels each was 84 (3*28=84). Going down the screen (31 tiles) at three pixels each was 93 (3*31=93). My scaled down Arduino maze would be 84×93, and that would allow it to be the same number of tiles as the original, just using smaller tiles.

I used a 3×3 grid to draw out the full maze, and took the liberty of making the side walls a bit wider than they really should be to make them look better. (If you look at the thick walls around the ghost house in the center, that is how the entire outer wall should be for the scaled down lines to be the same dimensions. That is the only thing about this maze recreation that isn’t dimensionally accurate.)

I used a website to convert my graphic file in to C data statements that could be displayed by the TVout bitmap library function.

2. Sprites

Pac-Man sprites
Pac-Man Arduino sprites.

I next created the ghost and Pac-Man character sprites to use in the game. The original arcade sprites were just under two 8×8 tiles wide and tall (less than 16×16 pixels) and would fit in the maze corridors. Since my corridors were now 5 pixels tall or wide (just under two 3×3 tiles) it seemed to work out perfectly to make my game characters 5×5. I designed Pac-Man with three frames (mouth closed, partially open and fully open) just like the arcade, and created versions for all four directions. For the ghosts, they had two frames of animation, and a version for all four directions (with the eyes facing that way). I drew these in a graphics program first, then hand entered them as data in the C source code.

2. Moving – some new stuff here!

Moving through the maze was a challenge. Originally I tried to detect walls by looking for set pixels, but I kept running in to places where the characters would get stuck at the rounded corners. After I learned about how Pac-Man used the tile system, that provided a nice and simple solution: I created a binary map that represented all of the tiles on the screen (28×31) and put a 1 where a wall would be and a 0 where a hall/path would be.

Once I had this, I worked out a system which would track which tile a sprite was in, and check for tiles around it to see if it could go in that direction. This is the part of the project that has evolved the most over the past week as I learned that tiles were also used for how the ghosts knew where to move (they head towards a specific tile), and how collisions between ghosts and Pac-Man were done.

Ghost sprite moving right.
Ghost sprite moving right.

Pac-Man considered a sprite to be in whatever tile the center of the sprite was in. Because of this, I would be tracking the X/Y coordinate of each object based on its center. In a 5×5 sprite, the center would be at (3,3) if we used base-1 and started with the top left pixel at (1,1) and the bottom right pixel at (5,5). In order to allow sprites to move smoothly, I would also track an X/Y offset within  pixel. In the diagram to the right, the top ghost appears at the center of, for example, tile (10,10) with an X offset of 0. As it moves to the right (second ghost), it would still be in tile (10,10) but would have an X offset of 1. As it moves to the right again (third ghost), its center would now have it be in tile (11,10) with an X offset of -1 from that tile. Lastly (four ghost), it would be in tile (11,10) with an offset of 0.

I would need, at the very least, for each sprite to contain something like:

uint8_t x,y;
int8_t  xOffset, yOffset;

To figure out where on the screen to draw the sprite, I would take the tile coordinate, and multiple it by the size (3 pixels tall or wide), and then add the offset. In the above example, tile (10,10) with an X offset of 0 (in the center) would correspond to screen pixel (30,30). As the ghost moved to the right, it would be at (31,30) and (32,30). I also needed to add a few pixels to X and Y to compensate for the maze bitmap being a bit wider than the tiles under it. (NOTE: This is simplified. In the actual code, I also add half the width of the tile to keep the X/Y positions representing the center.)

To keep from having to do this kind of math (MAZEX+x*3+xOffset) every time an object was displayed, I thought I might also track the screen position separately:

uint8_t xScreen, yScreen;

I would need to do some testing and see if the overhead of the formula to calculate screen position every time was better than tracking two separate X/Y coordinates (one for tile, one for screen). Therefore, this approach is still something that could change. It would make the code seem simpler, but perhaps at a cost of extra CPU cycles needed (versus just having a variable around).

Objects also need to know what direction they were headed in. Initially, since I had started with a bouncing ball demo, I gave each object a directional offset variable that could either be +1, -1 or 0 depending on if the object was moving right/up, left/down or stopped. I was calling them “mx” and “my” (movement X, movement Y) and to move, I would just do something like:

x = x + mx;
y = y + my;

As I got further in to development, I decided that since no object ever moved diagonally (thus, no mx=1 at the same time my=1), using two variables was not needed. Instead, I could just track the current direction with one, that would be set to UP, LEFT, DOWN or RIGHT:

uint8_t direction;

And, because ghosts look one tile ahead to decide where they will turn when they get there, and because Pac-Man allows the player to push the joystick in a different direction as he is moving and instantly be ready to turn there, I added:

uint8_t nextDirection;

When I share the updated code, this will make more sense, and the new code should also be much simpler.

3. Ghost A.I.

With the basics of a maze and ability to move around it figured out, I then moved on to the logic of the ghosts. Pac-Man ghosts basically just try to move towards a target tile. That tile either causes them to scatter to a corner, or to try to track down Pac-Man, based on the mode the game is in.

To achieve this, I created some functions that let the ghost determine what it’s next direction should be at every intersection. It does this by comparing the tiles it could turn to and seeing which one is the closest to the target tile. Once I had targeting working, the only thing left to do would be add code to change the tiles as the game modes changed (from scattering to the corner, to chasing Pac-Man, to running away frightened after an energizer pellet was eaten).

There are also some special rules that needed to be taken care of, like the four passageways that ghosts are never allowed to turn up, and handling the side tunnels.

4. Dealing with Dots

A big portion of the project, currently left untouched, is figuring out how to handle all the dots on the screen. I have previously shared my idea on how this will be done, but I have yet to write any code to test this idea (this will be next). Once this is done, there will be very little left to do for the “engine” of this game to function.

5. Miscellaneous TO DO

The final part of this project will include coding some specific routines to handle things like:

  1. Scoring as Pac-Man eats dots.
  2. Collision detection between Pac-Man and Ghosts.
  3. Changing mode to FRIGHTENED when Pac-Man eats an energizer pellet.
  4. Displaying bonus fruit at appropriate times and scoring if eaten.
  5. Altering speed of Ghosts and Pac-Man based on levels and mode. Also, ghosts slow down in the tunnels, Pac-Man is slower while eating dots, and Pac-Man can slightly cut corners when turning.
  6. Intro screen, intermissions and high scores.
  7. Sound.
  8. …and probably many other things I haven’t even thought about yet. For instance, just yesterday, I saw a “cut scene” between levels that I had never seen before. I will have to find videos of all of them to try to recreate them.

And with that out of the way, it’s time to resume this project reboot, and share the current state of the code.

To be continued…

Arduino Pac-Man part 9 – ghosts

NOTE: As mentioned in the last post, many of these are written at a time then posted over the next week. This is another example of that. Initially, I had planned to write some test code and get the dot stuff working, but I decided that the ghost movement would be more fun to work on. And, as previously mentioned, I have had more time to think about things by the time you read this, so much of what you will read here (“thoughts in progress”) have evolved. I will be sharing new things with you in part 10, including a substantial change to how movement is tracked (which solved two problems I encountered with the approaches I have been taking). Spoilers: below is a short video of how it is turning out… Details soon. Until then, read part 9…

It is time for things to get a bit deep as we look in to making the ghosts act like ghosts. So far, we have have figured out how to have our sprites move across dots and either eat them or redraw them. Now it’s time to figure out how the ghosts will be moving.

Pac-Man ghosts have five basic modes they are in. They may be stuck in the ghost house waiting to exit, they may be disembodied eyes trying to return to the ghost house (after being eaten by Pac-Man), or they may be in SCATTER, CHASE or FRIGHTENED mode.

For excellent descriptions and photos of these modes, see the excellent Pac-Man Dossier site, or the excellent GameInternals site that focuses just on the ghosts. Both are quite excellent.

SCATTER is the simplest of the modes. The ghosts will be trying to reach a specific tile, and simply turn at any intersection in the direction that would get them closest to it. The target tiles are outside the maze, so the ghosts will never reach them. Each ghost has a target tile in a different corner, which is why ghosts appear to have favorite corners they will head to during the game. If left in SCATTER mode, the ghosts would circle endlessly trying to reach the impossible tile.

CHASE changes these targets so they track Pac-Man. The red ghost, Blinky, starts outside of the ghost house and its CHASE target tile is whatever tile Pac-Man is in. In chase mode, Blinky is the only ghost that is directly pursuing Pac-Man.

Pinky, the pink ghost, targets four tiles in front of Pac-Man. A bug in the original Pac-Man code causes the target tile to be wrong when Pac-Man is going UP, and it will be shifted four tiles to the left as well. I plan to replicate this bug. (Interesting note: If Pac-Man heads directly towards Pinky and gets within four tiles, this causes the target to be behind Pinky. If there is an opening in the side of the hallway between Pac-Man and Pinky, Pinky will turn down that opening trying to backtrack and reach the target that is now behind him.)

Inky, the blue ghost, has a much more complex target. It is based on two tiles in front of Pac-Man, and the location of the red ghost. The target tile is created by taking a vector that runs between the red ghost and two tiles in front of Pac-Man, then doubling that length. Yowza. Due to the same Pinky bug, when Pac-Man is facing up, the tile used is actually two up and two left from Pac-Man.

Clyde, the orange ghost, bases his target on how close to Pac-Man he is. If he is eight tiles or more away, he targets Pac-Man just like Blinky. If he is closer, he starts targeting his scatter mode tile and heads to his corner. This means Clyde should never catch Pac-Man unless you get in his way as he is trying to flee back towards his corner! If you park Pac-Man in a corner, Clyde will never reach him unless that corner is on the way to Clyde’s home corner. Cool.

And to think… some folks figured this out back in 1982 and became world champions on this game! (And they did that without MAME and disassemblies of the game code.)

And lastly, we have FRIGHTENED mode. When one of the energizers is eaten, the ghosts reverse direction (as they do when any mode change happens) and then they wander around randomly at a slower speed. During this, if Pac-Man collides with a ghost, the ghost is eaten, points are scored, and the disembodied eyes of the eaten ghost target a tile above the ghost house so they can return there and resurrect as a ghost again.

Just think of all the Pac-Man ripoffs written over the years for home computers and consoles that used randomness for the ghosts… The only bit of randomness in the original Pac-Man was when they were running away, frightened!

There is alot to cover here, so let’s start small with how the ghosts actually target a tile. Every time the ghost moves to a new tile, it looks ahead to the next tile and determines which way it will go when it reaches it. Ghosts never reverse except when switching modes, so there can be up to three choices. If the next tile has only one way to go, that is chosen. If there is more than one choice, the distance between each of the choice tiles and the target tile will be compared and the shortest one will be chosen. In the case that multiple tiles are the same distance to the target, a priority of UP, LEFT and DOWN is used to choose.

It sounds like we need a function to return the distance between two tiles. It might look like this:

// Return the distance between two coordinates. Math is hard.
uint8_t getTileDistance(int8_t x, int8_t y, int8_t targetx, int8_t targety)
{
  int8_t distx, disty;
  distx = targetx-x;
  disty = targety-y;
  return sqrt( (distx*distx)+(disty*disty));
}

Yes, that’s the Pythagorean Formula theaory thing (A squared plus B squared equals C squared.) we learned back in high school algebra to determine the size of the edges of a right triangle. I knew someday it would come in handy, but since this was the first time it ever has, I had to look it up since I couldn’t remember much from classes I took over 30 years ago!

NOTE: Since I wrote this, my buddy Mike has pointed out a much easier way to achieve the same end result. He suggested that since we can easily determine which direction the target is, just checking the distance between X and Target X versus the distance between Y and Target Y should be enough. I will be revising things in a future article to use this approach. A benefit of this is that we will also save some CPU time. The sqrt() function uses floating point math, which is slower than quick integer math if you don’t have dedicated floating point hardware to help out (the Arduino doesn’t, does it?). But I digress. As you were. Nothing to read here, yet.

One more thing to consider is that ghosts cannot reverse direction unless their mode changes. Because of this, as potential directions are scanned, reverse can never be one of them. It looks like we need a simple way to figure out which direction is reverse. We could brute force it like this:

uint8_t getOppositeDir(uint8_t dir)
{
  if (dir==LEFT) {
    return RIGHT;
  } else if (dir==RIGHT) {
    return LEFT;
  } else if (dir==UP) {
    return DOWN;
  } else if (dir==DOWN) {
    return UP;
  } else {
    return dir;
  }
}

But, as Mike pointed out, reversing from DOWN would always be slower since it has to check LEFT, RIGHT and UP before it gets there. Maybe we could do it using switch/case statements:

uint8_t getOppositeDir(uint8_t dir)
{
  switch(dir)
  {
    case LEFT:
      return RIGHT;
    case RIGHT:
      return LEFT;
    case UP:
      return DOWN;
    case DOWN:
      return UP;
    default:
      return dir;
  }
}

The end result is the same, but depending on how the compiler generates code, this could be more efficient since it may be using lookup table so jumping to DOWN would be as quick as jumping to LEFT. (Highly optimizing smart compilers may do things like this with IF/THEN logic anyway. I believe the compiler made at the place Mike and I used to work actually did that, allowing programmers to write less efficient brute force source code in some instances and still generate efficient machine code… Hmm. At my current job, I work with one of the compiler engineers from back then, so I may have to ask his opinion sometime. But I digress…)

Or we could do something more clever…though clever doesn’t always make faster or smaller code. Time for a quick test!

The directions are represented by 0=RIGHT, 1=LEFT, 2=UP and 3=DOWN. (NOTE: This is going to change in the next article, for reasons I will explain then.) This is lucky because if you look a the bit patterns for 0-3, you get 00, 01, 10, 11. If you reverse from RIGHT (00) to LEFT (01), you see the difference is toggling that right most bit. If you reverse from UP (10) to down (11), you see the difference is toggling that right most bit. Knowing this pattern, we could write a simple function to flip the right most bit:

uint8_t getOppositeDir(uint8_t dir)
{
  if (dir & 1) // If rightmost bit is set,
  {
    return dir & ~1; // Turn it off.
  } else {
    return dir | 1;  // Else, turn it on.
  }
}

This wouldn’t have been the case if the directions had been represented clockwise as 0=UP (00), 1=RIGHT (01), 2=DOWN (10), and 3=LEFT (11). If that were the case, switching from LEFT (11) to RIGHT (01) would have toggled the left most bit, and UP (00) to DOWN (10) would have toggled…the left most bit. Oh. Apparently it could have worked that way too, just by changing which bit was being tested.

Well, if the directions were in the order of ghost priority (used to determine which direction the ghost turns in case there is a tie between potential tile distances to the target) 0=UP (00), 1=LEFT (01), 2=DOWN (10) and 3=RIGHT (11), then UP (00) to DOWN (10) would toggle the left most bit, and LEFT (01) to RIGHT (11) would toggle…the left most bit. Okay, that works too.

But SURELY it wouldn’t have worked if the order was more random like 0=UP (00), 1=RIGHT (01), 2=LEFT (10) and 3=DOWN (11). UP (00) to DOWN (11) toggles both bits, and LEFT (10) to RIGHT (01) toggles both bits. That would be  easy to do in C to by using the “~” bitflip thing.

Screw it. It looks like with only four bits there would be a way to flip using bit math. This is useful, since having the directions in the order of priority will come in handy later. (This is the reason for changing directions in a future article.)

NOTE TO SELF: Do that. It makes things easier. (NOTE BACK FROM SELF: Already did, next article. Move on.)

Which one we end up using depends on things like code size, speed, and memory. I compiled a sketch with an empty setup() and empty() loop, and added just these functions, one at a time. Here are the results:

getOppositeDir() if/then version – 466 bytes
getOppositeDir() switch/case version – 466 bytes
getOppositeDir() bit flip version – 466 bytes

How can that be? Because the compiler is smart. When I created the empty test code:

#define RIGHT 0
#define LEFT  1
#define UP    2
#define DOWN  3

void setup()
{
}

void loop()
{
}

uint8_t getOppositeDir(uint8_t dir)
{
  switch(dir)
  {
    case LEFT:
      return RIGHT;
    case RIGHT:
      return LEFT;
    case UP:
      return DOWN;
    case DOWN:
      return UP;
    default:
      return dir;
  }
}

…the compiler realized nothing was actually using that function and discarded it. If anything were using it, the compiler would have to keep the code. Let’s try this:

#define RIGHT 0
#define LEFT  1
#define UP    2
#define DOWN  3

void setup()
{
  Serial.begin(9600);

  Serial.println(getOppositeDir(DOWN));
}

void loop()
{
}

uint8_t getOppositeDir(uint8_t dir)
{
  if (dir==LEFT) {
    return RIGHT;
  } else if (dir==RIGHT) {
    return LEFT;
  } else if (dir==UP) {
    return DOWN;
  } else if (dir==DOWN) {
    return UP;
  } else {
    return dir;
  }
}

By printing the result, the code has to run to get the result. Now we get:

getOppositeDir() if/then version – 2214 bytes
getOppositeDir() switch/case version – 2214 bytes
getOppositeDir() bit flip version – 2214 bytes

We can see that using the Serial.xxx() functions adds quite a bit of code bulk, but the sizes are still the same. If the compiler was getting rid of unused code before, but why are the sizes still the same? Could each function really generate the same amount of code? If it was, then it wouldn’t matter which one we used — at least, not just based on code size. More on this in a moment…

Without looking at the assembly code generated by the compiler, I cannot say which one is more efficient. For now, we will use the switch/case approach since it is really easy to understand and doesn’t make the code any larger. Also, that will work regardless of what values are chosen for UP, DOWN, LEFT and RIGHT so if I change them later (NOTE: which I am doing), I won’t have to rewrite the getReverseDir() function like I would with the bit flipping version.

But wait! There’s more! While standing outside during a file alarm the other day, I mentioned this to the previously mentioned compiler engineer I used to work with… He suggested another option: a lookup table. It might look like this:

// Directions are: 0-RIGHT, 1-LEFT, 2-UP and 3-DOWN
// Therefore, reverse directions in this table would be:
const uint8_t oppositeDir[] = { LEFT, RIGHT, DOWN, UP };

uint8_t getOppositeDir(uint8_t dir)
{
  return oppositeDir[dir];
}

See what that does? If your original order is 0, 1, 2 and 3, you could create an array that contains four numbers in whatever order you want, like 1, 2, 3, and 2. Then if you asked for the element of the array that matches your original direction, it returns whatever is there (which is the opposite direction number). You wouldn’t even have to use a function: reverse = oppositeDir[dir];

getOppositeDir() array version – 2214 bytes

Dambit. Either these really are taking the same amount of space, or there is some compiler stuff going on where the size of the binary is being rounded up, or things are being optimized out.

Okay, one last try. This time, we’ll do more with the function than just change one hard coded value. A really smart compiler could see “oh, this is only being done once, and it’s being done with a 3, so I can just pull in the code that handles the 3 case and discard the rest.” Is this compiler that smart? Here is my test code, with all four variations included:

#define IFTHEN
//#define SWITCHCASE
//#define BITFLIP
//#define LOOKUPTABLE

#define RIGHT 0
#define LEFT  1
#define UP    2
#define DOWN  3

void setup()
{
  uint8_t dir;

  Serial.begin(9600);

  for(dir=0; dir<3; dir++)
  {
    Serial.println(getOppositeDir(dir));
  }
}

void loop()
{
}

#if defined(IFTHEN)
uint8_t getOppositeDir(uint8_t dir)
{
  if (dir==LEFT) {
    return RIGHT;
  } else if (dir==RIGHT) {
    return LEFT;
  } else if (dir==UP) {
    return DOWN;
  } else if (dir==DOWN) {
    return UP;
  } else {
    return dir;
  }
}
#endif

#if defined(SWITCHCASE)
uint8_t getOppositeDir(uint8_t dir)
{
  switch(dir)
  {
    case LEFT:
      return RIGHT;
    case RIGHT:
      return LEFT;
    case UP:
      return DOWN;
    case DOWN:
      return UP;
    default:
      return dir;
  }
}
#endif

#if defined(BITFLIP)
uint8_t getOppositeDir(uint8_t dir)
{
  if (dir & 1) // If rightmost bit is set,
  {
    return dir & ~1; // Turn it off.
  } else {
    return dir | 1;  // Else, turn it on.
  }
}
#endif

#if defined(LOOKUPTABLE)
// Directions are: 0-RIGHT, 1-LEFT, 2-UP and 3-DOWN
// Therefore, reverse directions in this table would be:
const uint8_t oppositeDir[] = { LEFT, RIGHT, DOWN, UP };

uint8_t getOppositeDir(uint8_t dir)
{
  return oppositeDir[dir];
}
#endif

To test, I comment out all of the defines at the top except for the one I want to test, then I check each one.

getOppositeDir() if/then version – 2264 bytes
getOppositeDir() switch/case version – 2264 bytes
getOppositeDir() bit flip version – 2244 bytes
getOppositeDir() array version – 2246 bytes

Finally! Indeed, it does appear the compiler was smart enough to figure out that the functions as only being called once with a specific value — so why keep the rest of the code around? (If this is true, that’s pretty cool.) It seems the bit flip version is the smallest, coming in 20 whole bytes smaller than the brute force if/then. But, remember that it would have to be recoded if the direction order ever changes (which it will be for me, so that would have created a bug if I didn’t remember to change it). The array version is 18 bytes smaller, and it, too, would have to change if the directions ever changed. And none of this tells us which one executes faster necessarily, which might be very important to speed up a video game.

But I digress… Optimizations could be the topic for a whole series, and will no doubt come up again in this series.

Wasn’t that fun? Where was I? Oh, right. Looking ahead…

Now all we need to do is look ahead and see what tile options there might be. Here is how it should work:

The next tile in the direction the ghost is running should be determined. Then, from that tile, all the tiles around it (not including reverse, which isn’t an option since a ghost can’t go backwards) should be checked to see how far they are from the target tile. They will be checked in the priority order of UP, LEFT, DOWN and RIGHT. If a shorter distance is found, remember that direction, else continue. This will take care of distance ties and the priority. If UP is checked and it is 5 away, then LEFT is checked and it is 6, it will be skipped. Next, if DOWN is checked and it is also 5, it won’t be recorded since we already have a 5 (UP, a higher priority).

To do this, we need to check the directions in the order of 2 (UP), 1 (LEFT), 3 (DOWN) and 0 (RIGHT). If only these priorities lined up with our directions this would be simple. Since they don’t, we may have to use an array like this: (NOTE: Now see why changing the directions will help out? None of the things involving priorityDirs[] will need to be used, but I am keeping it all in this article to show how the concept evolved.)

PROGMEN const uint8_t priorityDirs[DIRS] = { UP, LEFT, DOWN, RIGHT };

// Do this for each ghost.
for (i=0; i<GHOSTS; i++)
{
  // Get the file in front of us.
  getNextTile(gx[i], gy[i], ghostDir[i], &nextX, &nextY);

  // Now, from that tile, see what options might exist.

  // We cannot reverse, so we should ignore that direction.
  // Storing this in a variable for speed.
  uint8_t reverseDir = reverseDir(ghostDir[i]);

  for (j=0; j<DIRS; j++)
  {
    uint8_t potentialX, potentialY;
    uint8_t tempDist;
    uint8_t shortestDist = 255; // Max distance.

    // Skip reverse dir since it's not a choice.
    if (j==reverseDir) continue;

    // What tile is in that direction?
    getNextTile(nextX, nextY, priorityDir[j], &potentialX, &potentialY);

    // Get distance from potential tile to target tile
    tempDist = getDistance(potentialX, potentialY, targetX, targetY);
    // If closer than previous closest,
    if (tempDist < shortestDist) {
    {
      shortestDist = tempDist;
      tempDir = priDir[j];
    }
  }
  // The next direction the ghost should go is...
  ghostDir[i] = tempDir;
}

Could this actually work??? To find out, we can combine this mechanism with the dot merchanism, and see if the ghosts will now SCATTER to their corners and behave like Pac-Man ghosts should behave…

To be continued…