2017-05-25 18:33:57 +02:00
# Infection
2017-09-07 22:42:08 +02:00
## ~avatar avatar
2017-05-25 18:33:57 +02:00
There is a disease outbreak! Will you find patient zero?!?
2017-09-07 22:42:08 +02:00
## ~
2017-05-25 18:33:57 +02:00
**Infection** is a distributed game which simulates
the propagation of an illness. **The goal is to stop the outbreak before every player dies!**
2017-06-04 19:42:52 +02:00
* **Number of players:** 1 Master, 4 or more players. Each Master and players need a @boardname @ with battery pack.
2017-05-25 18:33:57 +02:00
In this game, a master @boardname @ infects a "patient zero" player
with the sickness. The infected player will be contagious immediately but won't show any sign
during the incubation time. The sickness gets transmitted when two @boardname @ get close to each other.
After the incubation period, the sad face will appear on the screen. After the sickness period, the player will eventually die and a skull will display on the screen. Dead players are out of the game.
If any player survives the outbreak, the game is won. Otherwise try again!
2017-09-07 22:42:08 +02:00
## ~ hint
2017-06-01 07:52:03 +02:00
**Infection** is an engaging game that will get your students running around.
We recommend playing it outside to give more space for the students.
2017-09-07 22:42:08 +02:00
## ~
2017-06-01 07:52:03 +02:00
2017-09-07 22:42:08 +02:00
## How to play
2017-05-25 18:33:57 +02:00
2017-05-31 22:22:08 +02:00
Press `A+B` to enter master mode (1 per game).
2017-05-25 18:33:57 +02:00
Wait for players to be paired. The number of paired player will display on screen.
2017-05-31 22:22:08 +02:00
A letter will appear on the player's screen, this letter is the player's identity.
2017-05-25 18:33:57 +02:00
2017-05-31 22:22:08 +02:00
Once all your players are registered, press `A+B` to start the infection game.
The game will pick a random player as **patient zero** .
2017-05-25 18:33:57 +02:00
2017-05-31 22:22:08 +02:00
A player will transmit the disease if close enough (`RSSI`) and with a certain probability (`TRANSMISSIONPROB`).
During the incudation phase (`INCUBATION`), the player does not show any sign of illness (happy face).
After that phase, the player gets sick and shows a sad face on the screen. After the sick face, the player dies and a skull shows up.
2017-05-25 18:33:57 +02:00
2017-05-31 22:22:08 +02:00
Once the game is over, the @boardname @ will show the player id (A,B,C...), health and
who infected him. The master @boardname @ will show the identity of patient zero.
Icons used in the game:
* Pairing: IconNames.Ghost
* Paired: IconNames.Happy
* Dead: IconNames.Skull
* Sick: IconNames.Sad
* Incubating: IconNames.Confused
* Healthy: IconNames.Happy
2017-07-24 22:39:27 +02:00
https://makecode.microbit.org/_gymCJCWPbiDu
2017-05-31 22:22:08 +02:00
2017-09-07 22:42:08 +02:00
## JavaScript code
2017-05-25 18:33:57 +02:00
```typescript
/**
* Infection game
*
* Flash all micro:bit will this script
*
* Press A+B to enter master mode (1 per game)
*
* Wait for players to be paired. The number of paired player will display on screen.
* An icon will appear on player's screen.
*
* Press A+B to start the infection game. The master will pick a random
* player as patient zero.
*
* A player will transmit the disease if close enough (RSSI)
* and with a certain probability (TRANSMISSIONPROB).
* During the incudation phase (INCUBATION), the player does not show any sign
* of illness. After that phase, the sad face shows up.
2017-05-31 22:22:08 +02:00
*
* The game will automatically stop once all players are dead or healthy. The master can
* also press A+B again to stop the game.
*
* Once the game is over, the micro:bit will show the player id (A,B,C...), health and
* who infected him.
*
* Icons used in the game:
*
* Pairing: IconNames.Ghost
* Paired: IconNames.Happy
* Dead: IconNames.Skull
* Sick: IconNames.Sad
* Incubating: IconNames.Confused
* Healthy: IconNames.Happy
*
2017-05-25 18:33:57 +02:00
*/
const INCUBATION = 20000; // time before showing symptoms
const DEATH = 40000; // time before dying off the disease
2017-05-31 22:22:08 +02:00
const RSSI = -45; // db
const TRANSMISSIONPROB = 40; // % probability to transfer disease
2017-05-25 18:33:57 +02:00
enum GameState {
Stopped,
Pairing,
Running,
Over
}
enum HealthState {
Healthy,
Incubating,
Sick,
Dead
}
2017-05-31 22:22:08 +02:00
const GameIcons = {
Pairing: IconNames.Ghost,
Paired: IconNames.Happy,
Dead: IconNames.Skull,
Sick: IconNames.Sad,
Incubating: IconNames.Confused,
Healthy: IconNames.Happy
}
const playerIcons = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
2017-05-25 18:33:57 +02:00
class Player {
id: number;
icon: number;
health: HealthState;
2017-05-31 22:22:08 +02:00
show() {
basic.showString(playerIcons[this.icon]);
}
2017-05-25 18:33:57 +02:00
}
2017-05-31 22:22:08 +02:00
// common state
2017-05-25 18:33:57 +02:00
let state = GameState.Stopped;
2017-05-31 22:22:08 +02:00
// master state
2017-05-25 18:33:57 +02:00
let master = false;
let patientZero: Player;
2017-05-31 22:22:08 +02:00
const players: Player[] = [];
2017-05-25 18:33:57 +02:00
2017-05-31 22:22:08 +02:00
// player state
2017-05-25 18:33:57 +02:00
let paired = false;
2017-05-31 22:22:08 +02:00
let infectedBy = -1; // who infected (playerIcon)
2017-05-25 18:33:57 +02:00
let infectedTime = 0; // local time when infection happened
2017-05-31 22:22:08 +02:00
let playerIcon = -1; // player icon and identity
2017-05-25 18:33:57 +02:00
let health = HealthState.Healthy;
// get a player instance (creates one as needed)
function player(id: number): Player {
for (const p of players)
if (p.id == id) return p;
// add player to game
let p = new Player();
p.id = id;
2017-05-31 22:22:08 +02:00
p.icon = (players.length + 1) % playerIcons.length;
2017-05-25 18:33:57 +02:00
p.health = HealthState.Healthy;
players.push(p);
serial.writeLine(`player ==> ${p.id}`)
return p;
}
function allDead(): boolean {
for (const p of players)
if (p.health != HealthState.Dead) return false;
return true;
}
function gameOver() {
state = GameState.Over;
2017-05-31 22:22:08 +02:00
if (patientZero)
patientZero.show();
2017-05-25 18:33:57 +02:00
}
function gameFace() {
switch (state) {
case GameState.Stopped:
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Pairing);
break;
2017-05-25 18:33:57 +02:00
case GameState.Pairing:
2017-05-31 22:22:08 +02:00
if (playerIcon > -1)
basic.showString(playerIcons[playerIcon]);
else
basic.showIcon(paired ? GameIcons.Paired : GameIcons.Pairing, 1);
2017-05-25 18:33:57 +02:00
break;
case GameState.Running:
switch (health) {
case HealthState.Dead:
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Dead, 1);
2017-05-25 18:33:57 +02:00
break;
case HealthState.Sick:
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Sick, 1);
2017-05-25 18:33:57 +02:00
break;
default:
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Healthy, 1);
2017-05-25 18:33:57 +02:00
break;
}
break;
case GameState.Over:
2017-05-31 22:22:08 +02:00
// show id
basic.showString(playerIcons[playerIcon]);
basic.pause(2000);
// show health
2017-05-25 18:33:57 +02:00
switch (health) {
case HealthState.Dead:
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Dead, 2000);
2017-05-25 18:33:57 +02:00
break;
case HealthState.Sick:
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Sick, 2000);
2017-05-25 18:33:57 +02:00
break;
case HealthState.Incubating:
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Incubating, 2000);
2017-05-25 18:33:57 +02:00
break;
default:
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Healthy, 2000);
2017-05-25 18:33:57 +02:00
break;
}
2017-05-31 22:22:08 +02:00
// show how infected
if (infectedBy > -1) {
basic.showString(" INFECTED BY");
basic.showString(playerIcons[infectedBy]);
basic.pause(2000);
} else {
basic.showString(" PATIENT ZERO");
basic.pause(2000);
}
// show score
game.showScore();
basic.pause(1000);
2017-05-25 18:33:57 +02:00
break;
}
}
// master button controller
input.onButtonPressed(Button.AB, () => {
// register as master
if (state == GameState.Stopped & & !master) {
master = true;
paired = true;
state = GameState.Pairing;
serial.writeLine("registered as master");
radio.setTransmitPower(7); // beef up master signal
2017-05-31 22:22:08 +02:00
basic.showString("0");
return;
2017-05-25 18:33:57 +02:00
}
2017-05-31 22:22:08 +02:00
if (!master) return; // master only beyond this
2017-05-25 18:33:57 +02:00
// launch game
2017-05-31 22:22:08 +02:00
if (state == GameState.Pairing) {
2017-05-25 18:33:57 +02:00
// pick 1 player and infect him
patientZero = players[Math.random(players.length)];
while (patientZero.health == HealthState.Healthy) {
radio.sendValue("infect", patientZero.id);
basic.pause(100);
}
// all ready
state = GameState.Running;
serial.writeLine(`game started ${players.length} players`);
// show startup
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Dead);
2017-05-25 18:33:57 +02:00
} // end game
2017-05-31 22:22:08 +02:00
else if (state == GameState.Running) {
2017-05-25 18:33:57 +02:00
gameOver();
}
})
radio.setGroup(42);
r adio.setTransmitSerialNumber(true)
radio.onDataPacketReceived(({ time, receivedNumber, receivedString, signal, serial: id }) => {
if (master) {
if (receivedString == "pair") {
// register player
let n = players.length;
let p = player(id);
// show player number if changed
if (n != players.length) {
led.stopAnimation();
basic.showNumber(players.length);
}
}
else if (receivedString == "health") {
let p = player(id);
p.health = receivedNumber;
// check if all infected
if (allDead())
gameOver();
}
} else {
if (receivedString == "state") {
// update game state
state = receivedNumber as GameState;
2017-05-31 22:22:08 +02:00
} else if (infectedBy < 0 & &
2017-05-25 18:33:57 +02:00
receivedString == "infect"
& & receivedNumber == control.deviceSerialNumber()) {
// infected by master
2017-05-31 22:22:08 +02:00
infectedBy = 0; // infected my master
2017-05-25 18:33:57 +02:00
infectedTime = input.runningTime();
health = HealthState.Incubating;
serial.writeLine(`infected ${control.deviceSerialNumber()}`);
}
2017-05-26 00:42:55 +02:00
if (receivedString == "h" + control.deviceSerialNumber().toString() & &
health < receivedNumber ) {
health = receivedNumber;
}
2017-05-25 18:33:57 +02:00
switch (state) {
case GameState.Pairing:
// medium range in pairing mode
if (!paired & &
receivedString == "paired"
& & receivedNumber == control.deviceSerialNumber()) {
// paired!
serial.writeLine(`player paired ==> ${control.deviceSerialNumber()}`)
paired = true;
return;
}
2017-05-26 00:42:55 +02:00
else if (paired & & receivedString == "i" + control.deviceSerialNumber().toString()) {
2017-05-31 22:22:08 +02:00
playerIcon = receivedNumber;
2017-05-25 18:33:57 +02:00
}
break;
case GameState.Running:
// broadcast infection status
2017-05-26 00:42:55 +02:00
if (health == HealthState.Healthy & & receivedString == "transmit") {
2017-05-25 18:33:57 +02:00
serial.writeLine(`signal: ${signal}`);
if (signal > RSSI & &
Math.random(100) > TRANSMISSIONPROB) {
infectedBy = receivedNumber;
infectedTime = input.runningTime();
health = HealthState.Incubating;
}
2017-05-31 22:22:08 +02:00
} else if (health != HealthState.Dead
& & receivedString == "health" & & signal > RSSI) {
2017-05-26 00:42:55 +02:00
game.addScore(1);
2017-05-25 18:33:57 +02:00
}
break;
}
}
})
// main game loop
basic.forever(() => {
if (master) {
switch (state) {
case GameState.Pairing:
// tell each player they are registered
for (const p of players) {
radio.sendValue("paired", p.id);
2017-05-26 00:42:55 +02:00
radio.sendValue("i" + p.id, p.icon);
2017-05-25 18:33:57 +02:00
}
serial.writeLine(`pairing ${players.length} players`);
basic.pause(500);
break;
2017-05-26 00:42:55 +02:00
case GameState.Running:
for (const p of players) {
radio.sendValue("h" + p.id, p.health);
}
break;
2017-05-31 22:22:08 +02:00
case GameState.Over:
if (patientZero)
patientZero.show();
break;
2017-05-25 18:33:57 +02:00
}
radio.sendValue("state", state); // keep broadcasting the game state
} else { // player loop
switch (state) {
case GameState.Pairing:
// broadcast player id
2017-05-31 22:22:08 +02:00
if (playerIcon < 0 )
2017-05-25 18:33:57 +02:00
radio.sendValue("pair", control.deviceSerialNumber());
2017-05-31 22:22:08 +02:00
else if (infectedBy > -1)
2017-05-25 18:33:57 +02:00
radio.sendValue("health", health);
break;
case GameState.Running:
// update health status
if (health != HealthState.Healthy & & input.runningTime() - infectedTime > DEATH)
health = HealthState.Dead;
else if (health != HealthState.Healthy & & input.runningTime() - infectedTime > INCUBATION)
health = HealthState.Sick;
// transmit disease
if (health == HealthState.Incubating || health == HealthState.Sick)
2017-05-31 22:22:08 +02:00
radio.sendValue("transmit", playerIcon);
2017-05-25 18:33:57 +02:00
radio.sendValue("health", health);
break;
}
2017-05-31 22:22:08 +02:00
// show current animation
gameFace();
2017-05-25 18:33:57 +02:00
}
})
2017-05-31 22:22:08 +02:00
basic.showIcon(GameIcons.Pairing)
2017-06-21 20:17:25 +02:00
```
```package
radio
2017-05-25 18:33:57 +02:00
```