#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <string.h>
#define MAX_PLAYERS 6
#define MAX_CARDS_IN_HAND 60
#define DECK_SIZE 53 // 52 + Joker(1)
typedef struct {
int rank; // 1-13, Joker = 0
int suit; // 0-3, Joker = -1
} Card;
typedef struct {
Card hand[MAX_CARDS_IN_HAND];
int count;
bool finished; // 上がったか
bool joker_dice_used; // 特殊ルールを使ったか
} Player;
/* ====== 表示補助 ====== */
static const char* rank_str(int r) {
switch (r) {
case 1: return "A";
case 11: return "J";
case 12: return "Q";
case 13: return "K";
case 0: return "JOKER";
default: return "N";
}
}
static void print_card(Card c) {
if (c.rank == 0) {
} else {
if (c.
rank >= 2 && c.
rank <= 10) printf("%d", c.
rank); else printf("%s", rank_str
(c.
rank)); }
}
/* ====== 山札 ====== */
static void init_deck(Card deck[DECK_SIZE]) {
int idx = 0;
for (int s = 0; s < 4; s++) {
for (int r = 1; r <= 13; r++) {
deck[idx++] = (Card){ .rank = r, .suit = s };
}
}
deck[idx++] = (Card){ .rank = 0, .suit = -1 }; // Joker
}
static void shuffle_deck(Card deck[DECK_SIZE]) {
for (int i = 0; i < DECK_SIZE; i++) {
int j
= rand() % DECK_SIZE
; Card tmp = deck[i];
deck[i] = deck[j];
deck[j] = tmp;
}
}
/* ====== 手札操作 ====== */
static void add_card(Player *p, Card c) {
p->hand[p->count++] = c;
}
static Card remove_card_at(Player *p, int idx) {
Card c = p->hand[idx];
for (int i = idx; i < p->count - 1; i++) p->hand[i] = p->hand[i + 1];
p->count--;
return c;
}
/* 同じrankのペアを捨てる(Jokerはペアにならない) */
static int discard_pairs(Player *p) {
int freq[14] = {0}; // 0..13
for (int i = 0; i < p->count; i++) {
int r = p->hand[i].rank;
if (r != 0) freq[r]++;
}
int to_discard_rank[14] = {0};
for (int r = 1; r <= 13; r++) {
to_discard_rank[r] = (freq[r] / 2) * 2;
}
int discarded = 0;
for (int r = 1; r <= 13; r++) {
while (to_discard_rank[r] > 0) {
for (int k = 0; k < 2; k++) {
for (int i = 0; i < p->count; i++) {
if (p->hand[i].rank == r) {
remove_card_at(p, i);
discarded++;
to_discard_rank[r]--;
break;
}
}
}
}
}
return discarded;
}
static bool has_joker(const Player *p) {
for (int i = 0; i < p->count; i++) {
if (p->hand[i].rank == 0) return true;
}
return false;
}
/* ====== 生存者(上がってない)関連 ====== */
static int count_alive(Player players[], int n, int *last_alive) {
int alive = 0;
*last_alive = -1;
for (int i = 0; i < n; i++) {
if (!players[i].finished) {
alive++;
*last_alive = i;
}
}
return alive;
}
static int next_alive(int cur, Player players[], int n) {
for (int step = 1; step <= n; step++) {
int j = (cur + step) % n;
if (!players[j].finished) return j;
}
return -1;
}
/* ====== 全員同時の手札交換(右/左) ====== */
static void rotate_hands(Player players[], int n, int dir) {
// dir: +1 = 右隣と交換(結果として「手札が右へ1つ移動」)
// dir: -1 = 左隣と交換(結果として「手札が左へ1つ移動」)
Card backup[MAX_PLAYERS][MAX_CARDS_IN_HAND];
int bcount[MAX_PLAYERS];
// バックアップ
for (int i = 0; i < n; i++) {
bcount[i] = players[i].count;
memcpy(backup
[i
], players
[i
].
hand, sizeof(Card
) * players
[i
].
count); }
// 入れ替え(同時)
for (int i = 0; i < n; i++) {
int src = (i - dir + n) % n; // i が受け取る相手
players[i].count = bcount[src];
memcpy(players
[i
].
hand, backup
[src
], sizeof(Card
) * bcount
[src
]); }
// 入れ替え後にペア捨て&上がり判定
for (int i = 0; i < n; i++) {
if (players[i].finished) continue; // 上がってる人はそのまま(本来0枚のはず)
discard_pairs(&players[i]);
if (players[i].count == 0) players[i].finished = true;
}
}
/* ====== 特殊ルール ======
Jokerを持ってるとき、その人は1回だけサイコロを振れる
1,2: 変化なし
3,4: 全員が右隣と手札交換(同時)
5,6: 全員が左隣と手札交換(同時)
*/
static void apply_joker_dice_rule(Player players[], int n, int player_id) {
Player *p = &players[player_id];
if (p->joker_dice_used) return;
if (p->finished) return;
if (!has_joker(p)) return;
int dice
= (rand() % 6) + 1; printf("\n[P%d] Joker special: rolled %d\n", player_id
, dice
);
if (dice == 1 || dice == 2) {
} else if (dice == 3 || dice == 4) {
printf(" -> Everyone swaps hands with RIGHT neighbor (simultaneous).\n"); rotate_hands(players, n, +1);
} else { // 5,6
printf(" -> Everyone swaps hands with LEFT neighbor (simultaneous).\n"); rotate_hands(players, n, -1);
}
p->joker_dice_used = true;
}
/* ====== メイン ====== */
int main(void) {
int n;
printf("Number of players (2-%d): ", MAX_PLAYERS
); if (scanf("%d", &n
) != 1 || n
< 2 || n
> MAX_PLAYERS
) { return 0;
}
Player players[MAX_PLAYERS] = {0};
for (int i = 0; i < n; i++) {
players[i].count = 0;
players[i].finished = false;
players[i].joker_dice_used = false;
}
Card deck[DECK_SIZE];
init_deck(deck);
shuffle_deck(deck);
// 配る(順番に1枚ずつ)
for (int i = 0; i < DECK_SIZE; i++) {
add_card(&players[i % n], deck[i]);
}
// 初期ペア捨て&上がり判定
for (int i = 0; i < n; i++) {
discard_pairs(&players[i]);
if (players[i].count == 0) players[i].finished = true;
}
// 開始プレイヤーを生存者に合わせる
int cur = 0;
while (players[cur].finished) cur = (cur + 1) % n;
while (1) {
int last_alive;
int alive = count_alive(players, n, &last_alive);
if (alive == 1) {
printf("\n=== GAME OVER ===\n"); printf("Loser (Old Maid) is P%d\n", last_alive
); break;
}
// 特殊ルール(ジョーカー所持で1回だけ)
apply_joker_dice_rule(players, n, cur);
// 特殊ルールで自分が上がった可能性もあるので再チェック
if (players[cur].finished) {
cur = next_alive(cur, players, n);
continue;
}
// 次の生存者から1枚引く
int target = next_alive(cur, players, n);
Player *p = &players[cur];
Player *t = &players[target];
int idx
= rand() % t
->count
; Card drawn = remove_card_at(t, idx);
add_card(p, drawn);
printf("\n[P%d] drew a card from [P%d]: ", cur
, target
); print_card(drawn);
// ペア捨て
int discarded = discard_pairs(p);
if (discarded > 0) {
printf("[P%d] discarded %d cards (pairs)\n", cur
, discarded
); }
// 上がり判定
if (p->count == 0) {
p->finished = true;
printf("[P%d] finished (no cards)!\n", cur
); }
if (t->count == 0) {
t->finished = true;
printf("[P%d] finished (no cards)!\n", target
); }
cur = next_alive(cur, players, n);
}
return 0;
}