【SUI move】共学营task4游戏解析

本文是对SUI官方举办的最新一期共学营task4的具体分析,有兴趣的伙伴可以看一下。

本文是对SUI官方举办的最新一期共学营task4的具体分析,有兴趣的伙伴可以看一下:

48d4960f891302a638912cb63e24793.jpg 关于task4任务是这样的:

image.png 我们可以看到,这其实可以写成一个猜正反游戏的形式。最简单的比如两人对猜,猜硬币正反,两人质押相同数量的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&lt;ALEXWAKER_FAUCET_COIN>应该是某一次领取的objectID,而不是这个币的objectID! 为了使得游戏公平,由于没有写复杂的判断逻辑,因此在让玩家领水的时候,统一领取相同数量的水。 这个deposit函数中有一些很重要的概念:Coin类型和Balance类型。这是在sui move中两类不同的类型,也是非常重要的概念。 <!--StartFragment-->

  • Coin&lt;T> 是一种具体的资源,表示一个代币实例。每个 Coin 对象都与一个特定的代币类型 T 相关联。
  • 每个 Coin 都有一个唯一的 Object ID,可以作为独立对象在 Sui 网络中流通。 <!--EndFragment--> 而Balance <!--StartFragment-->
  • Balance&lt;T> 是一种聚合的余额资源,表示某个账户对某种代币的总余额。
  • 它是一种抽象资源,不是一个独立的对象,不能直接转移或单独操作。 <!--EndFragment--> 看明白了不?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仓库

  • 原创
  • 学分: 6
  • 分类: Sui
  • 标签:
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
LewisGao
LewisGao
0xd2e9...333f
江湖只有他的大名,没有他的介绍。