猜数字游戏(又叫宾果Bingo游戏),游戏规则如下——
游戏开始,系统会生成一个四位整数,且这四位整数每一位数字都是不同的。接下去由游戏参与者开始猜这个四位数字。每猜测一次,系统会告诉游戏参与者本次猜测中有几个数字出现在答案中,但是位置不对(用B来表示);有几个数字不仅在答案中,而且位置也对了(用A来表示)。
举例说明,如果最终答案是7439。我的猜测是1234,那么系统会告诉我1A1B,因为其中一个数字(3)位置正确,所以1A;另有一个数字(4)位置不对,但是答案中有这个数字,所以1B。
因此,当玩家猜出4A0B时,就是猜对答案的时候。
本篇教程之所以采用这个游戏作为案例,是因为它用到了微信平台开发中比较重要的一个模式(用户操作需要考虑上下文的模式),在WeiPHP中,set_user_status
这个函数(字面意思:设置用户状态)帮我们很好解决了这个问题,它的原理是将该用户之前发的消息缓存起来。
这个猜数字游戏并没有用到太多的微信公众号接口,仅仅是很简单的一个消息窗口就可以完成这个游戏的流程——
1. 当用户输入“猜数字”时,开始进入游戏(调用插件),从这句话开始用户所有的输入都会被缓存,直到游戏结束。如果是游戏刚开始,那么我们应该向客户端返回一串提示语,告诉用户游戏开始并提示用户进行数字输入。
2. 接下去是游戏主体部分,用户每次输入,我们都对用户输入内容缓存,同时计算出应该告诉用户的aAbB,同时把这个内容也进行缓存。
3. 直到用户输入“退出”/“答案”。游戏结束。
从这个流程来看,所有的内容都可以在WeixinAddonModel.class.php中完成。
接下去我们来看一下reply方法传入的两个参数,其中第一个参数$dataArr
在第一篇教程中已经提到过,用户本次用微信客户端消息处发出的消息就存在$dataArr['Content']
中(注意这里Content首字母大写),而第二个参数$keywordArr
在前一篇教程中并没有提到,从字面意思看,它是“关键词数组”,每次都会被跟着传进reply方法,那么它实际上就是实现上下文缓存用的。
在这个插件中,我这样设计$keywordArr
的数据结构——
$keywordArr['step']
用于存储当前猜测的步骤数,初始化时为0;
$keywordArr['answer']
用于存储初始化时候生成的答案;
$keywordArr['userInput']
用数组方式存储用户的每一次输入;
$keywordArr['response']
用数组方式存储对用户每一次输入的返回结果。
具体代码看下面注释就可以了。
完整插件可从百度云获取:http://pan.baidu.com/s/1jGGWKrs
<?php
namespace Addons\BingoGame\Model;
use Home\Model\WeixinModel;
/**
* BingoGame的微信模型
* @author: Emptyset<21324784@qq.com>
*/
class WeixinAddonModel extends WeixinModel{
function reply($dataArr, $keywordArr = array()) {
$config = getAddonConfig ( 'BingoGame' ); // 获取后台插件的配置参数
//dump($config);
if (empty($keywordArr) && $dataArr['Content'] == 'BingoGame' || $dataArr['Content'] == '猜数字') {
$this -> gameStart($dataArr,$keywordArr);
$text = "游戏开始了!我已经想好了一个四个数字互不相同的四位数。";
$text .= "\n【游戏规则:假如答案是2134,如果猜测2043,那么我会回复1A2B,因为其中有1个数字位置对了,有2个数字位置不对,但是在答案中有。】";
$text .= "\n请输入一个四个数字各不相同的四位数(例如:1034)。";
$text .= "或者输入:\n退出:结束游戏\n答案:查看答案";
$this -> replyText($text);
} else if ($dataArr['Content']=='退出' || $dataArr['Content']=='结束' || $dataArr['Content']=='exit' || $dataArr['Content']=='quit') {
$this -> replyText("已经结束游戏");
return true;
} else if ($dataArr['Content']=='答案') {
$this -> replyText("答案是:".$keywordArr["answer"]."。笨死了,这都猜不出来~游戏结束了!");
return true;
}else {
switch ($keywordArr['status']) {
case 'trial' : //用户处在尝试答案阶段
//首先保证输入合法
if ($this -> inputValid($dataArr['Content'])) {
$step = $keywordArr['step'];
$step++;
$keywordArr['step'] = $step;
$keywordArr['userInput'] []= $dataArr['Content'];
$response = $this -> computeResult($dataArr['Content'],$keywordArr['answer']);
$keywordArr['response'] []= $response;
if ($response == '4A0B') {
set_user_status('BingoGame',$keywordArr);
$this -> replyText("恭喜你答对了!一共猜测了".$step."次");
return true;
} else {
set_user_status('BingoGame',$keywordArr);
$this -> replyText($response);
}
} else {
set_user_status('BingoGame',$keywordArr);
$text = "亲,说好的要输入一个四位整数的呢?四个数字都不能有重复的哟~";
$this -> replyText($text);
}
break;
default :
break;
}
}
}
//判断输入是否合法
private function inputValid($input) {
$pattern = "/^(?!.*?(\d).*?\1.*?$)\d{4}$/";
return preg_match($pattern,$input);
}
//生成一个四位的无重复位的数字
private function generateNumber() {
//先生成最高位
$number = array();
$number[0] = mt_rand(1,9);
$n = 0;
while($n<4) {
$number[$n] = mt_rand(0,9);
$duplicate = false;
for($i=0;$i<$n;$i++) {
if($number[$n] == $number[$i]) {
$duplicate = true;
}
}
if(!$duplicate) {
$n++;
}
}
return (string)($number[0]*1000 + $number[1]*100 + $number[2]*10 + $number[3]);
}
//游戏初始化
private function gameStart(&$dataArr,&$keywordArr) {
$keywordArr['step'] = 0;
$keywordArr['status'] = 'trial';
$keywordArr['userInput'] = array();
$keywordArr['userInput'] []= $dataArr['Content'];
$keywordArr['response'] = array();
$keywordArr['response'] []= '';
$keywordArr['answer'] = $this -> generateNumber();
set_user_status('BingoGame',$keywordArr);
return true;
}
//@description 根据合法输入返回结果
//@return string(4) 例如:2A1B
private function computeResult($input, $answer) {
$A = 0;
$B = 0;
//按位排查
for($i=0;$i<4;$i++) {
for($j=0;$j<4;$j++) {
if($i==$j && $input[$i]==$answer[$j]) {
$A++;
break;
} else if ($i!=$j && $input[$i]==$answer[$j]) {
$B++;
break;
}
}
}
$result = $A.'A'.$B.'B';
return $result;
}
}