= 0x8000 ? $x - 0x10000 : $x; } function mouse_y(int $lParam): int { $y = ($lParam >> 16) & 0xFFFF; return $y >= 0x8000 ? $y - 0x10000 : $y; } function in_rect(int $x, int $y, int $rx, int $ry, int $rw, int $rh): bool { return $x >= $rx && $x <= $rx + $rw && $y >= $ry && $y <= $ry + $rh; } function card_rank(int $card): int { if ($card == 52) { return 16; } if ($card == 53) { return 17; } return intdiv($card, 4) + 3; } function card_suit(int $card): int { return $card % 4; } function rank_label(int $rank): string { $labels = [ 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => 'J', 12 => 'Q', 13 => 'K', 14 => 'A', 15 => '2', 16 => '小王', 17 => '大王', ]; return $labels[$rank] ?? '?'; } function card_label(int $card): string { $rank = card_rank($card); if ($rank >= 16) { return 'JOKER'; } $suits = ['黑', '红', '梅', '方']; return rank_label($rank) . $suits[card_suit($card)]; } function card_suit_label(int $card): string { $rank = card_rank($card); if ($rank >= 16) { return ''; } $suits = ['♠', '♥', '♣', '♦']; return $suits[card_suit($card)]; } function sort_cards(array &$cards): void { usort($cards, function (int $a, int $b): int { $ra = card_rank($a); $rb = card_rank($b); return $ra == $rb ? card_suit($a) <=> card_suit($b) : $ra <=> $rb; }); } function rank_counts(array $cards): array { $counts = []; foreach ($cards as $card) { $rank = card_rank($card); $counts[$rank] = ($counts[$rank] ?? 0) + 1; } ksort($counts); return $counts; } function is_consecutive(array $ranks): bool { sort($ranks); for ($i = 1; $i < count($ranks); $i++) { if ($ranks[$i] != $ranks[$i - 1] + 1) { return false; } } return true; } function analyze_play(array $cards): array { $n = count($cards); if ($n == 0) { return ['valid' => false, 'type' => 'none', 'main' => 0, 'len' => 0]; } $counts = rank_counts($cards); $ranks = array_keys($counts); $values = array_values($counts); sort($values); if ($n == 2 && in_array(16, $ranks, true) && in_array(17, $ranks, true)) { return ['valid' => true, 'type' => 'rocket', 'main' => 17, 'len' => 2]; } if ($n == 4 && count($counts) == 1) { return ['valid' => true, 'type' => 'bomb', 'main' => $ranks[0], 'len' => 4]; } if ($n == 6 && $values == [1, 1, 4]) { foreach ($counts as $rank => $count) { if ($count == 4) { return ['valid' => true, 'type' => 'four_two_single', 'main' => $rank, 'len' => 6]; } } } if ($n == 8 && $values == [2, 2, 4]) { foreach ($counts as $rank => $count) { if ($count == 4) { return ['valid' => true, 'type' => 'four_two_pair', 'main' => $rank, 'len' => 8]; } } } if ($n == 1) { return ['valid' => true, 'type' => 'single', 'main' => $ranks[0], 'len' => 1]; } if ($n == 2 && count($counts) == 1) { return ['valid' => true, 'type' => 'pair', 'main' => $ranks[0], 'len' => 2]; } if ($n == 3 && count($counts) == 1) { return ['valid' => true, 'type' => 'triple', 'main' => $ranks[0], 'len' => 3]; } if ($n == 4 && $values == [1, 3]) { foreach ($counts as $rank => $count) { if ($count == 3) { return ['valid' => true, 'type' => 'triple_single', 'main' => $rank, 'len' => 4]; } } } if ($n == 5 && $values == [2, 3]) { foreach ($counts as $rank => $count) { if ($count == 3) { return ['valid' => true, 'type' => 'triple_pair', 'main' => $rank, 'len' => 5]; } } } if ($n >= 5 && count($counts) == $n && max($ranks) < 15 && is_consecutive($ranks)) { return ['valid' => true, 'type' => 'straight', 'main' => max($ranks), 'len' => $n]; } if ($n >= 6 && $n % 2 == 0 && min($values) == 2 && max($values) == 2 && max($ranks) < 15 && is_consecutive($ranks)) { return ['valid' => true, 'type' => 'pair_sequence', 'main' => max($ranks), 'len' => $n]; } if ($n >= 6 && $n % 3 == 0 && min($values) == 3 && max($values) == 3 && max($ranks) < 15 && is_consecutive($ranks)) { return ['valid' => true, 'type' => 'airplane', 'main' => max($ranks), 'len' => $n]; } return ['valid' => false, 'type' => 'invalid', 'main' => 0, 'len' => $n]; } function can_beat(array $play, array $last): bool { if (!$play['valid']) { return false; } if (!$last['valid']) { return true; } if ($play['type'] == 'rocket') { return $last['type'] != 'rocket'; } if ($play['type'] == 'bomb' && $last['type'] != 'bomb' && $last['type'] != 'rocket') { return true; } return $play['type'] == $last['type'] && $play['len'] == $last['len'] && $play['main'] > $last['main']; } function play_name(array $play): string { if (!$play['valid']) { return '无'; } $names = [ 'single' => '单牌', 'pair' => '对子', 'triple' => '三张', 'triple_single' => '三带一', 'triple_pair' => '三带一对', 'four_two_single' => '四带二', 'four_two_pair' => '四带两对', 'straight' => '顺子', 'pair_sequence' => '连对', 'airplane' => '飞机', 'bomb' => '炸弹', 'rocket' => '王炸', ]; return ($names[$play['type']] ?? $play['type']) . ' ' . rank_label($play['main']); } function remove_cards(array &$hand, array $cards): void { foreach ($cards as $card) { $idx = array_search($card, $hand, true); if ($idx !== false) { unset($hand[$idx]); } } $hand = array_values($hand); } function hand_strength(array $hand): int { $score = 0; $counts = rank_counts($hand); foreach ($hand as $card) { $rank = card_rank($card); if ($rank >= 16) { $score += 7; } elseif ($rank == 15) { $score += 4; } elseif ($rank >= 13) { $score += 2; } } foreach ($counts as $rank => $count) { if ($count == 4) { $score += 9; } elseif ($count == 3) { $score += 4; } elseif ($count == 2 && $rank >= 12) { $score += 2; } } return $score; }