CryptoBotWars or How to Build Shitty Demos and Why

Why shitty ? Because I like self deprecation and the game is no where near production.

Tokens, Robots, Payment Channels and Live Streams

So I built CryptoBotWars, a game on Raiden Network.

Want to play it? Read on…

Take a sneak peek at the game, behind the scenes or live stream sample. And join our Riot chat!

Behind the scenes

Why? … I had some questions:

  1. Is it easy to build something on top of Raiden right now? Do we lack anything that is fairly easy to change?
  2. How would Raiden work right now for one-to-many & many-to-one payments, cases frequently related to businesses
  3. Would people find bugs in Raiden? The best testers are the clueless, newly onboarded testers.
  4. Would Raiden behave nicely with high throughput?
  5. Would payments be successfully mediated (various locations & system setups)

A game, especially combined with some degree of entertainment, has the potential to incentivize new people to try Raiden out. Even more so, if you add some rewards on top.

A game … or a very serious service that would accept off-chain payments 💸, but I did not have enough time for the latter, so there you go:

Dark Vader and Blue Yoda. Soup bowl & ping-pong Santa Hat! Looks great! … from 4m away 😛

This blogpost is partially dedicated to the Thai restaurant where I get my favorite Berlin coconut-tofu-veggie soup 🍲. This is why we always have a bunch of to-go bowls around to make Vader Helmets & Santa Hats 🎅.

How did the idea come about?

One day, over lunch, we were discussing ideas for the Devcon4 Raiden Network workshop, trying to think of a fun and simple app that can be built on top of Raiden. Somehow, we got to the conclusion that a game may incentivize more people to try it out.

I thought about it a bit more afterwards and decided that a game with unlimited number of users might showcase Raiden better, as it would test throughput.

But what games can you play between an infinite number of players? Maybe something that required voting for an outcome.

I remembered the Prisoner’s Dilemma pattern, where two players try to minimize their loss without being able to communicate with one another, but could not find a suitable game variant for my purpose.

I then went back to the drawing board: voting for an outcome.

Then I realized the simplest thing you can do is rock-paper-scissors. Two players, three moves, each user picks a player and a move and the majority per player decides the final move. Then the actual game can be played between the two majority-chosen moves.

Eventually, the team showcased a fun race between Deva and some known people in the space, combining the introductory workshop with Devcon-appropriate memes 🌈 and some excitement ✨.

But, I still wanted to build my rock-paper-scissors demo and ETHSingapore seemed like a good time to do it! (Dark Vader and Blue Yoda visited Singapore, but they were not included in the demo, due to other various technical issues that I had to solve 😞)

Robots Want Crypto

You have a game, you have Raiden, why add another variable to the equation?

And robots do cause a lot of pain and I’m not talking about the Robot Apocalypse. Though you see, I have been preparing for a while now to be one of the tolerated humans: uRaiden Devcon3 Robo, Drones want crypto too 💸

  1. I and my partner, who actually built the Devcon3 RC Car, really like robots. And he already brought an army of them in the house … it’s the wild robowest for people who don’t want to grow up.
  2. Robots are fun and cute (🤖 vs. 😼 meme duel now!)
  3. I put my Yoda voice to work! (the beginning of yet another career..)
  4. But actually robots give us a sense of what the future could be 🚀.

How did I start building?

~5.5 days were enough to do the initial setup & robot prototype connections for ETHSingapore.

Then, during the winter holiday I built the current game on top of the first prototype.

First things first: Testing the Robot SDK

My partner already knew that the Wonder Robots had a Python SDK and tested some of the commands. This was a good time to introduce Claudiu, a friend of ours to programming — what can be more fun than programming robots?? So he did! He added commands for the win / lose behavior, together with custom soundtracks.

Game Flow

I had already written some ideas of what the game might look like. So, instead of actually creating the game’s logic diagrams at this point, I jumped to the implementation phase. This turned out to be a bad move, because it “helped” me take some wrong decisions due to not seeing the whole picture (refactored game states & UI, faulty game server logic on calculating the outcome, this exploit bug.

I eventually fixed things later and created a better logic sequence:

Final Components

  1. Dark Vader RobotServer & Blue Yoda RobotServer
  2. GameGuardianServer
  3. GameGuardianRaidenNode
  4. GameClient
  5. Live Stream on Twitch

2, 3, 4 are hosted on a Digital Ocean VPS.

The robot servers are hosted on two home computers.

One of these computers also does the live streaming through the webcam. We tried a lot of live stream options — from the most popular to self hosted, even looking at decentralized solutions, but all of them had drawbacks (latency, resolution, dynamic stream id, time consuming to set up etc.)

GameClient UI

I actually started with the UI first.

My partner suggested to use the same slider I already had in Pipeline and show each game state on an individual slide. I thought this was a great idea. It felt like a story book.

export const GameState = {
null: 0, // No current game running
    open: 1,      // During game time, users can make moves
    closed: 2,    // During game resolution time, users wait for results and payments
    resolved: 3,  // Game and resolution has ended.
}

You can see the game state views code here.

GameGuardianServer

After having an idea about the game states, I proceeded to define the game and move models, that correspond to the database MongoDB collections. See the current version here.

I then added the GameGuardian Raiden API and implemented the game outcome logic, to calculate the winners, send the off-chain payments and trigger the robot moves. Triggered by the code? make a PR to improve it!

moves = await moveController.find({where: {gameId: id}, order: ["_id ASC"]});
raidenPayments = await this.getRaidenPayments(TOKEN)
moves.forEach(sentMove => {
if (
sentMove.amount &&
sentMove.move &&
sentMove.amount >= game.move_amount
) {
raidenPayment = raidenPayments[0].find((payment) => {
return payment.identifier === sentMove.paymentIdentifier;
});
if (raidenPayment) {
total_amount += sentMove.amount;
move_count[sentMove.playerId][sentMove.move] += 1;
validMoves.push(sentMove);
}
}
});

sorted_moves_1 = Object.entries(move_count['1']).sort((a: any, b: any) => {return a[1] - b[1]});
sorted_moves_2 = Object.entries(move_count['2']).sort((a: any, b: any) => {return a[1] - b[1]});
move1 = sorted_moves_1[2][1] > 0 ? sorted_moves_1[2][0] : null;
move2 = sorted_moves_2[2][1] > 0 ? sorted_moves_2[2][0] : RockPaperScissorsGetLoser[move1];
// If we have one player, make sure he wins
if (!move1) {
move1 = RockPaperScissorsGetLoser[move2];
}
winningMove = RockPaperScissorsGetLoser[move1] === move2 ? move1 : move2;
validMoves.forEach((move) => {
// We reward both players if their final moves are the same
if (move.move === winningMove) {
winningMoves.push(move);
}
});
guardian_amount = total_amount / 10;
total_amount -= guardian_amount;
winner_amount = total_amount / winningMoves.length;
gameUpdate = {
winningMove,
player1: <PlayerResult> {
count: sorted_moves_1[0][1] + sorted_moves_1[1][1] + sorted_moves_1[2][1],
move: move1,
move_count: move_count['1'],
},
player2: <PlayerResult> {
count: sorted_moves_2[0][1] + sorted_moves_2[1][1] + sorted_moves_2[2][1],
move: move2,
move_count: move_count['2'],
},
amount: winner_amount,
amountGuardian: guardian_amount,
players: moves.length,
};

this.updateById(id, gameUpdate);
// Make Raiden payments to winners
winningMoves.forEach((move) => {
this.sendRaidenPayment(
TOKEN,
move.userAddress,
winner_amount,
move.paymentIdentifier
);
});
this.sendRobotCommands(move1, move2, winningMove);

Time Management: The Key To Success

Or the pain of implementing timers in the client side, that must match the server.

The game is timed and the game states are also timed. Because I chose to have an infinite number of simultaneous users, I had to limit support to only one game at a time, in order to keep development simple.

So, who starts the game?

Setting up a server cron job or similar to create games on a regular basis seemed to be the worst approach, as it would fill up the database in a useless way.

I opted for a client triggered new game start. In short, if there is no current game running, then one is created by the client. If there is, the game data is sent to the client.

The game data also contains the startTime of the game, along with gameTime (how long will the open state last) and resolveTime (how long will the resolution last). The client uses these values to set the timers here and here.

The timers are used to trigger some of the requests to the GameGuardianServer. For example, when the GameOpen state has ended, the GameClient sends the raw move data. Or when the GameResolved state is reached, a request is sent to the server to resolve the game state or return the resolve state if it was already resolved, triggering the GameGuardianServer to calculate the winners, send the off-chain payments and trigger the robot moves.

Robots — final touches

The robot servers are hosted on two home computers. Why two? Because both Dash & Cue robots use Bluetooth to connect to the computer. The current SDK first disconnects all connected devices and then connects the appropriate robot.

Each robot has multiple routes for: connecting to the robot, setting the appearance, move speech commands, win/lose commands. I opted for using Gunicorn, because we need to keep the connection process alive throughout all commands.

Note that we live stream the robots from time to time and this has transformed our home in a weird mime studio.

The internet connection and home router has not made the job easier.

Conclusions

Sparked your interest? Try out the game: https://cryptoplayer.one

Chat with us to give feedback or ask for help with setting up your Raiden channels or ping us to wake up the robots! Riot chat

Part 2 coming soon with more conclusions & robot tricks… but I need to see some claps here!

Read: CryptoBotWars — Part 2: Conclusions