本文是对SUI官方举办的最新一期共学营task4的具体分析,有兴趣的伙伴可以看一下。
本文是对SUI官方举办的最新一期共学营task4的具体分析,有兴趣的伙伴可以看一下:
关于task4任务是这样的:
我们可以看到,这其实可以写成一个猜正反游戏的形式。最简单的比如两人对猜,猜硬币正反,两人质押相同数量的coin,赢的人可以拿走所有的coin。
好,明确了需求,那么我们需要设计整个游戏。 首先,游戏本身需要一个struct,包含了游戏的各个属性:管理员,当前参与者的下注,以及资金池子总额。这个结构体应该是一个存储在链上且一直存在的对象,因此设置为key和store。
public struct Game has key, store {
id: UID,
admin: address, // 管理员地址
bets: vector<Bet>, // 当前所有下注
val: Balance<ALEXWAKER_FAUCET_COIN>, // 游戏总资金池 balance类型
}
我们对于“对赌”这个概念其实也可以做一个结构体:这个结构体具有drop能力,即游戏结束应该自动销毁,因此不具备id属性。一个对赌应该包含玩家地址和猜测结果。
public struct Bet has store, drop {
player: address, // 玩家地址
guess: bool, // true: 正, false: 反
}
然后,就是四个最核心的函数:deposit、withdraw和play。玩家调用deposit来参与游戏,管理员可以通过withdraw来从奖池中提取佣金或手续费,play则是开始游戏。
public fun deposit(game: &mut Game, player: address, guess: bool, coin: Coin<ALEXWAKER_FAUCET_COIN>, ctx: &mut TxContext) {
let mut all_balance = into_balance(coin); //coin转balance
balance::join(&mut game.val, all_balance); //balance加入奖池
game.bets.push_back(Bet { player, guess });//该玩家下注加入当前游戏
}
这里有一个很神奇的点。首先我假设你已经完成了task2,有了一个自己的faucet币。这个币本身会有一个objectID。但是,当你每次领faucet的时候,也会生成一个objectID。换句话说,你每次领faucet,都会有本次领取的objectID!我们在这个函数中传入的参数coin:Coin<ALEXWAKER_FAUCET_COIN>
应该是某一次领取的objectID,而不是这个币的objectID!
为了使得游戏公平,由于没有写复杂的判断逻辑,因此在让玩家领水的时候,统一领取相同数量的水。
这个deposit
函数中有一些很重要的概念:Coin
类型和Balance
类型。这是在sui move中两类不同的类型,也是非常重要的概念。
<!--StartFragment-->
Coin<T>
是一种具体的资源,表示一个代币实例。每个 Coin
对象都与一个特定的代币类型 T
相关联。Coin
都有一个唯一的 Object ID
,可以作为独立对象在 Sui 网络中流通。
<!--EndFragment-->
而Balance
<!--StartFragment-->Balance<T>
是一种聚合的余额资源,表示某个账户对某种代币的总余额。Balance
是一种天生适合做奖池的对象。因此我们需要将Coin
转化为Balance
。然后就是withdraw
函数
public fun withdraw(game: &mut Game, amount: u64, ctx: &mut TxContext) {
assert!(ctx.sender() == game.admin);//只有管理员可以取钱
assert!(game.val.value() >= amount);//判断是否取得太多,超过了总奖池
let admin_balance = balance::split(&mut game.val, amount); //分出一定数量的代币转给管理员
let admin_coin = from_balance(admin_balance, ctx);
public_transfer(admin_coin, ctx.sender());
}
这个逻辑就与deposit
相反,是要把Balance
转为Coin
,利用函数from_balance
可以把Balance
转化为Coin
,然后调用public_transfer
将这些coin发送给管理员账户。这里多提一嘴,ctx往往代表上下文信息,这是一个很抽象的概念,我们一般用ctx查看当前函数的调用者。
最后就是play函数:
public fun play(game: &mut Game, rand: &Random, ctx: &mut TxContext) {
assert!(ctx.sender() == game.admin); //只有调用者才能调
let mut gen = random::new_generator(rand, ctx);
let _random_number = random::generate_u64(&mut gen);
let _result = random::generate_bool(&mut gen);
// 找到猜对的玩家
let mut winner_address = @0x0;
let mut len = vector::length(&game.bets);
while (len > 0) {
let bet = &game.bets[len - 1];
if (bet.guess == _result) {
winner_address = bet.player;
};
len = len - 1;
}; // 循环查找当前获胜者
// 分配奖励
let coin_value = game.val.value();
let win_balance = balance::split(&mut game.val, coin_value);
let win_coin = from_balance(win_balance, ctx);
public_transfer(win_coin, winner_address);
// 清空下注记录
// 因为目前只让两个人玩,因此写死两个清空就好
vector::pop_back(&mut game.bets);
vector::pop_back(&mut game.bets);
}
我们用一个循环遍历vector
,找到猜对的人,然后把奖金全部给他。分割奖池需要用split
函数,这个函数此一个参数是Balance
对象,第二个参数是u64
整数。最后两个pop_back,表示把游戏中参与对赌的bet
清空,下次咱还可以接着玩!最后,sui官方随机数objectID是0x8
,主网和测试网都是这个。
好了,以上就是这个游戏的解析。
欢迎关注: HOH水分子公众号 HOH水分子X账号 bilibili课程 GitHub仓库
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!