4.测试合约
在本章中,我们将利用所学知识运行一个完整的国际象棋游戏场景。
下面是我们要做的测试:
- 让
white_pawn_1
出生在 (0,1) - 移动
white_pawn_1
到 (0,3) - 移动
black_pawn_2
到 (1,6) - 移动
white_pawn_1
到 (0,4) - 移动
black_pawn_2
到 (1,5) - 移动
white_pawn_1
到 (1,5) - 吃掉
black_pawn_2
要放置棋子,请使用我们的initiate_system
。要移动棋子,请使用move_system
。使用 move_system
时,请记住检查棋子是否能被吃掉。
在我们开始编写代码之前,请这样设置集成测试:
- 复制下面的测试并将其添加到你的
src/tests.cairo
文件中。 - 在你的 src 中创建一个
test.cairo
,更新lib.cairo
添加mod tests;
。
完整代码
#[cfg(test)]
mod tests {
use starknet::ContractAddress;
use dojo::test_utils::spawn_test_world;
use dojo_chess::components::{Game, game, GameTurn, game_turn, Square, square, PieceType};
use dojo_chess::systems::initiate_system;
use dojo_chess::systems::move_system;
use array::ArrayTrait;
use core::traits::Into;
use dojo::world::IWorldDispatcherTrait;
use core::array::SpanTrait;
#[test]
#[available_gas(3000000000000000)]
fn integration() {
let white = starknet::contract_address_const::<0x01>();
let black = starknet::contract_address_const::<0x02>();
// components
let mut components = array::ArrayTrait::new();
components.append(game::TEST_CLASS_HASH);
components.append(game_turn::TEST_CLASS_HASH);
components.append(square::TEST_CLASS_HASH);
//systems
let mut systems = array::ArrayTrait::new();
systems.append(initiate_system::TEST_CLASS_HASH);
systems.append(move_system::TEST_CLASS_HASH);
let world = spawn_test_world(components, systems);
// initiate
let mut calldata = array::ArrayTrait::<core::felt252>::new();
calldata.append(white.into());
calldata.append(black.into());
world.execute('initiate_system'.into(), calldata);
let game_id = pedersen(white.into(), black.into());
//White pawn is now in (0,1)
let a2 = get!(world, (game_id, 0, 1), (Square));
match a2.piece {
Option::Some(piece) => {
assert(piece == PieceType::WhitePawn, "should be White Pawn in (0,1)");
},
Option::None(_) => assert(false, 'should have piece in (0,1)),
};
//Black pawn is now in (1,6)
let b7 = get!(world, (game_id, 1, 6), (Square));
match b7.piece {
Option::Some(piece) => {
assert(piece == PieceType::BlackPawn, "should be Black Pawn in (1,6)");
},
Option::None(_) => assert(false, 'should have piece in (1,6)),
};
//Move White Pawn to (0,3)
let mut move_calldata = array::ArrayTrait::<core::felt252>::new();
move_calldata.append(0);
move_calldata.append(1);
move_calldata.append(0);
move_calldata.append(3);
move_calldata.append(white.into());
move_calldata.append(game_id);
world.execute('move_system'.into(), move_calldata);
//White pawn is now in (0,3)
let a4 = get!(world, (game_id, 0, 3), (Square));
match a4.piece {
Option::Some(piece) => {
assert(piece == PieceType::WhitePawn, "should be White Pawn in (0,3)");
},
Option::None(_) => assert(false, 'should have piece in (0,3)),
};
//Move black Pawn to (1,4)
let mut move_calldata = array::ArrayTrait::<core::felt252>::new();
move_calldata.append(1);
move_calldata.append(6);
move_calldata.append(1);
move_calldata.append(4);
move_calldata.append(black.into());
move_calldata.append(game_id);
world.execute('move_system'.into(), move_calldata);
//Black pawn is now in (1,4)
let b5 = get!(world, (game_id, 1, 4), (Square));
match b5.piece {
Option::Some(piece) => {
assert(piece == PieceType::BlackPawn, "should be Black Pawn in (1,4)");
},
Option::None(_) => assert(false, ‘should have piece in (1,4)),
};
// Move White Pawn to (1,4)
// Capture black pawn
let mut move_calldata = array::ArrayTrait::<core::felt252>::new();
move_calldata.append(0);
move_calldata.append(3);
move_calldata.append(1);
move_calldata.append(4);
move_calldata.append(white.into());
move_calldata.append(game_id);
world.execute(‘move_system’.into(), move_calldata);
let b5 = get!(world, (game_id, 1, 4), (Square));
match b5.piece {
Option::Some(piece) => {
assert(piece == PieceType::WhitePawn, “should be WhitePawn in (1,4)”);
},
Option::None(_) => assert(false, ‘should have piece in (1,4)),
};
}
}
深入代码
首先,我们将设置球员及其阵营。
let white = starknet::contract_address_const::<0x01>();
let black = starknet::contract_address_const::<0x02>();
我们应以数组形式列出 Components and Systems
,每个数组都以 CLASS_HASH 作为元素。
// components
let mut components = array::ArrayTrait::new();
components.append(game::TEST_CLASS_HASH);
components.append(game_turn::TEST_CLASS_HASH);
components.append(square::TEST_CLASS_HASH);
//systems
let mut systems = array::ArrayTrait::new();
systems.append(initiate_system::TEST_CLASS_HASH);
systems.append(move_system::TEST_CLASS_HASH);
接下来,我们将创建游戏世界。
let world = spawn_test_world(components, systems);
我们使用 initiate_system
将方块棋子放到棋盘上。每个方格放置一个棋子。系统的执行函数需要一些输入,我们将其作为 calldata 提供给它。
// initiate
let mut calldata = array::ArrayTrait::<core::felt252>::new();
calldata.append(white.into());
calldata.append(black.into());
world.execute(‘initiate_system’.into(), calldata);
让我们检查白方棋子是否位于 (0,1)。请记住,要获取存在于该方格上的棋子,需要使用 Square
组件的键,即 game_id
、x
和 y
。对黑方进行同样的检查。
//White pawn is now in (0,1)
let a2 = get!(world, (game_id, 0, 1), (Square));
match a2.piece {
Option::Some(piece) => {
assert(piece == PieceType::WhitePawn, “should be White Pawn in (0,1)”);
},
Option::None(_) => assert(false, ‘should have piece in (0,1)),
};
设置好棋盘后,使用 move_system
下棋。提供当前位置、下一个位置、玩家地址和游戏 ID。
//Move White Pawn to (0,3)
let mut move_calldata = array::ArrayTrait::<core::felt252>::new();
move_calldata.append(0);
move_calldata.append(1);
move_calldata.append(0);
move_calldata.append(3);
move_calldata.append(white.into());
move_calldata.append(game_id);
world.execute(‘move_system’.into(), move_calldata);
不断移动棋子,检查它们的位置是否正确。
恭喜!
您已经使用 Dojo 引擎制作了国际象棋游戏的基本合约!本教程只是一个开始。有很多方法可以让游戏变得更好,例如优化部分、添加检查或考虑特殊情况。如果您想在这个国际象棋游戏中做得更多,请尝试这些挑战:
- 创建一个使用 lazy init 的
initiate_system
。如果你对 lazy init 不确定,阅读一下。这有助于提高游戏操作的效率。 - 添加将死功能。我们的游戏现在还没有结束,因此请决定何时应该结束!
- 加入特殊的棋步,如投子、吃子或提卒。
- 制定自己的国际象棋规则!您甚至可以创建自己版本的 不朽游戏
最后,与 Dojo 社区 中的其他人分享您的项目!