Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update chess content #161

Merged
merged 3 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,8 @@
- [Onchain Chess](./tutorial/onchain-chess/README.md)
- [0. Setup](./tutorial/onchain-chess/0-setup.md)
- [1. Initiate](./tutorial/onchain-chess/1-action.md)
- [2. Check Legal Move](./tutorial/onchain-chess/2-legal.md)
- [3. Move](./tutorial/onchain-chess/3-move.md)
- [4. Test Chess](./tutorial/onchain-chess/4-test.md)
- [2. Move](./tutorial/onchain-chess/2-move.md)
- [3. Test Chess](./tutorial/onchain-chess/3-test.md)
- [Deploy using Slot](./tutorial/deploy-using-slot/main.md)

---
Expand Down
287 changes: 208 additions & 79 deletions src/tutorial/onchain-chess/0-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ _Before starting recommend following the [`hello-dojo`](../../cairo/hello-dojo.m
Create a new Dojo project folder. You can name your project what you want.

```sh
mkdir dojo-chess
mkdir chess
```

Open the project folder.

```sh
cd dojo-chess
cd chess
```

And initialize the project using sozo init.
Expand All @@ -24,30 +24,61 @@ sozo init

## Cleaning Up the Boilerplate

The project comes with a lot of boilerplate codes. Clear it all. Make sure both `actions.cairo`, `models.cairo` and `utils.cairo` files are empty. Then create a new empty `tests.cairo` file in your `/src` directory.
The project comes with a lot of boilerplate codes. Clear it all. Make sure your directory looks like this

```shell
├── README.md
├── Scarb.toml
└── src
├── actions.cairo
├── lib.cairo
├── models
│ ├── game.cairo
│ ├── piece.cairo
│ └── player.cairo
├── models.cairo
├── tests
│ ├── integration.cairo
│ └── units.cairo
└── tests.cairo
```

Remodel your`lib.cairo`, to look like this :
Remodel your `lib.cairo`, to look like this :

```rust,ignore
mod actions;
mod models;
mod utils;
mod tests;
```

Remodel your `models.cairo`, to look like this :

```rust,ignore
mod game;
mod piece;
mod player;
```

Remodel your `tests.cairo`, to look like this :

```rust,ignore
mod integration;
mod units;
```

Make sure your `Scarb.toml` looks like this:

```toml
[package]
cairo-version = "2.3.0"
name = "dojo_chess"
version = "0.3.15"
cairo-version = "2.4.0"
name = "chess"
version = "0.4.0"

[cairo]
sierra-replace-ids = true

[dependencies]
dojo = { git = "https://github.com/dojoengine/dojo", version = "0.3.15" }
dojo = { git = "https://github.com/dojoengine/dojo", version = "0.4.2" }

[[target.dojo]]

Expand All @@ -72,37 +103,84 @@ sozo build

While there are many ways to design a chess game using the ECS model, we'll follow this approach:

> Every square of the chess board (e.g., A1) will be treated as an entity. If a piece exists on a square, the square entity will hold that piece.
> Every square of the chess board (e.g., A1) will be treated as an entity. If a piece exists on a square position, that position will hold that piece.

First, add this basic model to `models.cairo` file. If you are not familar with model syntax in Dojo engine, go back to this [chapter](../../cairo/models.md).
First, add this basic `player` model to `models/player.cairo` file. If you are not familar with model syntax in Dojo engine, go back to this [chapter](../../cairo/models.md).

```rust,ignore
use starknet::ContractAddress;

#[derive(Model, Drop, Serde)]
struct Square {
struct Player {
#[key]
game_id: u32,
#[key]
x: u32,
address: ContractAddress,
color: Color
}

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

Second, we do the same for `game` model. Edit your `models/player.cairo` file and add this content.

```rust,ignore
use chess::models::player::Color;
use starknet::ContractAddress;

#[derive(Model, Drop, Serde)]
struct Game {
#[key]
game_id: u32,
winner: Color,
white: ContractAddress,
black: ContractAddress
}

#[derive(Model, Drop, Serde)]
struct GameTurn {
#[key]
game_id: u32,
player_color: Color
}
```

Lastly we create `piece` model in our `models/player.cairo` file.

```rust,ignore
use chess::models::player::Color;
use starknet::ContractAddress;

#[derive(Model, Drop, Serde)]
struct Piece {
#[key]
game_id: u32,
#[key]
y: u32,
piece: PieceType,
position: Vec2,
color: Color,
piece_type: PieceType,
}

#[derive(Copy, Drop, Serde, Introspect)]
struct Vec2 {
x: u32,
y: u32
}

#[derive(Serde, Drop, Copy, PartialEq, Introspect)]
enum PieceType {
WhitePawn: (),
WhiteKnight: (),
WhiteBishop: (),
WhiteRook: (),
WhiteQueen: (),
WhiteKing: (),
BlackPawn: (),
BlackKnight: (),
BlackBishop: (),
BlackRook: (),
BlackQueen: (),
BlackKing: (),
None: (),
Pawn,
Knight,
Bishop,
Rook,
Queen,
King,
None,
}
```

Expand All @@ -124,82 +202,133 @@ It should be noted that Systems function are contract methods, by implication, r

Now try `sozo build` to build.

Complied? Great! then let's move on. If not fix try issues, so that you can run the `sozo build` command successfully.
Complied? Great! then let's move on. If not fix the issues, so that you can run the `sozo build` command successfully.

## Add more models
## Implement Traits for models

Before you move on, add more models so we can use them in the next chapter when creating the action contract.
Before you move on, implement traits for models so we can use them in the next chapter when creating the action contract.

### Requirements

- `Color` enum with values: White,Black & None
Firt we have to define the following traits for `Game`, `Player`, `Piece` models respectively.

```rust,ignore
trait GameTurnTrait {
fn next_turn(self: @GameTurn) -> Color;
}

- `Game` model with fields: game_id, winner, white, black.
trait PlayerTrait {
fn is_not_my_piece(self: @Player, piece_color: Color) -> bool;
}

- `GameTurn` model with fields: game_id, turn.
trait PieceTrait {
fn is_out_of_board(next_position: Vec2) -> bool;
fn is_right_piece_move(self: @Piece, next_position: Vec2) -> bool;
}
```

Try to implement this code by yourself, Otherwise

<details>
<summary>Click to see full `models.cairo` code</summary>

```rust,ignore
use starknet::ContractAddress;

#[derive(Model, Drop, Serde)]
struct Square {
#[key]
game_id: u32,
#[key]
x: u32,
#[key]
y: u32,
piece: PieceType,
```c
// code for player.cairo file
trait PlayerTrait {
fn is_not_my_piece(self: @Player, piece_color: Color) -> bool;
}

#[derive(Serde, Drop, Copy, PartialEq, Introspect)]
enum PieceType {
WhitePawn: (),
WhiteKnight: (),
WhiteBishop: (),
WhiteRook: (),
WhiteQueen: (),
WhiteKing: (),
BlackPawn: (),
BlackKnight: (),
BlackBishop: (),
BlackRook: (),
BlackQueen: (),
BlackKing: (),
None: (),
impl PalyerImpl of PlayerTrait {
fn is_not_my_piece(self: @Player, piece_color: Color) -> bool {
*self.color != piece_color
}
}

#[derive(Serde, Drop, Copy, PartialEq, Introspect)]
enum Color {
White: (),
Black: (),
None: (),
// code for game.cairo file
trait GameTurnTrait {
fn next_turn(self: @GameTurn) -> Color;
}
impl GameTurnImpl of GameTurnTrait {
fn next_turn(self: @GameTurn) -> Color {
match self.player_color {
Color::White => Color::Black,
Color::Black => Color::White,
Color::None => panic(array!['Illegal turn'])
}
}
}

#[derive(Model, Drop, Serde)]
struct Game {
#[key]
game_id: u32,
winner: Color,
white: ContractAddress,
black: ContractAddress
// code for piece.cairo file
trait PieceTrait {
fn is_out_of_board(next_position: Vec2) -> bool;
fn is_right_piece_move(self: @Piece, next_position: Vec2) -> bool;
}

#[derive(Model, Drop, Serde)]
struct GameTurn {
#[key]
game_id: u32,
turn: Color
impl PieceImpl of PieceTrait {
fn is_out_of_board(next_position: Vec2) -> bool {
next_position.x > 7 || next_position.y > 7
}

fn is_right_piece_move(self: @Piece, next_position: Vec2) -> bool {
let n_x = next_position.x;
let n_y = next_position.y;
assert(!(n_x == *self.position.x && n_y == *self.position.y), 'Cannot move same position');
match self.piece_type {
PieceType::Pawn => {
match self.color {
Color::White => {
(n_x == *self.position.x && n_y == *self.position.y + 1)
|| (n_x == *self.position.x && n_y == *self.position.y + 2)
|| (n_x == *self.position.x + 1 && n_y == *self.position.y + 1)
|| (n_x == *self.position.x - 1 && n_y == *self.position.y + 1)
},
Color::Black => {
(n_x == *self.position.x && n_y == *self.position.y - 1)
|| (n_x == *self.position.x && n_y == *self.position.y - 2)
|| (n_x == *self.position.x + 1 && n_y == *self.position.y - 1)
|| (n_x == *self.position.x - 1 && n_y == *self.position.y - 1)
},
Color::None => panic(array!['Should not move empty piece']),
}
},
PieceType::Knight => { n_x == *self.position.x + 2 && n_y == *self.position.y + 1 },
PieceType::Bishop => {
(n_x <= *self.position.x && n_y <= *self.position.y && *self.position.y
- n_y == *self.position.x
- n_x)
|| (n_x <= *self.position.x && n_y >= *self.position.y && *self.position.y
- n_y == *self.position.x
- n_x)
|| (n_x >= *self.position.x && n_y <= *self.position.y && *self.position.y
- n_y == *self.position.x
- n_x)
|| (n_x >= *self.position.x && n_y >= *self.position.y && *self.position.y
- n_y == *self.position.x
- n_x)
},
PieceType::Rook => {
(n_x == *self.position.x || n_y != *self.position.y)
|| (n_x != *self.position.x || n_y == *self.position.y)
},
PieceType::Queen => {
(n_x == *self.position.x || n_y != *self.position.y)
|| (n_x != *self.position.x || n_y == *self.position.y)
|| (n_x != *self.position.x || n_y != *self.position.y)
},
PieceType::King => {
(n_x <= *self.position.x + 1 && n_y <= *self.position.y + 1)
|| (n_x <= *self.position.x + 1 && n_y <= *self.position.y - 1)
|| (n_x <= *self.position.x - 1 && n_y <= *self.position.y + 1)
|| (n_x <= *self.position.x - 1 && n_y <= *self.position.y - 1)
},
PieceType::None => panic(array!['Should not move empty piece']),
}
}
}
```

</details>

This tutorial is extracted from [here](https://github.com/dojoengine/origami/tree/main/dojo-chess)
This tutorial is extracted from [here](https://github.com/dojoengine/origami/tree/main/examples/chess)

Congratulations! You've completed the basic setup for building an on-chain chess game 🎉
Loading