0.设置

在开始之前,建议阅读 hello-dojo一章,以获得对 Dojo 游戏的基本了解。

初始化项目

创建一个新的 Dojo 项目文件夹。您可以随心所欲地为项目命名。

mkdir dojo-chess

打开项目文件夹。

cd dojo-chess

然后使用 sozo init 初始化项目。

sozo init

清理模板

该项目带有大量模板代码。请全部清除。确保 components.cairosystems.cairo 文件为空。

lib.cairo中,仅保留:

mod components;
mod systems;

用以下命令编译项目:

sozo build

基本组件

虽然有很多种方法使用 ECS 模型设计象棋游戏,但我们将采用这种方法:

棋盘上的每个方格(例如 A1)都将被视为一个实体。如果某个方格上有棋子,则该方格实体将包含该棋子。

首先,将这个基本组件添加到components.cairo文件中。如果你不熟悉 Dojo 引擎中的组件语法,请回顾这个 章节.。

#[derive(Component)]
struct Square {
    #[key]
    game_id: felt252,
    #[key]
    x: u32,
    #[key]
    y: u32,
    piece: Option<PieceType>,
}

enum PieceType {
    WhitePawn,
    WhiteKnight,
    WhiteBishop,
    WhiteRook,
    WhiteQueen,
    WhiteKing,
    BlackPawn,
    BlackKnight,
    BlackBishop,
    BlackRook,
    BlackQueen,
    BlackKing,
}

基本系统

从下一章节开始,您将在每一章节中依次实现 initiatemove 系统。为了更好地实现模块化,我们会在单独的文件中创建每个系统。

src 创建一个 systems 文件夹。在文件夹内创建 initiate.cairomove.cairo 两个文件。每个文件都应包含一个基本的系统结构。

例如,initiate.cairo 看起来像这样:

#[system]
mod initiate_system {

}

systems.cairo 中,我们将这样使用 initiate_system

mod initiate;

use initiate::initiate_system;

对其他系统执行同样操作。更新 systems.cairo 为:

mod initiate;
mod move;

use initiate::initiate_system;
use move::move_system;

编译项目

现在尝试 sozo build 进行构建。是不是遇到了错误?

error: Trait has no implementation in context:

您可能会遇到一些trait实现上的错误,您可以像这样通过派生来实现:


#[derive(Component, Drop, SerdeLen, Serde)]
struct Square {
    #[key]
    game_id: felt252,
    #[key]
    x: u32,
    #[key]
    y: u32,
    piece: Option<PieceType>,
}

#[derive(Serde, Drop, Copy, PartialEq)]
enum PieceType {
    WhitePawn,
    WhiteKnight,
    WhiteBishop,
    WhiteRook,
    WhiteQueen,
    WhiteKing,
    BlackPawn,
    BlackKnight,
    BlackBishop,
    BlackRook,
    BlackQueen,
    BlackKing,
}

很好!那就让我们来解决这个错误吧。

error: Trait has no implementation in context: dojo::serde::SerdeLen::<core::option::Option::<dojo_chess::components::PieceType>>
 --> Square:80:54
                dojo::SerdeLen::<Option<PieceType>>::len()
                                                     ^*^

必须明确的一点是,<Option<PieceType>> 是我们创建的类型。因此,该类型没有实现 SerdeLen 等基本traits。你需要自己定义实现。

impl PieceOptionSerdeLen of dojo::SerdeLen<Option<PieceType>> {
    #[inline(always)]
    fn len() -> usize {
        2
    }
}

修复上述其他问题,以便成功运行 sozo build 命令。

运行测试

在进入下一章之前,请记住sozo buildsozo test是确保代码正确的重要步骤。

运行 sozo 测试。出现错误了吗?

error: Trait has no implementation in context:
error: Variable not dropped. Trait has no implementation in context:

对于no implementation错误,请实现 PrintTrait 以成功运行 sozo test。对于not dropped错误,添加 Drop trait。其他错误可通过添加派生或实现来逐一解决。

添加更多组件

在继续之前,请添加更多组件,以便在下一章创建系统时使用。

要求

  • 具有白色和黑色值的 Color 枚举
  • Game组件:
    game_id: felt252,
    winner: Option<Color>,
    white: ContractAddress,
    black: ContractAddress
  • GameTurn组件:
    game_id: felt252,
    turn: Color
  • 稍后我们将设置由 GameGameTurn 组件组成的游戏实体。
  • 运行 sozo buildsozo test 并确保所有测试通过。

在进行下一步之前,请试着自己解题并对照下面的答案。

点击查看完整的 `components.cairo` 代码
use debug::PrintTrait;
use starknet::ContractAddress;

#[derive(Component, Drop, SerdeLen, Serde)]
struct Square {
    #[key]
    game_id: felt252,
    #[key]
    x: u32,
    #[key]
    y: u32,
    piece: Option<PieceType>,
}

#[derive(Serde, Drop, Copy, PartialEq)]
enum PieceType {
    WhitePawn,
    WhiteKnight,
    WhiteBishop,
    WhiteRook,
    WhiteQueen,
    WhiteKing,
    BlackPawn,
    BlackKnight,
    BlackBishop,
    BlackRook,
    BlackQueen,
    BlackKing,
}

#[derive(Serde, Drop, Copy, PartialEq)]
enum Color {
    White,
    Black,
}


impl PieceOptionSerdeLen of dojo::SerdeLen<Option<PieceType>> {
    #[inline(always)]
    fn len() -> usize {
        2
    }
}

impl ColorPrintTrait of PrintTrait<Color> {
    #[inline(always)]
    fn print(self: Color) {
        match self {
            Color::White(_) => {
                'White'.print();
            },
            Color::Black(_) => {
                'Black'.print();
            },
        }
    }
}

impl ColorOptionPrintTrait of PrintTrait<Option<Color>> {
    #[inline(always)]
    fn print(self: Option<Color>) {
        match self {
            Option::Some(color) => {
                color.print();
            },
            Option::None(_) => {
                'None'.print();
            }
        }
    }
}


impl BoardPrintTrait of PrintTrait<(u32, u32)> {
    #[inline(always)]
    fn print(self: (u32, u32)) {
        let (x, y): (u32, u32) = self;
        x.print();
        y.print();
    }
}


impl PieceTypeOptionPrintTrait of PrintTrait<Option<PieceType>> {
    #[inline(always)]
    fn print(self: Option<PieceType>) {
        match self {
            Option::Some(piece_type) => {
                piece_type.print();
            },
            Option::None(_) => {
                'None'.print();
            }
        }
    }
}


impl PieceTypePrintTrait of PrintTrait<PieceType> {
    #[inline(always)]
    fn print(self: PieceType) {
        match self {
            PieceType::WhitePawn(_) => {
                'WhitePawn'.print();
            },
            PieceType::WhiteKnight(_) => {
                'WhiteKnight'.print();
            },
            PieceType::WhiteBishop(_) => {
                'WhiteBishop'.print();
            },
            PieceType::WhiteRook(_) => {
                'WhiteRook'.print();
            },
            PieceType::WhiteQueen(_) => {
                'WhiteQueen'.print();
            },
            PieceType::WhiteKing(_) => {
                'WhiteKing'.print();
            },
            PieceType::BlackPawn(_) => {
                'BlackPawn'.print();
            },
            PieceType::BlackKnight(_) => {
                'BlackKnight'.print();
            },
            PieceType::BlackBishop(_) => {
                'BlackBishop'.print();
            },
            PieceType::BlackRook(_) => {
                'BlackRook'.print();
            },
            PieceType::BlackQueen(_) => {
                'BlackQueen'.print();
            },
            PieceType::BlackKing(_) => {
                'BlackKing'.print();
            },
        }
    }
}

impl ColorSerdeLen of dojo::SerdeLen<Color> {
    #[inline(always)]
    fn len() -> usize {
        1
    }
}

#[derive(Component, Drop, SerdeLen, Serde)]
struct Game {
    /// game id, computed as follows pedersen_hash(player1_address, player2_address)
    #[key]
    game_id: felt252,
    winner: Option<Color>,
    white: ContractAddress,
    black: ContractAddress
}


#[derive(Component, Drop, SerdeLen, Serde)]
struct GameTurn {
    #[key]
    game_id: felt252,
    turn: Color,
}

impl OptionPieceColorSerdeLen of dojo::SerdeLen<Option<Color>> {
    #[inline(always)]
    fn len() -> usize {
        1
    }
}

恭喜您!您已经完成了构建链上国际象棋游戏的基本设置 🎉