Assignment
Task Description​
In this homework you are to write the Pebbles Game. The games rules are the following:
- There are two players: User and Program. The first player is chosen randomly.
- The game starts with pebbles (e.g., ).
- On the player's turn they must remove from to pebbles (e.g., if , then the player removes or pebbles per turn).
- The player who takes last pebble(s) is the winner.
Project Structure​
It is necessary to make two crates: pebbles-game
for the program and pebbles-game-io
for data structures.
The directory structure should be the following:
pebbles-game
├── io
│ ├── src
│ │ └── lib.rs
│ └── Cargo.toml
├── src
│ └── lib.rs
├── tests
│ └── basic.rs
├── Cargo.lock
├── Cargo.toml
└── build.rs
Types Definition​
The pebbles-game-io
will contains type definitions for input, output, and internal state data.
Its Cargo.toml
manifest will be the following:
[package]
name = "pebbles-game-io"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
gstd = { git = "https://github.com/gear-tech/gear.git", tag = "v1.4.1" }
gmeta = { git = "https://github.com/gear-tech/gear.git", tag = "v1.4.1" }
parity-scale-codec = { version = "3", default-features = false }
scale-info = { version = "2", default-features = false }
Let's explore types used.
-
When initialising the game, it is necessary to pass some initial information. For example, the number of pebbles (), maximum pebbles to be removed per turn (), difficulty level.
io/src/lib.rs#[derive(Debug, Default, Clone, Encode, Decode, TypeInfo)]
pub struct PebblesInit {
pub difficulty: DifficultyLevel,
pub pebbles_count: u32,
pub max_pebbles_per_turn: u32,
}
#[derive(Debug, Default, Clone, Encode, Decode, TypeInfo)]
pub enum DifficultyLevel {
#[default]
Easy,
Hard,
} -
It needs to send actions message for every User's move and receive some event from the program. The action can be a turn with some count of pebbles to be removed or the give up. Also, there is a restart action than resets the game state .
io/src/lib.rs#[derive(Debug, Clone, Encode, Decode, TypeInfo)]
pub enum PebblesAction {
Turn(u32),
GiveUp,
Restart {
difficulty: DifficultyLevel,
pebbles_count: u32,
max_pebbles_per_turn: u32,
},
}And the event reflects the game state after the User's move: either pebbles count removed by the Program or the end of game with the information about the winner.
io/src/lib.rs#[derive(Debug, Clone, Encode, Decode, TypeInfo)]
pub enum PebblesEvent {
CounterTurn(u32),
Won(Player),
}
#[derive(Debug, Default, Clone, Encode, Decode, TypeInfo)]
pub enum Player {
#[default]
User,
Program,
} -
Internal game state should keep all information related to the current state of the game. Some information is set during initialization, the first player is chosen randomly, some data are change during the game.
io/src/lib.rs#[derive(Debug, Default, Clone, Encode, Decode, TypeInfo)]
pub struct GameState {
pub pebbles_count: u32,
pub max_pebbles_per_turn: u32,
pub pebbles_remaining: u32,
pub difficulty: DifficultyLevel,
pub first_player: Player,
pub winner: Option<Player>,
}
Finally, the metadata to be used by the IDEA portal.
impl Metadata for PebblesMetadata {
type Init = In<PebblesInit>;
type Handle = InOut<PebblesAction, PebblesEvent>;
type State = Out<GameState>;
type Reply = ();
type Others = ();
type Signal = ();
}
The Homework Assignment​
-
Write
init()
function that:- Receives
PebblesInit
using themsg::load
function; - Checks input data for validness;
- Chooses the first player using the
exec::random
function; - Processes the first turn if the first player is Program.
- Fills the
GameState
structure.
- Receives
-
Write the
handle()
function that:- Receives
PebblesAction
usingmsg::load
function; - Checks input data for validness;
- Processes the User's turn and check whether they win;
- Processes the Program turn and check whether it wins;
- Send a message to the user with the correspondent
PebblesEvent
;
- Receives
-
Write the
state()
function that returns theGameState
structure using themsg::reply
function.
Additional Information​
There are two difficulty levels in the game: DifficultyLevel::Easy
and DifficultyLevel::Hard
. Program should choose the pebbles count to be removed randomly at the easy level, and find the best pebbles count (find a winning strategy) at the hard level.
Use the following helper function to get a random 32-bit number:
fn get_random_u32() -> u32 {
let salt = msg::id();
let (hash, _num) = exec::random(salt.into()).expect("get_random_u32(): random call failed");
u32::from_le_bytes([hash[0], hash[1], hash[2], hash[3]])
}
Testing​
You are to cover program initialization and all actions by tests using the gtest
crate.
- Check whether the game initialized correctly.
- Check all program strategies (you may split the
get_random_u32()
function into two separated implementations for#[cfg(test)]
and#[cfg(not(test))]
environments). - Check negative scenarios and invalid input data processing.
Afterword
- The homework should be done as the PR in the GitHub repository.
- You are to upload the Wasm binary to the Vara Network Testnet and send its address.
- If you encounter challenges in the development of a project, it is advisable to refer to the Gear Wiki for guidance. It provides comprehensive instructions for developing programs on Gear.