0.设置
在开始之前,建议阅读 hello-dojo
一章,以获得对 Dojo 游戏的基本了解。
初始化项目
创建一个新的 Dojo 项目文件夹。您可以随心所欲地为项目命名。
mkdir dojo-chess
打开项目文件夹。
cd dojo-chess
然后使用 sozo init 初始化项目。
sozo init
清理模板
该项目带有大量模板代码。请全部清除。确保 components.cairo
和 systems.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,
}
基本系统
从下一章节开始,您将在每一章节中依次实现 initiate
和 move
系统。为了更好地实现模块化,我们会在单独的文件中创建每个系统。
在 src
创建一个 systems
文件夹。在文件夹内创建 initiate.cairo
和 move.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 build
与 sozo 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
- 稍后我们将设置由
Game
和GameTurn
组件组成的游戏实体。 - 运行
sozo build
和sozo 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
}
}
恭喜您!您已经完成了构建链上国际象棋游戏的基本设置 🎉