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

Implement join game function #84

Merged
merged 14 commits into from
Oct 17, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@
"name": "game_mode",
"type": "starkludo::models::game::GameMode"
},
{
"name": "game_status",
"type": "starkludo::models::game::GameStatus"
},
{
"name": "player_green",
"type": "core::felt252"
Expand Down Expand Up @@ -400,6 +404,30 @@
],
"outputs": [],
"state_mutability": "external"
},
{
"type": "function",
"name": "join_game",
"inputs": [
{
"name": "game_id",
"type": "core::integer::u64"
},
{
"name": "player_color",
"type": "core::felt252"
},
{
"name": "username",
"type": "core::felt252"
}
],
"outputs": [
{
"type": "starkludo::models::game::Game"
}
],
"state_mutability": "external"
}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
kind = "DojoContract"
class_hash = "0x4cecefc87acb36b188cec659cfde8d7cbfafaf8684297f7d66cadd573cbfec5"
original_class_hash = "0x4cecefc87acb36b188cec659cfde8d7cbfafaf8684297f7d66cadd573cbfec5"
class_hash = "0x4822d5336e093bd5060f977312516677c87cda7cc86ebac5941cf148054a2bf"
original_class_hash = "0x4822d5336e093bd5060f977312516677c87cda7cc86ebac5941cf148054a2bf"
base_class_hash = "0x0"
abi = "manifests/dev/base/abis/contracts/starkludo-GameActions-72b46dc4.json"
reads = []
Expand Down
54 changes: 52 additions & 2 deletions onchain/src/systems/game_actions.cairo
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use starkludo::models::{game::{Game, GameTrait, GameMode}, player::{Player}};
use starkludo::models::{game::{Game, GameTrait, GameStatus, GameMode}, player::{Player}};
use starknet::{ContractAddress, get_block_timestamp};
use core::poseidon::PoseidonTrait;
use core::hash::HashStateTrait;
use traits::Into;
use option::OptionTrait;

#[dojo::interface]
trait IGameActions {
fn create(
ref world: IWorldDispatcher,
created_by: ContractAddress,
game_mode: GameMode,
game_status: GameStatus,
player_green: felt252,
player_yellow: felt252,
player_blue: felt252,
Expand All @@ -17,14 +20,16 @@ trait IGameActions {
) -> Game;
fn restart(ref world: IWorldDispatcher, game_id: u64);
fn terminate_game(ref world: IWorldDispatcher, game_id: u64);
fn join_game(ref world: IWorldDispatcher, game_id: u64, player_color: felt252, username: felt252) -> Game;
// fn join_game(ref world: IWorldDispatcher, game_id: u64, player_color: felt252) -> Game;
}

#[dojo::contract]
mod GameActions {
// use Zero;
// use core::num::traits::Zero;
use core::array::ArrayTrait;
use super::{IGameActions, Game, GameTrait, GameMode, Player};
use super::{IGameActions, Game, GameTrait, GameStatus, GameMode, Player};
use starknet::{ContractAddress, get_caller_address, get_block_timestamp};

#[abi(embed_v0)]
Expand All @@ -33,6 +38,7 @@ mod GameActions {
ref world: IWorldDispatcher,
created_by: ContractAddress,
game_mode: GameMode,
game_status: GameStatus,
player_green: felt252,
player_yellow: felt252,
player_blue: felt252,
Expand Down Expand Up @@ -120,6 +126,49 @@ mod GameActions {
game.terminate_game();
set!(world, (game));
}


fn join_game(ref world: IWorldDispatcher, game_id: u64, player_color: felt252, username: felt252) -> Game {
// Get the current game state
let mut game: Game = get!(world, game_id, (Game));

// Check if the game is in a pending state
assert(game.game_status == GameStatus::Waiting, 'Game is not waiting for players');

// Add the player to the game based on the color
if player_color == 'green' {
assert(game.player_green.is_zero(), 'Green player already joined');
game.player_green = username.into();
} else if player_color == 'yellow' {
assert(game.player_yellow.is_zero(), 'Yellow player already joined');
game.player_yellow = username.into();
} else if player_color == 'blue' {
assert(game.player_blue.is_zero(), 'Blue player already joined');
game.player_blue = username.into();
} else if player_color == 'red' {
assert(game.player_red.is_zero(), 'Red player already joined');
game.player_red = username.into();
} else {
panic(array!['Invalid player color']);
}

// Check if all players have joined
let green_joined: u8 = if !game.player_green.is_zero() { 1 } else { 0 };
let yellow_joined: u8 = if !game.player_yellow.is_zero() { 1 } else { 0 };
let blue_joined: u8 = if !game.player_blue.is_zero() { 1 } else { 0 };
let red_joined: u8 = if !game.player_red.is_zero() { 1 } else { 0 };

let players_joined: u8 = green_joined + yellow_joined + blue_joined + red_joined;

if players_joined == game.number_of_players {
game.game_status = GameStatus::Ongoing;
}

// Update the game state and return the updated game
set!(world, (game));
get!(world, game_id, (Game))
}

}
}

Expand Down Expand Up @@ -154,3 +203,4 @@ pub impl DiceImpl of DiceTrait {
(random % self.face_count.into() + 1).try_into().unwrap()
}
}

121 changes: 121 additions & 0 deletions onchain/src/tests/test_game.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -170,5 +170,126 @@ mod tests {

assert_eq!(game.game_status, GameStatus::Ended);
}


#[test]
fn test_join_game() {
// Set up the test environment
let caller = contract_address_const::<'ben'>();
let number_of_players = 4;
let game_mode: GameMode = GameMode::MultiPlayer;

testing::set_account_contract_address(caller);
testing::set_contract_address(caller);

// Create a new game
let (mut game, game_actions, world, _) = create_and_setup_game(
game_mode,
number_of_players,
0, // Start with no players
0,
0,
0
);

let game_id = game.id;

// Test joining as red player
game = game_actions.join_game(game_id, 'red', 'player_red');
assert_eq!(game.player_red, 'player_red'.into(), "Red player should be joined");
assert_eq!(game.game_status, GameStatus::Waiting, "Game should still be waiting");

// Test joining as blue player
game = game_actions.join_game(game_id, 'blue', 'player_blue');
assert_eq!(game.player_blue, 'player_blue'.into(), "Blue player should be joined");
assert_eq!(game.game_status, GameStatus::Waiting, "Game should still be waiting");

// Test joining as yellow player
game = game_actions.join_game(game_id, 'yellow', 'player_yellow');
assert_eq!(game.player_yellow, 'player_yellow'.into(), "Yellow player should be joined");
assert_eq!(game.game_status, GameStatus::Waiting, "Game should still be waiting");

// Test joining as green player (last player)
game = game_actions.join_game(game_id, 'green', 'player_green');
assert_eq!(game.player_green, 'player_green'.into(), "Green player should be joined");
assert_eq!(game.game_status, GameStatus::Ongoing, "Game should now be ongoing");

// Create a new game with fewer players
let (mut game2, game_actions2, _, _) = create_and_setup_game(
game_mode,
2, // Only 2 players this time
0,
0,
0,
0
);

let game_id2 = game2.id;

// Join with two players
game2 = game_actions2.join_game(game_id2, 'red', 'player_red_2');
game2 = game_actions2.join_game(game_id2, 'blue', 'player_blue_2');

// Verify game status changes to Ongoing with fewer players
assert_eq!(game2.game_status, GameStatus::Ongoing, "Game should be ongoing with 2/2 players");
}

#[test]
#[should_panic(expected: ('Game is not waiting for players', ))]
fn test_join_non_waiting_game() {
// Set up the test environment
let caller = contract_address_const::<'test_caller'>();
let number_of_players = 2;
let game_mode: GameMode = GameMode::MultiPlayer;

testing::set_account_contract_address(caller);
testing::set_contract_address(caller);

// Create a new game
let (mut game, game_actions, world, _) = create_and_setup_game(
game_mode,
number_of_players,
0,
0,
0,
0
);

let game_id = game.id;

// Fill the game
game = game_actions.join_game(game_id, 'red', 'player_red');
game = game_actions.join_game(game_id, 'blue', 'player_blue');

// This should panic
game_actions.join_game(game_id, 'yellow', 'late_player');
}

#[test]
#[should_panic(expected: ('Invalid player color', ))]
fn test_join_game_invalid_color() {
// Set up the test environment
let caller = contract_address_const::<'test_caller'>();
let number_of_players = 4;
let game_mode: GameMode = GameMode::MultiPlayer;

testing::set_account_contract_address(caller);
testing::set_contract_address(caller);

// Create a new game
let (game, game_actions, world, _) = create_and_setup_game(
game_mode,
number_of_players,
0,
0,
0,
0
);

let game_id = game.id;

// This should panic
game_actions.join_game(game_id, 'purple', 'invalid_player');
}
}

Loading