div#pop_ad { opacity: 0; }
AD
首页 > 数字货币 > 正文

EOS井字游戏(Tic-tac-toe)智能合约

[2021-01-29 06:30:35] 来源: 编辑:wangjia 点击量:
评论 点击收藏
导读: 很多网络游戏编程的基础课程,会以井字游戏开始。EOS官方教程也不例外,本篇将讲述如何在EOS区块链上做一个去中心化的双人互动井字游戏。 一、设计与准备玩家该游戏将采取标准的3X3井字游戏方式。玩家被

EOS井字游戏(Tic-tac-toe)智能合约

很多网络游戏编程的基础课程,会以井字游戏开始。EOS官方教程也不例外,本篇将讲述如何在EOS区块链上做一个去中心化的双人互动井字游戏。

一、设计与准备

玩家

该游戏将采取标准的3X3井字游戏方式。玩家被定义为两个角色:host与·challenger,Host(庄)首先画。同时,每对玩家最多同时玩两局,一局先下者做庄,一局后下者坐庄。

棋盘与数据结构

Instead of using o and x as in the traditional tic tac toe game. We use 1 to denote movement by host, 2 to denote movement by challenger, and 0 to denote empty cell. Furthermore, we will use one dimensional array to store the board. Hence: 在游戏中,1代表host占的格子,2代表challenger占的格子,0代表还没被占领的格子。

如上图,我们设定x(叉)为host占的格子,用1表示;o(圈)为challenger占的格子,用2表示,则上图的数据结构可以表示为:[0, 2, 1, 0, 1, 0, 1, 2, 2](先横后竖)

动作

User will have the following actions to interact with this contract:

create: create a new game restart: restart an existing game, host or challenger is allowed to do this close: close an existing game, which frees up the storage used to store the game, only host is allowed to do this move: make a movement

玩家将会做出以下动作,与智能合约交互:
create: 创建游戏restart: 重启当前已有的一局游戏,host和challenger都能执行该行为close: 关闭一局游戏,将存储资源从EOS中释放,只有host允许操作该行为move: 占领某一个位置的格子
创建合约

下面教程,将会基于一个命名为 tic.tac.toe 的账户创建智能合约。如果 tic.tac.toe 账户名被使用,可以创建另外一个账户名,并将代码中出现的 tic.tac.toe 改为你的账户名。 如果还没建立账户,请先按以下命令建立账户,再进行下一步操作。
$ cleos create account ${creator_name} ${contract_account_name} ${contract_pub_owner_key} ${contract_pub_active_key} --permission ${creator_name}@active
# e.g. $ cleos create account inita tic.tac.toe EOS4toFS3YXEQCkuuw1aqDLrtHim86Gz9u3hBdcBw5KNPZcursVHq EOS7d9A3uLe6As66jzN8j44TXJUqJSK3bFjjEEqR4oTvNAB3iM9SA --permission inita@active

请确保钱包已经解锁,并且创建者的私钥已经被导入钱包。

二、开始

本合约将创建以下三个文件:
tic_tac_toe.hpp => 定义合约数据结构的头文件tic_tac_toe.cpp => 合约主逻辑代码tic_tac_toe.abi => 合约ABI文件
三、定义数据结构

新建(或打开)tic_tac_toe.hpp 文件,加入如下代码模板:
// Import necessary library
#include // Generic eosio library, i.e. print, type, math, etc
using namespace eosio;
namespace tic_tac_toe {
static const account_name games_account = N(games);
static const account_name code_account = N(tic.tac.toe); //账户名称,如果需要用其他账户名,在这里改
// Your code here
}

游戏列表

在这个智能合约中,我们需要定义一个Table列表,用来存储所有游戏。
...
namespace tic_tac_toe {
...
typedef eosio::multi_index games;
}

其中第一个参数games_account定义了table名称。后一个参数game定义了游戏结构(Game Structure)。

游戏数据结构 Game Structure
...
namespace tic_tac_toe {
static const uint32_t board_len = 9; //井字游戏3x3,数据长度
struct game {
game() {};
//构造函数
game(account_name challenger, account_name host):challenger(challenger), host(host), turn(host) {
// Initialize board
initialize_board();
};
account_name challenger;
account_name host;
account_name turn; // = account name of host/ challenger
account_name winner = N(none); // = none/ draw/ account name of host/ challenger
uint8_t board[9]; //棋盘数组
// 将3x3上的格子全部清零,即回到最初状态
void initialize_board() {
for (uint8_t i = 0; i
using namespace eosio;
/**
* The apply() method must have C calling convention so that the blockchain can lookup and
* call these methods.
*/
extern "C" {
using namespace tic_tac_toe;
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
// Put your action handler here
}
} // extern "C"

行为响应框架

我们只想让合约相应来自于合约创建人帐号,而且不同的行为需要执行不同的命令,因此,创建impl结构:
using namespace eosio;
namespace tic_tac_toe {
struct impl {
...
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
if (code == code_account) {
if (action == N(create)) {
\t//注意:eosio::unpack_action_data()用于将收到的动作转换为T
impl::on(eosio::unpack_action_data());
} else if (action == N(restart)) {
impl::on(eosio::unpack_action_data());
} else if (action == N(close)) {
impl::on(eosio::unpack_action_data());
} else if (action == N(move)) {
impl::on(eosio::unpack_action_data());
}
}
}
};
}
...
extern "C" {
using namespace tic_tac_toe;
// apply 方法用来执行广播到该合约的事件
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
//直接执行impl方法
impl().apply(receiver, code, action);
}
} // extern "C"

在impl结构中,需要添加如下实现方法,先把框架写入,具体实现方法见下文:
...
struct impl {
...
/**
* @param create - action to be applied
*/
void on(const create& c) {
// Put code for create action here
}
/**
* @brief Apply restart action
* @param restart - action to be applied
*/
void on(const restart& r) {
// Put code for restart action here
}
/**
* @brief Apply close action
* @param close - action to be applied
*/
void on(const close& c) {
// Put code for close action here
}
/**
* @brief Apply move action
* @param move - action to be applied
*/
void on(const move& m) {
// Put code for move action here
}
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
...

实现 Create 创建游戏

在创建游戏操作中,我们需要做以下几件事情:
确保该动作已经host签名确保host和challenger不是同一个玩家确保没有已经存在的游戏将该新建的游戏保存到db中
struct impl {
...
/**
* @brief Apply create action
* @param create - action to be applied
*/
void on(const create& c) {
require_auth(c.host); //从c.host中获取host用户名,并且验证是否已经签名
eosio_assert(c.challenger != c.host, "challenger shouldn t be the same as host"); //确保host和challenger不是一个玩家
//检查是否存在相同的游戏
games existing_host_games(code_account, c.host);
auto itr = existing_host_games.find( c.challenger );
eosio_assert(itr == existing_host_games.end(), "game already exists");
existing_host_games.emplace(c.host, [&]( auto& g ) {
//判断是否是相同的游戏,即challenger一致,host一致,当前庄家一致
g.challenger = c.challenger;
g.host = c.host;
g.turn = c.host;
});
}
...
}

实现 Close 结束游戏

结束游戏操作中,我们需要做以下事情:
确保该动作已经host签名确保游戏存在从链上数据库中删除游戏
struct impl {
...
/**
* @brief Apply close action
* @param close - action to be applied
*/
void on(const close& c) {
require_auth(c.host); //验证host签名
//检查游戏是否存在,实现方法在上面create中有了
games existing_host_games(code_account, c.host);
auto itr = existing_host_games.find( c.challenger );
eosio_assert(itr != existing_host_games.end(), "game doesn t exists");
//删除游戏,释放链上资源
existing_host_games.erase(itr);
}
...
}

实现 Move 占位操作

在Move操作中,我们需要实现:
确保该操作已经被host或者challenger签名确保游戏存在确保游戏尚未结束确保该操作是有host或者challenger发出的指令确保当前轮正好由正确的玩家操作确认占位操作是有效的更新棋盘数据变更玩家轮次判断是否有胜者保存游戏数据到链上数据库中
struct impl {
...
bool is_valid_movement(const movement& mvt, const game& game_for_movement) {
// 占位是否有效
}
account_name get_winner(const game& current_game) {
// 是否有胜者
}
...
/**
* @brief Apply move action
* @param move - action to be applied
*/
void on(const move& m) {
require_auth(m.by); //判断当前轮的用户是否已经签名
// 检查游戏是否存在
games existing_host_games(code_account, m.host);
auto itr = existing_host_games.find( m.challenger );
eosio_assert(itr != existing_host_games.end(), "game doesn t exists");
// 检查游戏是否已经结束
eosio_assert(itr->winner == N(none), "the game has ended!");

// 检查指令是否由host或者challenger发出
eosio_assert(m.by == itr->host || m.by == itr->challenger, "this is not your game!");

// 检查发送指令的是不是轮到该轮
eosio_assert(m.by == itr->turn, "it s not your turn yet!");
// 检查占位是否有效
eosio_assert(is_valid_movement(m.mvt, *itr), "not a valid movement!");
// 修改占位数据,1-host,2-challenger
const auto cell_value = itr->turn == itr->host ? 1 : 2;
const auto turn = itr->turn == itr->host ? itr->challenger : itr->host;
existing_host_games.modify(itr, itr->host, [&]( auto& g ) {
g.board[m.mvt.row * 3 + m.mvt.column] = cell_value;
g.turn = turn;
//检查是否有胜者
g.winner = get_winner(g);
});
}
...
}

验证占位是否有效实现方法: 1- 检查占位是否有效的方法,是否超出9格长度 2- 格子是否位空,即为0
struct impl {
...
/**
* @brief Check if cell is empty
* @param cell - value of the cell (should be either 0, 1, or 2)
* @return true if cell is empty
*/
bool is_empty_cell(const uint8_t& cell) {
return cell == 0;
}
/**
* @brief Check for valid movement
* @detail Movement is considered valid if it is inside the board and done on empty cell
* @param movement - the movement made by the player
* @param game - the game on which the movement is being made
* @return true if movement is valid
*/
bool is_valid_movement(const movement& mvt, const game& game_for_movement) {
uint32_t movement_location = mvt.row * 3 + mvt.column;
bool is_valid = movement_location 做成网页,前端通过eosjs调用智能合约执行。在合约中加入代币转移,这个游戏则可以立即变成一个有奖惩的小游戏。

添加新手交流群:币种分析、每日早晚盘分析

添加助理微信,一对一亲自指导:YoYo8abc

查看更多:

为您推荐