{"author":{"address":null,"user":"https://learnblockchain.cn/people/23814"},"content":{"body":"如果你是初学者，强烈建议看下前几节内容：\r\n\r\n[入门 Sui Move 开发：1. 环境安装](https://learnblockchain.cn/article/9814) \r\n\r\n[ 入门 Sui Move 开发：2. 常用命令、编写并发布 Hello World 合约](https://learnblockchain.cn/article/9839)\r\n\r\n[入门 Sui Move 开发：3. Sui Move 语法和设计模式](https://learnblockchain.cn/article/9866) \r\n\r\n[入门 Sui Move 开发：4. Sui Move 中集合、对象、泛型、动态字段](https://learnblockchain.cn/article/9876)\r\n\r\n[入门 Sui Move 开发：5. 发布同质化代币 — NFT\r\n](https://learnblockchain.cn/article/9925)\r\n\r\n\r\n### 内容概览\r\n本节学习目标完成一个掷骰子游戏的合约，游戏内容包含：**初始化的时候合约创建者创建一个游戏，管理员向游戏奖池中存入奖金，管理员从游戏奖池中提取金额，用户玩游戏需要输入猜测的结果和押注金额。**\r\n\r\n实现上面的功能，会涉及到以下新内容的学习：\r\n* 在合约中添加对其他合约的依赖：在合约中添加对自己发布过的其他合约的依赖（水龙头代币合约的依赖）\r\n* 断言 `assert!` 的使用，如果不符合预期则抛出错误并停止执行\r\n* 随机数：在合约中如何生成随机数，使用随机数可以保证游戏结果的随机增加公平性。\r\n* `coin` 模块：对币的拆分、合并处理，可以转移\r\n* `balance` 模块: 游戏合约中对 `token` 的处理（使用之前发布的水龙头代币为游戏币）\r\n\r\n### 合约依赖\r\n创建好合约之后，如果想添加其他合约的依赖需要添加一下配置\r\n\r\n* 模块依赖：在模块中添加对其他模块的依赖\r\n    当前模块 `Move.toml` 添加：\r\n   ```\r\n        [dependencies]\r\n            ...\r\n        coin_greyhao = { local = \"../coin_greyhao\" }   # 你需要依赖的模块的实际路径\r\n   ```\r\n\r\n* 被依赖模块（游戏币合约）需要在 `Move.toml` 中添加：\r\n\r\n     ```\r\n        published-at = \"合约发布得到的 packageId\"\r\n            ...\r\n        [dependencies]\r\n    ```\r\n\r\n### 随机数\r\n\r\n```\r\n    // 使用 new_generator 创建 RandomGenerator 对象\r\n    // random 的值为 0x8 (系统预留的值)\r\n    let mut g = new_generator(random, ctx);\r\n    // 然后通过 RandomGenerator 的实例可以生成不同类型的 随机数\r\n    let win_num = generate_u8_in_range(\u0026mut g, 1, 6);\r\n```\r\n\r\n### Coin、Balance 使用到的方法\r\n\r\nCoin 是对 Balance 的包装；Balance 用来操作 Coin 的数量。\r\n\r\n```\r\n// 获取 in_coin 对象所包含的数量\r\ncoin::value(in_coin)\r\n\r\n// 从 balance 中取出指定数量的 coin，用于转账\r\nlet coin = coin::take(\u0026mut game.pool_amount, amount, ctx);\r\n\r\n// 拆分指定数量的 coin，返回的是 balance 类型\r\nlet coin_balance = in_coin.balance_mut().split(拆分的数量)\r\n\r\n// 合并两个 balance\r\ngame.pool_amount.join(coin_balance);\r\n```\r\n\r\n`Token Coin Balance 关系和转换`\r\n![image.png](https://img.learnblockchain.cn/attachments/2025/02/smpOKMmG67b2af5d0cefb.png)\r\n\r\n\r\n以下是完整代码\r\n```\r\n/// 游戏：掷骰子，用户下注并猜测点数，如果猜中则获取同下注数量相同的奖励，如果猜错则下注金额归游戏池所有\r\nmodule game_greyhao::game_greyhao;\r\n\r\nuse sui::balance::{Self, Balance};\r\nuse sui::coin::{Self, Coin};\r\nuse sui::random::{Random, new_generator, generate_u8_in_range};\r\nuse coin_greyhao::greyhaofaucet::GREYHAOFAUCET;\r\n\r\nconst ErrorUserInsufficient: u64 = 0x101;\r\nconst ErrorGameInsufficient: u64 = 0x101;\r\n\r\npublic struct Game has key {\r\n  id: UID,\r\n  pool_amount: Balance\u003cGREYHAOFAUCET\u003e,\r\n}\r\n\r\npublic struct Admin has key {\r\n  id: UID,\r\n}\r\n\r\nfun init(ctx: \u0026mut TxContext) {\r\n  let game = Game {\r\n    id: object::new(ctx),\r\n    pool_amount: balance::zero()\r\n  };\r\n  transfer::share_object(game);\r\n\r\n  let admin = Admin { id: object::new(ctx) };\r\n  transfer::transfer(admin, ctx.sender());\r\n}\r\n\r\npublic entry fun addCoinToGamePool(game: \u0026mut Game, in_coin: \u0026mut Coin\u003cGREYHAOFAUCET\u003e, amount: u64, _: \u0026mut TxContext) {\r\n  // coin 的总金额\r\n  let value = coin::value(in_coin);\r\n  assert!(amount \u003c= value, ErrorUserInsufficient);\r\n  // 拆分指定金额的 coin\r\n  let coin_balance = in_coin.balance_mut().split(amount);\r\n  // 添加到游戏奖池中\r\n  game.pool_amount.join(coin_balance);\r\n}\r\n\r\npublic entry fun removeCoinFromGamePool(_: \u0026Admin, game: \u0026mut Game, amount: u64, ctx: \u0026mut TxContext) {\r\n  // 池子里的余额是否大于要提取的数量\r\n  assert!(game.pool_amount.value() \u003e= amount, ErrorGameInsufficient);\r\n\r\n  let coin = coin::take(\u0026mut game.pool_amount, amount, ctx);\r\n  transfer::public_transfer(coin, ctx.sender());\r\n}\r\n\r\nentry fun play(game: \u0026mut Game, random: \u0026Random, guess_num: u8, in_coin: \u0026mut Coin\u003cGREYHAOFAUCET\u003e, amount: u64, ctx: \u0026mut TxContext) {\r\n  // 最大下注金额为奖池的 三分之一\r\n  assert!(game.pool_amount.value() \u003e= (amount * 3), ErrorGameInsufficient);\r\n  // 用户余额数值是要大于下注金额\r\n  assert!(in_coin.balance().value() \u003e= amount, ErrorUserInsufficient);\r\n\r\n  let mut g = new_generator(random, ctx);\r\n  let win_num = generate_u8_in_range(\u0026mut g, 1, 6);\r\n\r\n  if(win_num == guess_num) {\r\n    // 从奖池中拿出同下注金额相同的数量作为奖励\r\n    let reward_coin = coin::take(\u0026mut game.pool_amount, amount, ctx);\r\n    // 将奖励返回给玩家\r\n    in_coin.join(reward_coin);\r\n  } else {\r\n    // 下注金额放入池子里\r\n    Self::addCoinToGamePool(game, in_coin, amount, ctx);\r\n  }\r\n}\r\n```\r\n\r\n    \r\n ### 可能遇到的问题 \r\n使用命令行调用 `play` 方法传入 `0x8` 作为 `Random` 的值没问题，但是如果在浏览器中调用会一直报错。\r\n\r\n\r\n以上就是本节的所有内容。\r\n\r\n如果觉得本节内容对你有所帮助，可以点赞鼓励下。\r\n\r\n如果你对文章中内容有任何疑问可以留言。","title":"入门 Sui Move 开发：5. 发布一个小游戏合约"},"history":null,"timestamp":1741233660,"version":1}