SUI Move官方示例合约实践——游戏类:井字游戏(tic_tac_toe)
Tic-Tac-Toe
即井字游戏,又称井字棋。玩法是有两个玩家,一个打圈(◯),一个打叉(✗),轮流在3乘3的格上打自己的符号,最先以横、直、斜连成一线则为胜。TicTacToe
),包括游戏棋盘、游戏参与双方地址、游戏状态等,都由游戏管理员拥有。 MarkMintCap
),他们可以调用合约接口send_mark_to_game
创建一个带有放置信息的标记对象(Mark
),并将标记发送给管理员来表明他们放置标记的意图。 place_mark
尝试在游戏棋盘上放置其标记。每次放心标记后,都会检测(update_winner
)是否已经产生获胜方。若产生了获胜方便会将“奖杯”转到获胜方的地址。shared_tic_tac_toe
),使用共享对象更昂贵, 但它消除了中心化服务的依赖。create_game
)TicTacToe
游戏对象,指定游戏对战双方的地址,该游戏对象会转发给管理员MarkMintCap
对象 /// `x_address` and `o_address` are the account address of the two players.
public entry fun create_game(x_address: address, o_address: address, ctx: &mut TxContext) {
// TODO: Validate sender address, only GameAdmin can create games.
let id = object::new(ctx);
let game_id = object::uid_to_inner(&id);
let gameboard = vector[
vector[option::none(), option::none(), option::none()],
vector[option::none(), option::none(), option::none()],
vector[option::none(), option::none(), option::none()],
];
let game = TicTacToe {
id,
gameboard,
cur_turn: 0,
game_status: IN_PROGRESS,
x_address: x_address,
o_address: o_address,
};
transfer::transfer(game, tx_context::sender(ctx));
let cap = MarkMintCap {
id: object::new(ctx),
game_id,
remaining_supply: 5,
};
transfer::transfer(cap, x_address);
let cap = MarkMintCap {
id: object::new(ctx),
game_id,
remaining_supply: 5,
};
transfer::transfer(cap, o_address);
}
send_mark_to_game
)Mark
)给游戏管理员MarkSentEvent
事件 /// Generate a new mark intended for location (row, col).
/// This new mark is not yet placed, just transferred to the game.
public entry fun send_mark_to_game(
cap: &mut MarkMintCap,
game_address: address,
row: u64,
col: u64,
ctx: &mut TxContext,
) {
if (row > 2 || col > 2) {
abort EInvalidLocation
};
let mark = mint_mark(cap, row, col, ctx);
// Once an event is emitted, it should be observed by a game server.
// The game server will then call `place_mark` to place this mark.
event::emit(MarkSentEvent {
game_id: *&cap.game_id,
mark_id: object::id(&mark),
});
transfer::public_transfer(mark, game_address);
}
place_mark
)place_mark
尝试在游戏棋盘上放置其标记update_winner
)是否已经产生获胜方 public entry fun place_mark(game: &mut TicTacToe, mark: Mark, ctx: &mut TxContext) {
// If we are placing the mark at the wrong turn, or if game has ended,
// destroy the mark.
let addr = get_cur_turn_address(game);
if (game.game_status != IN_PROGRESS || &addr != &mark.player) {
delete_mark(mark);
return
};
let cell = get_cell_mut_ref(game, mark.row, mark.col);
if (option::is_some(cell)) {
// There is already a mark in the desired location.
// Destroy the mark.
delete_mark(mark);
return
};
option::fill(cell, mark);
update_winner(game);
game.cur_turn = game.cur_turn + 1;
if (game.game_status != IN_PROGRESS) {
// Notify the server that the game ended so that it can delete the game.
event::emit(GameEndEvent { game_id: object::id(game) });
if (game.game_status == X_WIN) {
transfer::transfer(Trophy { id: object::new(ctx) }, *&game.x_address);
} else if (game.game_status == O_WIN) {
transfer::transfer(Trophy { id: object::new(ctx) }, *&game.o_address);
}
}
}
delete_game
) public entry fun delete_game(game: TicTacToe) {
let TicTacToe { id, gameboard, cur_turn: _, game_status: _, x_address: _, o_address: _ } = game;
while (vector::length(&gameboard) > 0) {
let row = vector::pop_back(&mut gameboard);
while (vector::length(&row) > 0) {
let element = vector::pop_back(&mut row);
if (option::is_some(&element)) {
let mark = option::extract(&mut element);
delete_mark(mark);
};
option::destroy_none(element);
};
vector::destroy_empty(row);
};
vector::destroy_empty(gameboard);
object::delete(id);
}
别名 | 地址 | 角色 |
---|---|---|
Jason | 0x5c5882d73a6e5b6ea1743fb028eff5e0d7cc8b7ae123d27856c5fe666d91569a |
游戏管理员 |
Alice | 0x2d178b9704706393d2630fe6cf9415c2c50b181e9e3c7a977237bb2929f82d19 |
玩家X |
Bob | 0xf2e6ffef7d0543e258d4c47a53d6fa9872de4630cc186950accbd83415b009f0 |
玩家O |
export JASON=0x5c5882d73a6e5b6ea1743fb028eff5e0d7cc8b7ae123d27856c5fe666d91569a
export ALICE=0x2d178b9704706393d2630fe6cf9415c2c50b181e9e3c7a977237bb2929f82d19
export BOB=0xf2e6ffef7d0543e258d4c47a53d6fa9872de4630cc186950accbd83415b009f0
切换到Jason账号
sui client publish --gas-budget 100000000
export PACKAGE_ID=0x65a98e73760b6940e6d5118efd66c71cec4f06a86ce731c73f440d09df5d30ee
create_game
)切换到Jason
sui client call --function create_game --package $PACKAGE_ID --module tic_tac_toe --args $ALICE $BOB --gas-budget 10000000
# PACKAGE_ID::tic_tac_toe::TicTacToe
export GAME=0x4274301e3e07a4c0450cf35fddb8691ab61e450512811f14ed406606162dcef6
# PACKAGE_ID::tic_tac_toe::MarkMintCap
export MARK_MINT_CAP_ALICE_X=0x7b5fe85ed140e46375abd1ebe2f0cd4a32701149cfd4b1727fb97bc4d016176e
export MARK_MINT_CAP_BOB_O=0x3b56a2e184d7d53bda1df52fa4571129dd243bc4caf3303c8d7221f7d89a5fd7
sui client object $GAME
send_mark_to_game
)切换Alice
sui client call --function send_mark_to_game --package $PACKAGE_ID --module tic_tac_toe --args $MARK_MINT_CAP_ALICE_X $JASON 0 0 --gas-budget 10000000
# PACKAGE_ID::tic_tac_toe::Mark
export MARK_ALICE_X=0x8ab75d83f6a0b32eeb5a12a67775424ab4e382ee55ee17fe2e0249d70aceb428
sui client object $MARK_ALICE_X
place_mark
)切换到Jason,管理员监听到玩家X创建的Mark对象事件后,根据Mark对象进行棋盘的放置
sui client call --function place_mark --package $PACKAGE_ID --module tic_tac_toe --args $GAME $MARK_ALICE_X --gas-budget 10000000
$ sui client object $GAME
切换到Bob
sui client call --function send_mark_to_game --package $PACKAGE_ID --module tic_tac_toe --args $MARK_MINT_CAP_BOB_O $JASON 1 2 --gas-budget 10000000
export MARK_BOB_O=0x2f0d3aad177cbc9189e31ef97d344fb7348b5b082520b7f24e6faee08d6ded1d
切换到Jason,管理员监听到玩家O创建的Mark对象事件后,根据Mark对象进行棋盘的放置
sui client call --function place_mark --package $PACKAGE_ID --module tic_tac_toe --args $GAME $MARK_BOB_O --gas-budget 10000000
sui client object $GAME
切换到Alice
sui client call --function send_mark_to_game --package $PACKAGE_ID --module tic_tac_toe --args $MARK_MINT_CAP_ALICE_X $JASON 1 1 --gas-budget 10000000
export MARK_ALICE_X=0xcef6776d2c1a9174bc3f127c8b8a5cc2108ee38fd9d50696479a482ec0aeb14e
切换到Jason,管理员监听到玩家X创建的Mark对象事件后,根据Mark对象进行棋盘的放置
sui client call --function place_mark --package $PACKAGE_ID --module tic_tac_toe --args $GAME $MARK_ALICE_X --gas-budget 10000000
sui client object $GAME
切换到Bob
sui client call --function send_mark_to_game --package $PACKAGE_ID --module tic_tac_toe --args $MARK_MINT_CAP_BOB_O $JASON 0 1 --gas-budget 10000000
export MARK_BOB_O=0xb5908f9936a0983cec6c1b7f0f388d23a56af78ed183c8a26a9c32301b315b7f
切换到Jason,管理员监听到玩家O创建的Mark对象事件后,根据Mark对象进行棋盘的放置
sui client call --function place_mark --package $PACKAGE_ID --module tic_tac_toe --args $GAME $MARK_BOB_O --gas-budget 10000000
sui client object $GAME
切换到Alice
sui client call --function send_mark_to_game --package $PACKAGE_ID --module tic_tac_toe --args $MARK_MINT_CAP_ALICE_X $JASON 2 2 --gas-budget 10000000
export MARK_ALICE_X=0x32d526fd8d33bf282ba5443e350c4c9ad3b981fa37a063296ead3833dfac7f24
切换到Jason,管理员监听到玩家X创建的Mark对象事件后,根据Mark对象进行棋盘的放置
sui client call --function place_mark --package $PACKAGE_ID --module tic_tac_toe --args $GAME $MARK_ALICE_X --gas-budget 10000000
sui client object $GAME
delete_game
)切换到Jason
sui client call --function delete_game --package $PACKAGE_ID --module tic_tac_toe --args $GAME --gas-budget 10000000
Transaction Digest: BWZb4FmdgTrt6BrbRYYVgVf7coLgk1HpzuNEZnLBrtFD
Internal error, cannot read the object: Object has been deleted object_id: 0x4274301e3e07a4c0450cf35fddb8691ab61e450512811f14ed406606162dcef6 at version: SequenceNumber(17681407) in digest o#7gyGAp71YXQRoxmFBaHxofQXAipvgHyBKPyxmdSJxyv
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!