Skip to content

Commit

Permalink
Merge pull request #161 from dojoengine/chess-2
Browse files Browse the repository at this point in the history
Update chess content
  • Loading branch information
ponderingdemocritus authored Dec 21, 2023
2 parents 2ff5506 + 0804e04 commit a2be2bd
Show file tree
Hide file tree
Showing 8 changed files with 636 additions and 518 deletions.
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
285 changes: 207 additions & 78 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,77 +202,128 @@ 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']),
}
}
}
```

Expand Down
Loading

0 comments on commit a2be2bd

Please sign in to comment.