4.测试合约

在本章中,我们将利用所学知识运行一个完整的国际象棋游戏场景。

下面是我们要做的测试:

  1. white_pawn_1 出生在 (0,1)
  2. 移动 white_pawn_1 到 (0,3)
  3. 移动 black_pawn_2 到 (1,6)
  4. 移动 white_pawn_1 到 (0,4)
  5. 移动 black_pawn_2 到 (1,5)
  6. 移动 white_pawn_1 到 (1,5)
  7. 吃掉 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_idxy。对黑方进行同样的检查。

        //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 社区 中的其他人分享您的项目!