1. 使用对象封装游戏数据:可定义Player、Card、Record等类,更好地组织数据及业务逻辑。
2. 存储游戏数据:使用Redis或其他数据库存储玩家、牌组、出牌记录等数据,实现服务端数据持久化。
3. 完善各游戏规则:发牌、出牌判断、升级、进贡、炸弹、胜负、计分等规则都需要细致全面地实现。
4. 服务端维护玩家状态:判断玩家是否在线、出牌顺序、已出牌信息等,实时更新并与前端同步状态。
5. 实现更丰富的消息类型:除了出牌、登录和聊天外,还需要上线、离线、获取排行、使用道具、完成任务等类型的消息交互。
6. 聊天功能完善:可加入私聊、表情、禁言等功能,同时需要判断和过滤敏感词汇,确保聊天体验。
7. 使用Daemons管理定时器:使用daemon管理多个定时器,执行不同的游戏逻辑,更高效稳定。
8. 日志与异常处理:需要进行完善的日志记录与异常处理,保证程序健壮性。
9. 加密机制:如对用户密码进行加密存储与验证,保证用户数据安全。
10. 部署与性能优化:使用Supervisord部署,根据QPS优化Worker进程数量,提高服务器性能。
服务端代码
用workman 写一个掼蛋规则,包含了 发牌、出牌判断、升级、进贡、炸弹、胜负、计分等规则
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Workerman\Lib\Timer;
// worker进程,监听4000端口
$worker = new Worker("websocket://0.0.0.0:4000");
// 全局变量
$pai = get_pai(); // 牌组
$player = []; // 玩家列表
$chu_pai = []; // 出牌记录
$dipai = []; // 底牌
$level = 1; // 当前级别
// 定时器,每秒执行
Timer::add(1, function() {
// 发牌
fa_pai();
// 检测是否升级
sheng_ji();
// 检测是否需要进贡
jin_gong();
// 检测是否有炸弹
zha_dan();
// 检测是否有胜者
sheng_fu();
});
// 收到消息,判断消息类型执行对应逻辑
$worker->onMessage = function($connection, $msg) {
$data = json_decode($msg, true);
switch ($data['type']) {
case 'login': // 登录
login($connection, $data);
break;
case 'chu_pai': // 出牌
chu_pai($data['pai']);
send_chu_pai(); // 发送最新出牌记录到前端
break;
case 'chat': // 聊天
chat($msg);
break;
}
};
// 出牌判断
function chu_pai($pai) {
...
}
// 其他规则实现
function fa_pai() { ... }
function sheng_ji() { ... }
function jin_gong() { ... }
function zha_dan() { ... }
function sheng_fu() { ... }
// 运行worker
Worker::runAll();
以上是标准掼蛋棋牌中52张牌的映射数组,分为:1. 方块(1-13):数字2到10和J、Q、K、A
2. 红桃(21-34):数字2到10和J、Q、K、A
3. 梅花(41-54):数字2到10和J、Q、K、A
4. 黑桃(61-74):数字2到10和J、Q、K、A
$pai = [
1 => 'A', 2 => '2', 3 => '3', 4 => '4', 5 => '5',
6 => '6', 7 => '7', 8 => '8', 9 => '9', 10 => 'X',
11 => 'J', 12 => 'Q', 13 => 'K', 14 => 'A',
// 红桃
21 => 'A', 22 => '2', 23 => '3', 24 => '4', 25 => '5',
26 => '6', 27 => '7', 28 => '8', 29 => '9', 30 => 'X',
31 => 'J', 32 => 'Q', 33 => 'K', 34 => 'A',
// 梅花
41 => 'A', 42 => '2', 43 => '3', 44 => '4', 45 => '5',
46 => '6', 47 => '7', 48 => '8', 49 => '9', 50 => 'X',
51 => 'J', 52 => 'Q', 53 => 'K', 54 => 'A',
// 黑桃
61 => 'A', 62 => '2', 63 => '3', 64 => '4', 65 => '5',
66 => '6', 67 => '7', 68 => '8', 69 => '9', 70 => 'X',
71 => 'J', 72 => 'Q', 73 => 'K', 74 => 'A'
];
发牌逻辑
发牌逻辑主要包含:
1. 洗牌:使用shuffle()函数随机乱序牌组$pai。
2. 发牌:从牌组$pai中发17张牌给每位玩家,存储在$player[$i]['pai']中。
3. 底牌:将剩余2张牌设为底牌$global['dipai']。
4. 发送手牌:将每位玩家的手牌发送到对应前端,展示给用户。
5. 出牌记录:每个玩家都有一个出牌记录$player[$i]['out']数组,初始为空。
发牌是棋牌游戏的开始,需要公平与完全随机。牌组的数据结构设计也需要合理,方便进行各种算法判断与操作
function fa_pai() {
global $pai, $player;
// 洗牌
shuffle($pai);
// 发17张牌给每个玩家
for ($i=0; $i<4; $i++) {
$player[$i]['pai'] = array_slice($pai, $i * 17, 17);
$player[$i]['out'] = []; // 出牌记录
}
// 剩余两张作为底牌
$global['dipai'] = array_slice($pai, 68, 2);
// 发送玩家手牌到前端
foreach ($player as $p) {
$p['connection']->send(json_encode($p['pai']));
}
}
出牌的逻辑
出牌逻辑主要包含:
1. 获取请求出牌的玩家$p。
2. 调用can_chu_pai()函数判断请求出的牌$pai['pai']是否合法。如果不合法,发送出牌失败提示到玩家前端。
3. 如果合法,从玩家手中移除要出的牌。
4. 添加至玩家的出牌记录和公共出牌记录$chu_pai。
5. 发送最新出牌记录$chu_pai到所有前端。
6. can_chu_pai()函数判断首出牌可以任意出,否则需要出最大牌+1或炸弹。
function chu_pai($pai) {
global $player, $chu_pai;
// 获取出牌玩家
$p = $player[$pai['p']];
// 判断是否合法出牌
if (!can_chu_pai($pai['pai'], $chu_pai)) {
$p['connection']->send(json_encode([
'type' => 'chu_pai_fail'
]));
return;
}
// 出牌,从手中移除
$p['pai'] = array_diff($p['pai'], [$pai['pai']]);
// 添加至出牌记录
$p['out'][] = $pai['pai'];
$chu_pai[] = $pai['pai'];
// 发送最新出牌记录到前端
send_chu_pai();
}
// 判断是否可以出牌
function can_chu_pai($pai, $chu_pai) {
if (empty($chu_pai)) {
return true; // 首出牌,任意一张都可以出
} else {
$max_pai = max($chu_pai);
// 最大牌+1或炸弹
return ($pai > $max_pai) || ($pai == $max_pai && count($chu_pai) >= 3);
}
升级逻辑主要包含:
1. 判断是否需要升级:如果出牌记录$chu_pai数量达到当前级别$level的17倍,即可升级。
2. 升到下一级:级别$level加1,发送升级提示到前端。
3. 执行对应级别规则:不同级别有不同的规则,如第二级和第三级规则函数。
4. 规则实现:根据产品需求,实现不同级别的规则,如特殊牌型、特殊操作等。升级是棋牌游戏的重要规则之一,需要根据不同级别设计丰富的规则,不断增加游戏难度和趣味性,给玩家带来全新的游戏体验。
php
// 升级
function sheng_ji() {
global $chu_pai, $level;
if (count($chu_pai) >= 17 * $level) {
$level++; // 升到下一级
// 发送升级提示到前端
broadcast(json_encode([
'type' => 'sheng_ji',
'level' => $level
]));
// 执行下一级牌型判断规则
switch ($level) {
case 2:
sheng_ji_rule_2();
break;
case 3:
sheng_ji_rule_3();
break;
...
}
}
}
// 第二级升级规则
function sheng_ji_rule_2() {
...
}
// 第三级升级规则
function sheng_ji_rule_3() {
...
}
进贡逻辑主要包含:
1. 设置最大进贡次数$max_jin_gong,默认为3次,每轮开始前重置每个玩家的进贡次数。
2. 判断每个玩家是否调用前端的JinGong方法进行进贡,如果进贡次数未超过最大次数则进行进贡。3. 进贡次数+1,并发送进贡提示到前端,提示上一张出的牌。
4. 从底牌中弹出一张牌,添加至玩家的出牌记录中。
5. 重新发两张底牌,以备其他玩家进贡。进贡是掼蛋游戏的特色规则之一,需要根据不同级别和玩家状态设计周全的规则,增加游戏趣味性和策略性,是该游戏的重要特色。
function jin_gong() {
global $player, $level;
$max_jin_gong = 3; // 最多进贡次数
// 每轮开始前,重置每个玩家的进贡次数
foreach ($player as &$p) {
$p['jin_gong_num'] = 0;
}
// 判断是否有玩家进贡
foreach ($player as $p) {
if ($p['jin_gong_num'] < $max_jin_gong && $p['connection']-> JinGong) { // 调用前端JinGong方法
$p['jin_gong_num']++; // 进贡次数+1
// 发送进贡提示
broadcast(json_encode([
'type' => 'jin_gong',
'pai' => $p['out'][count($p['out']) - 1] // 上一张出的牌
]));
// 添加底牌至出牌记录
$p['out'][] = array_pop($global['dipai']);
// 重新发两张底牌
$global['dipai'] = [get_random_pai(), get_random_pai()];
}
}
}
炸弹判断逻辑主要包含:
1. 获取当前出牌池$chu_pai中的最大牌$max_pai。
2. 遍历每个玩家,判断手中是否有2张或更多的最大牌。
3. 如果有,则发送炸弹提示到对应玩家前端。
4. 出两张最大牌作为炸弹,从手中移除,添加至出牌记录和出牌池。
5. 重新发两张随机牌给触发炸弹的玩家。
6. 结束循环,每个回合只判断一次炸弹。炸弹是掼蛋游戏的重要规则,需要根据当前出牌情况与每个玩家的手牌判断是否有炸弹,并执行对应的操作与提示。这需要对各种可能的牌型和玩家手牌状态进行全面考虑,保证规则的准确性与高效性。
function zha_dan() {
global $player, $chu_pai;
// 获取当前可出的最大牌
$max_pai = max($chu_pai);
foreach ($player as $p) {
// 玩家手中是否有2张或更多最大牌
if (count($p['pai']) >= 2 && in_array($max_pai, $p['pai'], true)) {
// 发送炸弹提示
$p['connection']->send(json_encode([
'type' => 'zha_dan'
]));
// 出炸弹,多个最大牌从手中移除
$zha_dan_pai = array_splice($p['pai'],
array_search($max_pai, $p['pai'], true), 2);
$chu_pai = array_merge($chu_pai, $zha_dan_pai);
// 添加至出牌记录
$p['out'] = array_merge($p['out'], $zha_dan_pai);
// 重新发两张牌
$p['pai'][] = get_random_pai();
$p['pai'][] = get_random_pai();
break;
}
}
}
这里是一个掼蛋游戏胜负判断逻辑的实现:
胜负判断逻辑主要包含:
1. 初始化胜利玩家$winner为null。
2. 遍历每个玩家,先判断是否有出牌,如果未出牌则不判断。
3. 判断该玩家出的牌是否为最大炸弹或最大对子,如果是则标记为最大$is_max。
4. 继续遍历其他玩家,如果找到更大的牌则标记$is_max为false,并退出内层循环。
5. 如果$is_max为true,则找到胜利玩家,退出外层循环。
6. 如果找到胜利玩家,则发送胜利提示到前端,显示胜利玩家序号。
7. 如果未找到,则本轮没有胜负结果,继续下一轮。
// 胜负判断
function sheng_fu() {
global $player;
$winner = null; // 胜利玩家
// 判断牌型和数量
foreach ($player as $p) {
if (count($p['out']) == 0) {
continue; // 未出牌者不判断
}
// 是否为炸弹或对子最大
$is_max = true;
foreach ($player as $other) {
if ($other != $p && count($other['out']) > 0
&& max($other['out']) > max($p['out'])) {
$is_max = false;
break;
}
}
if ($is_max) {
$winner = $p; // 找到胜利玩家
break;
}
}
if ($winner) {
// 发送胜利提示
broadcast(json_encode([
'type' => 'sheng_li',
'winner' => $winner['idx'] // 玩家序号
]));
}
在掼蛋游戏的代码规则上,我还有以下几点补充:
1. 针对不同的级别设计丰富的规则,如三带二、飞机等复杂的牌型,增加游戏难度和趣味性。这需要对各种牌型进行深入研究与判断。
2. 对庄家和闲家设定不同的规则,如庄家必须出首张牌等,增加游戏策略性。
3. 设计吃牌规则,如吃三张相连的牌。这需要判断手中是否有满足条件的组合,以及与当前出牌池的关系。
4. 实现跟牌与不跟牌,如连续三次最大出牌后不必跟上一次的最大牌。这需要对出牌历史进行判断与限制。
5. 添加选项设置,如扣底牌规则、最大进贡次数等。这需要添加对应的参数并在逻辑中进行判断。
6. 实现超级玩家和钻石玩家等特权玩家的特殊规则。这需要在玩家初始化阶段进行判断与分类。
7. 进行高级策略判断,如统计每个玩家的出牌频率与喜好,预测对手可能的出牌策略。这需要记录各种出牌数据并进行算法计算。
8. 在游戏过程中增加随机事件,如中途出现王炸等。这需要在发牌时留有余地,以便在后续过程中出现。
9. 增加聊天窗口,用于玩家交流与信任增加。需要实现双向实时通信与过滤不良信息。
10. 实现背景音乐、游戏特效等,增强游戏氛围与乐趣。需要进行网页音视频的开发与应用。