Ще цікавіше. Для початку визначимо, що будемо називати колодою. В моїх уявленях колодою можна назвати будь-яку 1-вимірну впорядковану колекцію карт. За замовчаням будемо уважати, що гравець може взяти тільки верхню або нижню карту з колоди, при цьому кількість карт в колоді зменшиться на 1 і кількість карт у гравця збільшиться на 1. Крім того, іноді колоду треба буде перемішувати (shuffle), для цього можна розбити колоду на 2 менші колоди. Майже кожна карта в колоді має "сусіда" з верху і з низу.
Все це впливає на вибір структури даних, в яких будемо зберігати всі карти колоди. В даному випадку 1-вимірний 2-напрямний звязний список прекрасно підійде для зберіганя і розбитя колоди. Структура "колода" (struct deck) буде містити інфо про (вказівники на) верхній і низній "елементи". Сам "елемент" (node) буде містити карту і інфо про (вказівники на) "сусідів" (елементи під- (next) і над- (prev)).
// Deck.h
#ifndef _DECK_H
#define _DECK_H
#include<stdbool.h>
#include"Card.h"
#include"Uint.h"
// Колода карт
typedef struct Deck {
struct Node *top;
struct Node *bottom;
} Deck;
typedef struct Node {
Card card;
struct Node *next;
struct Node *prev;
} Node;
Deck const EMPTY_DECK;
bool is_empty(Deck const *const);
int get_cards_count(Deck const *const);
void put_card_on_top(Deck *, Card const);
void put_card_under_bottom(Deck *, Card const);
void unite_decks(Deck *bottom, Deck *top);
Card take_card_from_top(Deck *);
Card take_card_from_bottom(Deck *);
Deck *take_deck_from(Deck *, int count);
void free_deck(Deck *);
Deck *new_deck(Uint const min_rank, Uint const max_rank);
Deck *new_deck_36(void);
Deck *new_deck_52(void);
void rotate_deck(Deck *, int offset);
void shuffle_deck(Deck *);
void print_deck(Deck const *const, Uint const columns);
void print_deck_with(Deck const *const, Uint const columns, int(*const printf)(char const *const, ...));
#endif // _DECK_H
Деякі функції далекі від оптимального рішеня. Якщо знайдете косяки, пишіть.
Функції new_deck* виділяють память під нову колоду, наповнюють її, і повертають вказівник на неї. Не загубіть його, бо потім цю память треба буде звільнити через free_deck (або free, залежить від користаня).
Функція shuffle_deck симулює перемішуваня карт в колоді так, як це робить людина.
// Deck.c
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include"Card.h"
#include"Deck.h"
#include"Rank.h"
#include"Uint.h"
Deck const EMPTY_DECK = { NULL, NULL };
bool is_empty(Deck const *const deck) { return !deck->top; }
int get_cards_count(Deck const *const deck) {
int count = 0;
if(deck) {
Node *node = deck->top;
while(node) {
++count;
node = node->next;
}
}
return count;
}
void put_card_on_top(Deck *deck, Card const card) {
if(!deck) return;
Node *const top = malloc(sizeof(Node));
*top = (Node){ card, deck->top, NULL };
if(deck->top) deck->top->prev = top;
else deck->bottom = top;
deck->top = top;
}
void put_card_under_bottom(Deck *deck, Card const card) {
if(!deck) return;
Node *const bottom = malloc(sizeof(Node));
*bottom = (Node){ card, NULL, deck->bottom };
if(deck->bottom) deck->bottom->next = bottom;
else deck->top = bottom;
deck->bottom = bottom;
}
void unite_decks(Deck *bottom, Deck *top) {
if(!bottom || !top) return;
if(top->bottom) top->bottom->next = bottom->top;
if(bottom->top) bottom->top->prev = top->bottom;
if(bottom->bottom) top->bottom = bottom->bottom;
else bottom->bottom = top->bottom;
if(top->top) bottom->top = top->top;
else top->top = bottom->top;
}
Card take_card_from_top(Deck *deck) {
if(!deck) return NO_CARD;
Node *const top = deck->top;
if(!top) return NO_CARD;
if(deck->top = top->next)
deck->top->prev = NULL;
else deck->bottom = NULL;
top->next = NULL;
Card const card = top->card;
top->card = NO_CARD;
free(top);
return card;
}
Card take_card_from_bottom(Deck *deck) {
rotate_deck(deck, -1);
return take_card_from_top(deck);
}
Deck *take_deck_from(Deck *deck, int count) {
Deck *const top = malloc(sizeof(Deck));
*top = EMPTY_DECK;
if(!count || !deck || !deck->top) return top;
if(count < 0) {
if(-count < get_cards_count(deck))
rotate_deck(deck, count);
count = -count;
}
Node *node = deck->top, *next = node->next;
while(--count > 0 && next)
next = (node = next)->next;
*top = (Deck){ deck->top, node };
if(deck->top = next)
next->prev = NULL;
else deck->bottom = NULL;
node->next = NULL;
return top;
}
void free_deck(Deck *deck) {
if(!deck) return;
Node *node = deck->top, *next;
while(node) {
next = node->next;
// *node = (Node){ (Card){ 0 }, NULL, NULL };
free(node);
node = next;
}
// *deck = EMPTY_DECK;
free(deck);
}
Deck *new_deck(Uint const min_rank, Uint const max_rank) {
Deck *const deck = malloc(sizeof(Deck));
*deck = EMPTY_DECK;
for(Uint r = min_rank; r <= max_rank; ++r)
for(Uint s = 0; s < 4; ++s)
put_card_on_top(deck, (Card){ s, r });
return deck;
}
Deck *new_deck_36(void) { return new_deck(6, Ace); }
Deck *new_deck_52(void) { return new_deck(2, Ace); }
void rotate_deck(Deck *deck, int offset) {
int count = get_cards_count(deck);
if(count <= 1) return;
offset = (offset % count + count) % count;
if(!offset) return;
Node *node = deck->bottom;
(node->next = deck->top)->prev = node;
do
node = node->next;
while(--offset > 0);
(deck->top = node->next)->prev = NULL;
(deck->bottom = node)->next = NULL;
}
void shuffle_deck(Deck *deck) {
int count = get_cards_count(deck);
if(count <= 1) return;
int few_times = rand() % 3 + 3;
Deck *base, *left, *top, *bottom;
do {
base = take_deck_from(NULL, 0);
left = deck;
do {
top = take_deck_from(left, rand() % ((count >> 3) + 1) + 1);
bottom = take_deck_from(left, -(rand() % ((count >> 3) + 1)));
unite_decks(bottom, top);
unite_decks(base, top);
free(top);
free(bottom);
} while(left->top);
*deck = *base;
free(base);
rotate_deck(deck, rand());
} while(--few_times);
}
void print_deck(Deck const *const deck, Uint const columns) {
print_deck_with(deck, columns, printf);
}
void print_deck_with(Deck const *const deck, Uint const columns, int(*const printf)(char const *const, ...)) {
printf("\r\n");
if(!deck) return;
Node *node = deck->top;
int i = 0;
while(node) {
printf(" ");
print_card_with(node->card, printf);
node = node->next;
if(columns <= ++i) {
printf("\r\n");
i = 0;
}
}
}