Rpsteams (#426)
* add rps teams project * renamed project page * fixed summary
This commit is contained in:
parent
94753dfc4e
commit
39edf10ada
@ -26,6 +26,7 @@
|
|||||||
* [Voting Machine](/projects/voting-machine)
|
* [Voting Machine](/projects/voting-machine)
|
||||||
* [Infection](/projects/infection)
|
* [Infection](/projects/infection)
|
||||||
* [Fireflies](/projects/fireflies)
|
* [Fireflies](/projects/fireflies)
|
||||||
|
* [Rock Paper Scissors Teams](/projects/rps-teams)
|
||||||
* [Inchworm](/projects/inchworm)
|
* [Inchworm](/projects/inchworm)
|
||||||
* [Milk Carton Robot](/projects/milk-carton-robot)
|
* [Milk Carton Robot](/projects/milk-carton-robot)
|
||||||
* [Milk monster](/projects/milky-monster)
|
* [Milk monster](/projects/milky-monster)
|
||||||
|
@ -52,6 +52,10 @@ Fun games to build with your @boardname@.
|
|||||||
"name": "Voting Machine",
|
"name": "Voting Machine",
|
||||||
"url": "/projects/voting-machine",
|
"url": "/projects/voting-machine",
|
||||||
"imageUrl": "/static/mb/projects/voting-machine.png"
|
"imageUrl": "/static/mb/projects/voting-machine.png"
|
||||||
|
}, {
|
||||||
|
"name": "Rock Paper Scissors Teams",
|
||||||
|
"url": "/projects/rps-teams",
|
||||||
|
"imageUrl": "/static/mb/projects/rpsteams.png"
|
||||||
}]
|
}]
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -158,4 +162,5 @@ Fun games to build with your @boardname@.
|
|||||||
[Flashing Heart](/projects/flashing-heart), [Smiley Buttons](/projects/smiley-buttons), [Love Meter](/projects/love-meter), [Rock Paper Scissors](/projects/rock-paper-scissors), [Compass](/projects/compass), [Hack your headphones](/projects/hack-your-headphones), [Banana keyboard](/projects/banana-keyboard), [Telegraph](/projects/telegraph), [Guitar](/projects/guitar), [Wallet](/projects/wallet), [Watch](/projects/watch),
|
[Flashing Heart](/projects/flashing-heart), [Smiley Buttons](/projects/smiley-buttons), [Love Meter](/projects/love-meter), [Rock Paper Scissors](/projects/rock-paper-scissors), [Compass](/projects/compass), [Hack your headphones](/projects/hack-your-headphones), [Banana keyboard](/projects/banana-keyboard), [Telegraph](/projects/telegraph), [Guitar](/projects/guitar), [Wallet](/projects/wallet), [Watch](/projects/watch),
|
||||||
[Milk Monster](/projects/milky-monster), [Karel the LED](/projects/karel), [Infection](/projects/infection), [Voting Machine](/projects/voting-machine)
|
[Milk Monster](/projects/milky-monster), [Karel the LED](/projects/karel), [Infection](/projects/infection), [Voting Machine](/projects/voting-machine)
|
||||||
[Fireflies](/projects/fireflies), [Soil Moisture](/projects/soil-moisture),
|
[Fireflies](/projects/fireflies), [Soil Moisture](/projects/soil-moisture),
|
||||||
[States Of Matter](/projects/states-of-matter), [Reaction Time](/projects/reaction-time)
|
[States Of Matter](/projects/states-of-matter), [Reaction Time](/projects/reaction-time),
|
||||||
|
[Rock Paper Scissors Teams](/projects/rps-teams)
|
||||||
|
221
docs/projects/rps-teams.md
Normal file
221
docs/projects/rps-teams.md
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
# Rock Paper Scissors Teams
|
||||||
|
|
||||||
|
### ~avatar avatar
|
||||||
|
|
||||||
|
Massively multi-player rock paper scissors!
|
||||||
|
|
||||||
|
### ~
|
||||||
|
|
||||||
|
Playing rock paper scissors is usually a two player game... but it works with much more players too!
|
||||||
|
When playing with more than two players, it becomes a team game: all players shake at the same time,
|
||||||
|
then the number of **rock**, **paper**, **scissors** is tallied between all the players.
|
||||||
|
The team with the most players wins the game.
|
||||||
|
|
||||||
|
Starting from the [basic version of the RPS game](/projects/rock-paper-scissors), we are going
|
||||||
|
to change the code so that the @boardname@ counts and displays the number of players in the same team.
|
||||||
|
Using the **radio** communication, the @boardname@ will send its status and receive the status of other boards.
|
||||||
|
|
||||||
|
Let's get started!
|
||||||
|
|
||||||
|
## Starting blocks
|
||||||
|
|
||||||
|
Let's start form a . The basic version picks the weapon in a ``|on shake|``
|
||||||
|
event and display an icon accordingly. Take a peek at the code below to refresh your memory on that game.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
let weapon = 0
|
||||||
|
input.onGesture(Gesture.Shake, () => {
|
||||||
|
weapon = Math.random(3)
|
||||||
|
if (weapon == 0) {
|
||||||
|
basic.showIcon(IconNames.SmallSquare)
|
||||||
|
} else if (weapon == 1) {
|
||||||
|
basic.showIcon(IconNames.Square)
|
||||||
|
} else {
|
||||||
|
basic.showIcon(IconNames.Scissors)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 1: Refactoring the rendering
|
||||||
|
|
||||||
|
**Refactoring** is a funny word used in coding which pretty much means ``reorganizing``. In this case,
|
||||||
|
we are going to move the code that displays the rock/paper/scissor icon into its own ``|forever|`` loop.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
let weapon = 0
|
||||||
|
input.onGesture(Gesture.Shake, () => {
|
||||||
|
weapon = Math.random(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
basic.forever(() => {
|
||||||
|
if (weapon == 0) {
|
||||||
|
basic.showIcon(IconNames.SmallSquare)
|
||||||
|
} else if (weapon == 1) {
|
||||||
|
basic.showIcon(IconNames.Square)
|
||||||
|
} else {
|
||||||
|
basic.showIcon(IconNames.Scissors)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: send status via radio
|
||||||
|
|
||||||
|
We send the value of ``weapon`` via radio to other @boardname@ in the ``|forever|``.
|
||||||
|
Since radio packet may or may not arrive, it's a good idea to keep sending them.
|
||||||
|
|
||||||
|
We also set the radio group and send the device serial number (a number that uniquely identifies a @boardname@) as we will need that later.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
let weapon = 0
|
||||||
|
input.onGesture(Gesture.Shake, () => {
|
||||||
|
weapon = Math.random(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
basic.forever(() => {
|
||||||
|
radio.sendNumber(weapon)
|
||||||
|
if (weapon == 0) {
|
||||||
|
basic.showIcon(IconNames.SmallSquare)
|
||||||
|
} else if (weapon == 1) {
|
||||||
|
basic.showIcon(IconNames.Square)
|
||||||
|
} else {
|
||||||
|
basic.showIcon(IconNames.Scissors)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
radio.setGroup(10)
|
||||||
|
radio.setTransmitSerialNumber(true)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 3: the team roster
|
||||||
|
|
||||||
|
So all players are constantly broadcasting which face they picked to other players.
|
||||||
|
Let's add the code that receives those status and counts them.
|
||||||
|
|
||||||
|
We are going to add an **Array** variable that contains all the players in the same team as the current player.
|
||||||
|
That array, named ``players``, is like your team roster: it contains the list of @boardname@ serial numbers
|
||||||
|
that have the same weapon as you.
|
||||||
|
|
||||||
|
```block
|
||||||
|
let players: number[] = [0]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Receiving a message (part 1)
|
||||||
|
|
||||||
|
In an the ``|on radio received|`` event, we receive the status from another @boardname@. Click on the **gearwheel*
|
||||||
|
to add the ``serial`` parameter as we will need it to identify who sent that packet.
|
||||||
|
|
||||||
|
We compute three values from the data received:
|
||||||
|
|
||||||
|
* ``match``, a boolean value indicating whether the weapon of the other @boardname@ matches our current weapon
|
||||||
|
* ``player_index``, the position in the array of the other board's serial number. It will be -1 if it is not in the array
|
||||||
|
* ``found``, a boolean value indicating whether the @boardname@ serial number is part of the ``players`` array
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
let match = false
|
||||||
|
let player_index = 0
|
||||||
|
let players: number[] = [0]
|
||||||
|
let weapon = 0
|
||||||
|
let found = false
|
||||||
|
radio.onDataPacketReceived(({ receivedNumber, serial }) => {
|
||||||
|
match = weapon == receivedNumber
|
||||||
|
player_index = players.indexOf(serial)
|
||||||
|
found = player_index >= 0
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 5: Receiving a message (part 2)
|
||||||
|
|
||||||
|
There are two cases that we need to handle when looking at ``match`` and ``found``:
|
||||||
|
|
||||||
|
* **if** we have a ``match`` **and** the player is ``not found`` in the list, **then** we **add** it to ``players``
|
||||||
|
* **if** we don't have a ``match`` **and** the player is ``found`` in the list, **then** we **remove** it from ``players``
|
||||||
|
|
||||||
|
We turn the two rules above into two ``if`` statement where the serial number is added or removed.
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
let match = false
|
||||||
|
let player_index = 0
|
||||||
|
let players: number[] = [0]
|
||||||
|
let weapon = 0
|
||||||
|
let found = false
|
||||||
|
let temp = 0
|
||||||
|
radio.onDataPacketReceived(({ receivedNumber, serial }) => {
|
||||||
|
match = weapon == receivedNumber
|
||||||
|
player_index = players.indexOf(serial)
|
||||||
|
found = player_index >= 0
|
||||||
|
if (match && !(found)) {
|
||||||
|
players.push(serial)
|
||||||
|
}
|
||||||
|
if (!(match) && found) {
|
||||||
|
temp = players.removeAt(player_index)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 6: reseting the team
|
||||||
|
|
||||||
|
What if another player would leave the game? He would stop broadcasting its status and it would stay in our
|
||||||
|
list of players. To avoid this problem, we reset the ``players`` array each time we shake:
|
||||||
|
|
||||||
|
```block
|
||||||
|
input.onGesture(Gesture.Shake, () => {
|
||||||
|
let players: number[] = [0]
|
||||||
|
let weapon = Math.random(3)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 7: showing team score
|
||||||
|
|
||||||
|
The team score is the number of players in that team... which boils down to the ``length`` of the ``players``
|
||||||
|
array. We add a ``|show number|`` block in the ``|forever|`` loop to display it.
|
||||||
|
|
||||||
|
```block
|
||||||
|
let players: number[] = [0]
|
||||||
|
let weapon = 0
|
||||||
|
basic.forever(() => {
|
||||||
|
basic.showNumber(players.length)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## The final code
|
||||||
|
|
||||||
|
Now it's time to glue all together the pieces of our program.
|
||||||
|
Go carefully through all the steps and assemble the various features. Eventually, it should look
|
||||||
|
like the following program. Download it and plays with your friend**ssss**!
|
||||||
|
|
||||||
|
```blocks
|
||||||
|
let temp = 0
|
||||||
|
let found = false
|
||||||
|
let player_index = 0
|
||||||
|
let weapon = 0
|
||||||
|
let match = false
|
||||||
|
let players: number[] = []
|
||||||
|
input.onGesture(Gesture.Shake, () => {
|
||||||
|
players = [0]
|
||||||
|
weapon = Math.random(3)
|
||||||
|
})
|
||||||
|
radio.onDataPacketReceived( ({ receivedNumber, serial }) => {
|
||||||
|
match = weapon == receivedNumber
|
||||||
|
player_index = players.indexOf(serial)
|
||||||
|
found = player_index >= 0
|
||||||
|
if (match && !(found)) {
|
||||||
|
players.push(serial)
|
||||||
|
}
|
||||||
|
if (!(match) && found) {
|
||||||
|
temp = players.removeAt(player_index)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
basic.forever(() => {
|
||||||
|
radio.sendNumber(weapon)
|
||||||
|
if (weapon == 0) {
|
||||||
|
basic.showIcon(IconNames.SmallSquare)
|
||||||
|
} else if (weapon == 1) {
|
||||||
|
basic.showIcon(IconNames.Square)
|
||||||
|
} else {
|
||||||
|
basic.showIcon(IconNames.Scissors)
|
||||||
|
}
|
||||||
|
basic.showNumber(players.length)
|
||||||
|
})
|
||||||
|
players = [0]
|
||||||
|
radio.setGroup(10)
|
||||||
|
radio.setTransmitSerialNumber(true)
|
||||||
|
```
|
BIN
docs/static/mb/projects/rpsteams.png
vendored
Normal file
BIN
docs/static/mb/projects/rpsteams.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
Loading…
Reference in New Issue
Block a user