TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
4.3 KiB
151 lines
4.3 KiB
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;
|
|
|
|
/**
|
|
* 从一手牌枚举所有合法牌型(用于 AI 决策与合法性校验)。
|
|
* 同 (type,rank,length) 的去重,避免手牌多花色导致组合爆炸。
|
|
*/
|
|
final class MoveGenerator
|
|
{
|
|
/**
|
|
* @param list<Card> $hand
|
|
* @return list<Combo>
|
|
*/
|
|
public static function all(array $hand): array
|
|
{
|
|
if ($hand === []) {
|
|
return [];
|
|
}
|
|
$byRank = [];
|
|
foreach ($hand as $c) {
|
|
$byRank[$c->rank][] = $c;
|
|
}
|
|
$ranks = \array_keys($byRank);
|
|
\sort($ranks);
|
|
$has = static fn (int $r) => isset($byRank[$r]);
|
|
$cnt = static fn (int $r) => \count($byRank[$r] ?? []);
|
|
|
|
$out = [];
|
|
$seen = [];
|
|
|
|
$push = static function (array $cards) use (&$out, &$seen): void {
|
|
$combo = Combo::parse($cards);
|
|
if ($combo === null) {
|
|
return;
|
|
}
|
|
$key = $combo->type . ':' . $combo->rank . ':' . $combo->length;
|
|
if (isset($seen[$key])) {
|
|
return;
|
|
}
|
|
$seen[$key] = true;
|
|
$out[] = $combo;
|
|
};
|
|
|
|
// 单 / 对 / 三
|
|
foreach ($ranks as $r) {
|
|
$push([$byRank[$r][0]]);
|
|
if ($cnt($r) >= 2) {
|
|
$push([$byRank[$r][0], $byRank[$r][1]]);
|
|
}
|
|
if ($cnt($r) >= 3) {
|
|
$push([$byRank[$r][0], $byRank[$r][1], $byRank[$r][2]]);
|
|
}
|
|
}
|
|
|
|
// 三带一 / 三带二
|
|
foreach ($ranks as $r) {
|
|
if ($cnt($r) < 3) {
|
|
continue;
|
|
}
|
|
$triple = [$byRank[$r][0], $byRank[$r][1], $byRank[$r][2]];
|
|
foreach ($ranks as $s) {
|
|
if ($s === $r) {
|
|
continue;
|
|
}
|
|
if ($cnt($s) >= 1) {
|
|
$push(\array_merge($triple, [$byRank[$s][0]]));
|
|
}
|
|
if ($cnt($s) >= 2) {
|
|
$push(\array_merge($triple, [$byRank[$s][0], $byRank[$s][1]]));
|
|
}
|
|
}
|
|
}
|
|
|
|
// 顺子 (5..12 连)
|
|
for ($len = 5; $len <= 12; $len++) {
|
|
for ($start = 3; $start + $len - 1 <= 14; $start++) {
|
|
$ok = true;
|
|
$cards = [];
|
|
for ($k = 0; $k < $len; $k++) {
|
|
$rr = $start + $k;
|
|
if (!$has($rr)) {
|
|
$ok = false;
|
|
break;
|
|
}
|
|
$cards[] = $byRank[$rr][0];
|
|
}
|
|
if ($ok) {
|
|
$push($cards);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 连对 (3..10 连)
|
|
for ($len = 3; $len <= 10; $len++) {
|
|
for ($start = 3; $start + $len - 1 <= 14; $start++) {
|
|
$ok = true;
|
|
$cards = [];
|
|
for ($k = 0; $k < $len; $k++) {
|
|
$rr = $start + $k;
|
|
if ($cnt($rr) < 2) {
|
|
$ok = false;
|
|
break;
|
|
}
|
|
$cards[] = $byRank[$rr][0];
|
|
$cards[] = $byRank[$rr][1];
|
|
}
|
|
if ($ok) {
|
|
$push($cards);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 飞机(纯三连,2..6 连)
|
|
for ($len = 2; $len <= 6; $len++) {
|
|
for ($start = 3; $start + $len - 1 <= 14; $start++) {
|
|
$ok = true;
|
|
$cards = [];
|
|
for ($k = 0; $k < $len; $k++) {
|
|
$rr = $start + $k;
|
|
if ($cnt($rr) < 3) {
|
|
$ok = false;
|
|
break;
|
|
}
|
|
$cards[] = $byRank[$rr][0];
|
|
$cards[] = $byRank[$rr][1];
|
|
$cards[] = $byRank[$rr][2];
|
|
}
|
|
if ($ok) {
|
|
$push($cards);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 炸弹
|
|
foreach ($ranks as $r) {
|
|
if ($cnt($r) === 4) {
|
|
$push($byRank[$r]);
|
|
}
|
|
}
|
|
|
|
// 火箭
|
|
if ($has(Card::JOKER_SMALL) && $has(Card::JOKER_BIG)) {
|
|
$push([$byRank[Card::JOKER_SMALL][0], $byRank[Card::JOKER_BIG][0]]);
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
}
|
|
|