Write fn init()
Same as default.
fn init(self: @ContractState) {
let world = self.world_dispatcher.read();
let core_actions = pixelaw::core::utils::get_core_actions(world);
core_actions.update_app(APP_KEY, APP_ICON, APP_MANIFEST);
// TODO: replace this with proper granting of permission
core_actions.update_permission('snake',
Permission {
alert: false,
app: false,
color: true,
owner: false,
text: true,
timestamp: false,
action: false
});
core_actions.update_permission('paint',
Permission {
alert: false,
app: false,
color: true,
owner: false,
text: true,
timestamp: false,
action: false
});
}
Write a draft of fn interact()
Initially, load some information
let caller_address = get_caller_address();
let mut game = get!(world, (position.x, position.y), (Game));
let timestamp = starknet::get_block_timestamp();
We call this interact()
function when we click pixels. So, we use pixel.alert
to call functions.
if pixel.alert = 'reveal' {
// call reveal function
self.reveal(default_params);
} else if pixel.alert = 'explode' {
// call explode function
self.explode(default_params);
} else if self.ownerless_space(default_params, size: size) == true {
// declare a new game
} else {
// we can't do anything, so we just return
'find a free area'.print();
}
Implement function when we start game
If self.ownerless_space()
returns true, we can start new game. Let's start new game.
let mut id = world.uuid();
// set game information
game =
Game {
x: position.x,
y: position.y,
id,
creator: player,
state: State::Open,
size: size,
mines_amount: mines_amount,
started_timestamp: timestamp
};
emit!(world, GameOpened {game_id: game.id, creator: player});
set!(world, (game));
let mut i: u64 = 0;
let mut j: u64 = 0;
// update pixels to set color and alert. Then, if player click the pixel, they call reveal function.
loop {
if i >= size {
break;
}
j = 0;
loop {
if j >= size {
break;
}
core_actions
.update_pixel(
player,
system,
PixelUpdate {
x: position.x + j,
y: position.y + i,
color: Option::Some(default_params.color), //should I pass in a color to define the minesweepers field color?
alert: Option::Some('reveal'),
timestamp: Option::None,
text: Option::None,
app: Option::Some(system),
owner: Option::Some(player),
action: Option::None,
}
);
j += 1;
};
i += 1;
};
Then, set mines.
let mut num_mines = 0;
loop {
if num_mines >= mines_amount {
break;
}
let timestamp_felt252 = timestamp.into();
let x_felt252 = position.x.into();
let y_felt252 = position.y.into();
let m_felt252 = num_mines.into();
//random = (timestamp + i) + position.x.into() + position.y.into();
let hash: u256 = poseidon_hash_span(array![timestamp_felt252, x_felt252, y_felt252, m_felt252].span()).into();
random_number = hash % (size * size).into();
core_actions
.update_pixel(
player,
system,
PixelUpdate {
//x: (position.x + random_x),
x: position.x + (random_number / size.into()).try_into().unwrap(),
//y: (position.y + random_y),
y: position.y + (random_number % size.into()).try_into().unwrap(),
color: Option::Some(default_params.color),
alert: Option::Some('explode'),
timestamp: Option::None,
text: Option::None,
app: Option::Some(system),
owner: Option::Some(player),
action: Option::None,
}
);
num_mines += 1;
};
Whole code in minesweeper_actions
is like this
#[dojo::contract]
/// contracts must be named as such (APP_KEY + underscore + "actions")
mod minesweeper_actions {
use starknet::{
get_tx_info, get_caller_address, get_contract_address, get_execution_info, ContractAddress
};
use super::IMinesweeperActions;
use pixelaw::core::models::pixel::{Pixel, PixelUpdate};
use pixelaw::core::models::permissions::{Permission};
use pixelaw::core::actions::{
IActionsDispatcher as ICoreActionsDispatcher,
IActionsDispatcherTrait as ICoreActionsDispatcherTrait
};
use super::{Game, State};
use super::{APP_KEY, APP_ICON, APP_MANIFEST, GAME_MAX_DURATION};
use pixelaw::core::utils::{get_core_actions, Direction, Position, DefaultParameters};
use debug::PrintTrait;
use poseidon::poseidon_hash_span;
#[derive(Drop, starknet::Event)]
struct GameOpened {
game_id: u32,
creator: ContractAddress
}
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
GameOpened: GameOpened
}
fn subu8(nr: u8, sub: u8) -> u8 {
if nr >= sub {
return nr - sub;
} else {
return 0;
}
}
// ARGB
// 0xFF FF FF FF
// empty: 0x 00 00 00 00
// normal color: 0x FF FF FF FF
fn encode_color(r: u8, g: u8, b: u8) -> u32 {
(r.into() * 0x10000) + (g.into() * 0x100) + b.into()
}
fn decode_color(color: u32) -> (u8, u8, u8) {
let r = (color / 0x10000);
let g = (color / 0x100) & 0xff;
let b = color & 0xff;
(r.try_into().unwrap(), g.try_into().unwrap(), b.try_into().unwrap())
}
// impl: implement functions specified in trait
#[external(v0)]
impl ActionsImpl of IMinesweeperActions<ContractState> {
/// Initialize the MyApp App (TODO I think, do we need this??)
fn init(self: @ContractState) {
let world = self.world_dispatcher.read();
let core_actions = pixelaw::core::utils::get_core_actions(world);
core_actions.update_app(APP_KEY, APP_ICON, APP_MANIFEST);
// TODO: replace this with proper granting of permission
core_actions.update_permission('snake',
Permission {
alert: false,
app: false,
color: true,
owner: false,
text: true,
timestamp: false,
action: false
});
core_actions.update_permission('paint',
Permission {
alert: false,
app: false,
color: true,
owner: false,
text: true,
timestamp: false,
action: false
});
}
/// Put color on a certain position
///
/// # Arguments
///
/// default_params: Default parameters for the action
/// size: Size of the board
/// mines_amount: Amount of mines to place
fn interact(self: @ContractState, default_params: DefaultParameters, size: u64, mines_amount: u64) {
'put_color'.print();
// Load important variables
let world = self.world_dispatcher.read();
let core_actions = get_core_actions(world);
let position = default_params.position;
let player = core_actions.get_player_address(default_params.for_player);
let system = core_actions.get_system_address(default_params.for_system);
// Load the Pixel
let mut pixel = get!(world, (position.x, position.y), (Pixel));
let caller_address = get_caller_address();
let mut game = get!(world, (position.x, position.y), (Game));
let timestamp = starknet::get_block_timestamp();
if (pixel.alert == 'reveal') {
// call reveal function
self.reveal(default_params);
} else if (pixel.alert == 'explode') {
// call explode function
self.explode(default_params);
} else if (self.ownerless_space(default_params, size: size) == true ){
// start a new game
let mut id = world.uuid();
game =
Game {
x: position.x,
y: position.y,
id,
creator: player,
state: State::Open,
size: size,
mines_amount: mines_amount,
started_timestamp: timestamp
};
emit!(world, GameOpened {game_id: game.id, creator: player});
set!(world, (game));
let mut i: u64 = 0;
let mut j: u64 = 0;
loop {
if i >= size {
break;
}
j = 0;
loop {
if j >= size {
break;
}
core_actions
.update_pixel(
player,
system,
PixelUpdate {
x: position.x + j,
y: position.y + i,
color: Option::Some(default_params.color), //should I pass in a color to define the minesweepers field color?
alert: Option::Some('reveal'),
timestamp: Option::None,
text: Option::None,
app: Option::Some(system),
owner: Option::Some(player),
action: Option::None,
}
);
j += 1;
};
i += 1;
};
let mut random_number: u256 = 0;
let mut num_mines = 0;
loop {
if num_mines >= mines_amount {
break;
}
let timestamp_felt252 = timestamp.into();
let x_felt252 = position.x.into();
let y_felt252 = position.y.into();
let m_felt252 = num_mines.into();
let hash: u256 = poseidon_hash_span(array![timestamp_felt252, x_felt252, y_felt252, m_felt252].span()).into();
random_number = hash % (size * size).into();
core_actions
.update_pixel(
player,
system,
PixelUpdate {
//x: (position.x + random_x),
x: position.x + (random_number / size.into()).try_into().unwrap(),
//y: (position.y + random_y),
y: position.y + (random_number % size.into()).try_into().unwrap(),
color: Option::Some(default_params.color),
alert: Option::Some('explode'),
timestamp: Option::None,
text: Option::None,
app: Option::Some(system),
owner: Option::Some(player),
action: Option::None,
}
);
num_mines += 1;
};
} else {
// we can't do anything, so we just return
'find a free area'.print();
}
assert(
pixel.owner.is_zero() || (pixel.owner) == player || starknet::get_block_timestamp()
- pixel.timestamp < COOLDOWN_SECS,
'Cooldown not over'
);
// We can now update color of the pixel
core_actions
.update_pixel(
player,
system,
PixelUpdate {
x: position.x,
y: position.y,
color: Option::Some(default_params.color),
alert: Option::None,
timestamp: Option::None,
text: Option::None,
app: Option::Some(system),
owner: Option::Some(player),
action: Option::None // Not using this feature for myapp
}
);
'put_color DONE'.print();
}
/// Reveal a pixel on a certain position
///
/// # Arguments
/// default_params: Default parameters for the action
fn reveal(self: @ContractState, default_params: DefaultParameters) {
}
/// Explode a pixel on a certain position
///
/// # Arguments
/// default_params: Default parameters for the action
fn explode(self: @ContractState, default_params: DefaultParameters) {
}
/// Check if a certain position is ownerless
///
/// # Arguments
/// default_params: Default parameters for the action
/// size: Size of the board
fn ownerless_space(self: @ContractState, default_params: DefaultParameters, size: u64) -> bool {
return true; // for debug
}
}
}
Test so far
Please do not foget adding grant_writer:
world.grant_writer('Game',minesweeper_actions_address);
Check your codes by this command:
sozo test