入门 Sui Move 开发:6. 发布一个小游戏合约

  • greyhao
  • 发布于 6天前
  • 阅读 330

在 Sui 上开发一个简单的掷骰子游戏

如果你是初学者,强烈建议看下前几节内容:

入门 Sui Move 开发:1. 环境安装 

 入门 Sui Move 开发:2. 常用命令、编写并发布 Hello World 合约

入门 Sui Move 开发:3. Sui Move 语法和设计模式 

入门 Sui Move 开发:4. Sui Move 中集合、对象、泛型、动态字段

入门 Sui Move 开发:5. 发布同质化代币 — NFT

内容概览

本节学习目标完成一个掷骰子游戏的合约,游戏内容包含:初始化的时候合约创建者创建一个游戏,管理员向游戏奖池中存入奖金,管理员从游戏奖池中提取金额,用户玩游戏需要输入猜测的结果和押注金额。

实现上面的功能,会涉及到以下新内容的学习:

  • 在合约中添加对其他合约的依赖:在合约中添加对自己发布过的其他合约的依赖(水龙头代币合约的依赖)
  • 断言 assert! 的使用,如果不符合预期则抛出错误并停止执行
  • 随机数:在合约中如何生成随机数,使用随机数可以保证游戏结果的随机增加公平性。
  • coin 模块:对币的拆分、合并处理,可以转移
  • balance 模块: 游戏合约中对 token 的处理(使用之前发布的水龙头代币为游戏币)

合约依赖

创建好合约之后,如果想添加其他合约的依赖需要添加一下配置

  • 模块依赖:在模块中添加对其他模块的依赖 当前模块 Move.toml 添加:

        [dependencies]
            ...
        coin_greyhao = { local = "../coin_greyhao" }   # 你需要依赖的模块的实际路径
  • 被依赖模块(游戏币合约)需要在 Move.toml 中添加:

        published-at = "合约发布得到的 packageId"
            ...
        [dependencies]

随机数

    // 使用 new_generator 创建 RandomGenerator 对象
    // random 的值为 0x8 (系统预留的值)
    let mut g = new_generator(random, ctx);
    // 然后通过 RandomGenerator 的实例可以生成不同类型的 随机数
    let win_num = generate_u8_in_range(&mut g, 1, 6);

Coin、Balance 使用到的方法

Coin 是对 Balance 的包装;Balance 用来操作 Coin 的数量。

// 获取 in_coin 对象所包含的数量
coin::value(in_coin)

// 从 balance 中取出指定数量的 coin,用于转账
let coin = coin::take(&mut game.pool_amount, amount, ctx);

// 拆分指定数量的 coin,返回的是 balance 类型
let coin_balance = in_coin.balance_mut().split(拆分的数量)

// 合并两个 balance
game.pool_amount.join(coin_balance);

Token Coin Balance 关系和转换 image.png

以下是完整代码

/// 游戏:掷骰子,用户下注并猜测点数,如果猜中则获取同下注数量相同的奖励,如果猜错则下注金额归游戏池所有
module game_greyhao::game_greyhao;

use sui::balance::{Self, Balance};
use sui::coin::{Self, Coin};
use sui::random::{Random, new_generator, generate_u8_in_range};
use coin_greyhao::greyhaofaucet::GREYHAOFAUCET;

const ErrorUserInsufficient: u64 = 0x101;
const ErrorGameInsufficient: u64 = 0x101;

public struct Game has key {
  id: UID,
  pool_amount: Balance<GREYHAOFAUCET>,
}

public struct Admin has key {
  id: UID,
}

fun init(ctx: &mut TxContext) {
  let game = Game {
    id: object::new(ctx),
    pool_amount: balance::zero()
  };
  transfer::share_object(game);

  let admin = Admin { id: object::new(ctx) };
  transfer::transfer(admin, ctx.sender());
}

public entry fun addCoinToGamePool(game: &mut Game, in_coin: &mut Coin<GREYHAOFAUCET>, amount: u64, _: &mut TxContext) {
  // coin 的总金额
  let value = coin::value(in_coin);
  assert!(amount <= value, ErrorUserInsufficient);
  // 拆分指定金额的 coin
  let coin_balance = in_coin.balance_mut().split(amount);
  // 添加到游戏奖池中
  game.pool_amount.join(coin_balance);
}

public entry fun removeCoinFromGamePool(_: &Admin, game: &mut Game, amount: u64, ctx: &mut TxContext) {
  // 池子里的余额是否大于要提取的数量
  assert!(game.pool_amount.value() >= amount, ErrorGameInsufficient);

  let coin = coin::take(&mut game.pool_amount, amount, ctx);
  transfer::public_transfer(coin, ctx.sender());
}

entry fun play(game: &mut Game, random: &Random, guess_num: u8, in_coin: &mut Coin<GREYHAOFAUCET>, amount: u64, ctx: &mut TxContext) {
  // 最大下注金额为奖池的 三分之一
  assert!(game.pool_amount.value() >= (amount * 3), ErrorGameInsufficient);
  // 用户余额数值是要大于下注金额
  assert!(in_coin.balance().value() >= amount, ErrorUserInsufficient);

  let mut g = new_generator(random, ctx);
  let win_num = generate_u8_in_range(&mut g, 1, 6);

  if(win_num == guess_num) {
    // 从奖池中拿出同下注金额相同的数量作为奖励
    let reward_coin = coin::take(&mut game.pool_amount, amount, ctx);
    // 将奖励返回给玩家
    in_coin.join(reward_coin);
  } else {
    // 下注金额放入池子里
    Self::addCoinToGamePool(game, in_coin, amount, ctx);
  }
}

可能遇到的问题

使用命令行调用 play 方法传入 0x8 作为 Random 的值没问题,但是如果在浏览器中调用会一直报错。

以上就是本节的所有内容。

如果觉得本节内容对你有所帮助,可以点赞鼓励下。

如果你对文章中内容有任何疑问可以留言。

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

0 条评论

请先 登录 后评论
greyhao
greyhao
Sui 合约开发打怪升级中 & 多年前端/手机端开发 & 低成本撸毛选手 欢迎一起学习交流