2 Move function
- Write a
move
function that accepts thecurrent position
,next position
,caller address
, andgame_id
. Themove
function should look like this:
#[abi(embed_v0)]
impl PlayerActionsImpl of IActions<ContractState> {
fn spawn(
self: @ContractState, white_address: ContractAddress, black_address: ContractAddress
) -> u32 {
// Rest of code
}
fn move(
self: @ContractState,
curr_position: Vec2,
next_position: Vec2,
caller: ContractAddress, //player
game_id: u32
) {
let world = self.world_dispatcher.read();
let mut current_piece = get!(world, (game_id, curr_position), (Piece));
// check if next_position is out of board or not
assert(!PieceTrait::is_out_of_board(next_position), 'Should be inside board');
// check if this is the right move for this piece type
assert(
current_piece.is_right_piece_move(next_position), 'Illegal move for type of piece'
);
// Get piece data from to next_position in the board
let mut next_position_piece = get!(world, (game_id, next_position), (Piece));
let player = get!(world, (game_id, caller), (Player));
// check if there is already a piece in next_position
assert(
next_position_piece.piece_type == PieceType::None
|| player.is_not_my_piece(next_position_piece.color),
'Already same color piece exist'
);
next_position_piece.piece_type = current_piece.piece_type;
next_position_piece.color = player.color;
// make current_piece piece none
current_piece.piece_type = PieceType::None;
current_piece.color = Color::None;
set!(world, (next_position_piece));
set!(world, (current_piece));
// change turn
let mut game_turn = get!(world, game_id, (GameTurn));
game_turn.player_color = game_turn.next_turn();
set!(world, (game_turn));
}
}
-
Run
sozo build
to compile the code.Great, Now we can start testing our functions
Test Flow
- Spawn the test world (
spawn_test_world
) that imports the models in testing. - Deploy actions contract
- Interact with
spawn
function in theactions
contract by providing white and black player's wallet addresses as inputs. - Retrieve the game entity and piece entity created in
actions
contract. - Ensure the game has been correctly created.
- Verify that each
Piece
is located in the correct position.
Unit Tests
- Copy the test below and add it to your
tests/units.cairo
file.
#[cfg(test)]
mod tests {
use starknet::ContractAddress;
use dojo::test_utils::{spawn_test_world, deploy_contract};
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};
use chess::models::player::{Player, Color, player};
use chess::models::piece::{Piece, PieceType, Vec2, piece};
use chess::models::game::{Game, GameTurn, game, game_turn};
use chess::actions::{actions, IActionsDispatcher, IActionsDispatcherTrait};
// helper setup function
fn setup_world() -> (IWorldDispatcher, IActionsDispatcher) {
// models
let mut models = array![
game::TEST_CLASS_HASH,
player::TEST_CLASS_HASH,
game_turn::TEST_CLASS_HASH,
piece::TEST_CLASS_HASH
];
// deploy world with models
let world = spawn_test_world(models);
// deploy systems contract
let contract_address = world
.deploy_contract('salt', actions::TEST_CLASS_HASH.try_into().unwrap());
let actions_system = IActionsDispatcher { contract_address };
(world, actions_system)
}
#[test]
#[available_gas(3000000000000000)]
fn test_spawn() {
let white = starknet::contract_address_const::<0x01>();
let black = starknet::contract_address_const::<0x02>();
let (world, actions_system) = setup_world();
//system calls
let game_id = actions_system.spawn(white, black);
//get game
let game = get!(world, game_id, (Game));
let game_turn = get!(world, game_id, (GameTurn));
assert(game_turn.player_color == Color::White, 'should be white turn');
assert(game.white == white, 'white address is incorrect');
assert(game.black == black, 'black address is incorrect');
//get a1 piece
let curr_pos = Vec2 { x: 0, y: 0 };
let a1 = get!(world, (game_id, curr_pos), (Piece));
assert(a1.piece_type == PieceType::Rook, 'should be Rook');
assert(a1.color == Color::White, 'should be white color');
assert(a1.piece_type != PieceType::None, 'should have piece');
}
#[test]
#[available_gas(3000000000000000)]
fn test_move() {
let white = starknet::contract_address_const::<0x01>();
let black = starknet::contract_address_const::<0x02>();
let (world, actions_system) = setup_world();
let game_id = actions_system.spawn(white, black);
let curr_pos = Vec2 { x: 0, y: 1 };
let a2 = get!(world, (game_id, curr_pos), (Piece));
assert(a2.piece_type == PieceType::Pawn, 'should be Pawn');
assert(a2.color == Color::White, 'should be white color piece 1');
assert(a2.piece_type != PieceType::None, 'should have piece');
let next_pos = Vec2 { x: 0, y: 2 };
let game_turn = get!(world, game_id, (GameTurn));
assert(game_turn.player_color == Color::White, 'should be white player turn');
actions_system.move(curr_pos, next_pos, white.into(), game_id);
let curr_pos = next_pos;
let c3 = get!(world, (game_id, curr_pos), (Piece));
assert(c3.piece_type == PieceType::Pawn, 'should be Pawn');
assert(c3.color == Color::White, 'should be white color piece 2');
assert(c3.piece_type != PieceType::None, 'should have piece');
let game_turn = get!(world, game_id, (GameTurn));
assert(game_turn.player_color == Color::Black, 'should be black player turn');
}
}
Diving into the Code
setup_world
We should list all models with each having CLASS_HASH as elements and then we deploy world to models with spawn_test_world
//models
let mut models = array![
game::TEST_CLASS_HASH,
player::TEST_CLASS_HASH,
game_turn::TEST_CLASS_HASH,
piece::TEST_CLASS_HASH
];
// deploy world with models
let world = spawn_test_world(models);
After that, we deploy our system contracts, then we return our world
and actions_systems
dispatchers.
let contract_address = world
.deploy_contract('salt', actions::TEST_CLASS_HASH.try_into().unwrap());
let actions_system = IActionsDispatcher { contract_address };
(world, actions_system)
test_spawn
First, we'll set up the players address and their colors.
let white = starknet::contract_address_const::<0x01>();
let black = starknet::contract_address_const::<0x02>();
We use spawn
function in actions.cairo
to put our pieces on the board. Each square position holds a piece. The system's spawn
function needs some input i.e the addresses of the players.
// spawn
let game_id = actions_system.spawn(white, black);
Then we check if the players got their setup address. After that we check if a White rook is at (0,0). Remember, to get a piece that exists on the position, you need to use the keys of the Piece
model, which are game_id
, and curr_pos
.
//get a1 square
let curr_pos = Vec2 { x: 0, y: 0 };
let a1 = get!(world, (game_id, curr_pos), (Piece));
assert(a1.piece_type == PieceType::Rook, 'should be Rook');
assert(a1.color == Color::White, 'should be white color');
assert(a1.piece_type != PieceType::None, 'should have piece');
test_move
Here, after setting up the board, we use move
function in the contract to make moves. Provide the current position, the next position, the player's address, and the game id.
//Move White Pawn to (0,2)
actions_system.move(curr_pos, next_pos, white.into(), game_id);
Then we check if a White Pawn is at the new position.
let curr_pos = next_pos;
let c3 = get!(world, (game_id, curr_pos), (Piece));
assert(c3.piece_type == PieceType::Pawn, 'should be Pawn');
assert(c3.color == Color::White, 'should be white color piece 2');
assert(c3.piece_type != PieceType::None, 'should have piece');
Need help?
If you're stuck, don't hesitate to ask questions at the Dojo community!