Multiplayer implementation ideas and stream of consciousness for Speed Snake

I was thinking just now about multiplayer games.

There’s a couple of popular control methods,

Such as the “take turns” system that works well for board games (chess, chequers) and turn based games like Teeny Tank:

Then there’s two people sharing the same Arduboy:

There’s also peer to peer (hard to implement - even needs micro to micro cable):

Then there’s using a PC as a hub - the “Server” as it where.

I wrote a game people loved in school on the Archimedes (over 25 years ago now) - a multiplayer (up to 4 players) “Snake” that had “speed up” and “slow down” apples, “Slow down ALL other players” as well as a “tunnel” button that made a “worm hole” under the ground that immediately moved the snake forward several pixels.

The dynamics made was interesting - a slow snake was at a disadvantage - there would be often spirals created where the faster snake on the “outside” of the spiral keeps getting ahead and turning towards the slower worm. They’re done for! But no! They can worm-hole out of there - an instant jump 20 pixels forward under the faster worm. A sudden turn, and the fast worm is dead!

The speed implementation was easy - instead of “X += 1”, the speed would be a decimal “X
+= speed” where speed is something like 0.1 Checking at the “head” of the worm just as it moves a pixel is used for detection. This is better than always checking one pixel ahead - which would not give a slower travelling snake a chance to turn out of the way of the body of a worm in front of them.

I think I’ll use the PC as a server - my question then is would several Arduboy’s connect to the PC on different COM ports?

I could poll the COM ports in .Net for Arduboy names, and keep track of them as they’re added and removed.

The interesting bit then is the abstraction of the worms head position and how its direction changes, and then the body.

Would I send run-length-encoded body updates? Sounds good - draw the head from the head backwards - e.g. up 20 pixels from the head, left 20, down 10. That’s 2 bytes for each corner. Direction, and length. Or maybe even one byte? Bit 0 and 1 are direction, bit 2 to 7 are length - up to 32 pixels…

If the connection is ALWAYS solid - we could send a single byte per snake on each frame that says “Situation same as last time”, so no “path description” is sent instead of the 1 to 9 bytes depending on snake turns. Say there’s 4 players and 10 turns each - that’s 40 bytes per frame, and maybe 30 frames a second. The speed of the serial connection would have to be high to move those 1,200 bytes per second. Hm, maybe 15 frames a second?

Maybe sending a zero 5 times then a number - tells the devices to start that phase of the game - waiting in the foyer, playing the game, viewing the final scores and winner, and back around.

Graphics are easy - pixels. Though how big to make the board? Larger than the display? How’d we store the board, and detect hits? Check bytes? Use the “GetPixel” function? It’s likely better on the server… the PC.
In that case - we can use a 2D array of BYTE’s - a 500 x 500 board is a tiny amount of memory on the PC, and can be checked for collisions extremely easily.

We could send each Arduboy the actual OLED DISPLAY RAM it needs for its display!
Made from the 2D grid packed into stripes of vertical bytes aligned to the OLED RAM, and streamed to the Arduboy.

This opens up a new world of animations and pixel art - we’d have the PC’s memory to store them all in! The PC then streams them to the Arduboy… just like a remote desktop.

Hmm… there’s such a huge amount to design for such a simple snake game… awesome!

Ideas? Thoughts?

1 Like

The serial link would be over USB, so should be capable of adequate speeds if you can supply and process the data fast enough.

I think my idea would vary based on the game but for a game like snake you could just send the updated board to each of the Arduboys and they render them and then the server would only need the Arduboy to send which direction the head is moving in. This is assuming the server handles keeping track of the game state, power ups, etc. Then the Arduboy would only need to know how to display the board, get input, and send info to the server. Idk how well this will work but this is my first thought on how I would handle a non local multiplayer game.

Never actually tested this but I think that would be the case.

You don’t need to poll if you’re ok with making the program Windows-only.
System.Management has a ManagementEventWatcher class that can monitor device changes using WMI queries (WMI is this weird outdated SQL thing, don’t worry about it, you’ll only need one or two).
There’s a good example on Stackoverflow.

I actually wrote a little demo program a while back in C# using this.
(I never published it though.)
It could detect connecting and disconnecting.
It would transmit ascii characters typed into the console and recieve displayed data as ascii characters on the console.
It helps that the Arduboy’s default font uses the same encoding as Window’s console/terminal.

In terms of protocol for that game, I think you’d be better off focusing on state updates and/or player inputs.

If you synchronise the state information at the start and (assuming no ‘packets’ are dropped) only transmit updates, you will have fewer, shorter packets to transmit.

E.g. each packet could be a case of sending the new button state along with a timestamp (obviously the time would have to be synchronised at the start and a rough measure of transmission latency would be required).

I have in the past contemplated designing a sort of udp/ip system for the Arduboy’s serial, but it’s hard to judge how complicated it would need to be without knowing exact applications. I can’t remember where I last mentioned that but there’s a thread somewhere where I mentioned it.

Also I’d suggest detecting collisions using a virtual representation of the snake rather than pixels.
E.g. if each snake is just a series of lines and thus can be represented as a list of points, you might be better off doing line-line collision rather than doing a ‘foreach pixel’ approach.

If floats turn out to be too costly for this, there’s this neat fixed point arithmetic library you can use.

*totally not an obvious self-promotion* :P