1

Тема: Елементи масиву. Колода карт.

Допоможіть, будь-ласка, програма здає по 6 карт чотирьом гравцям та підраховує
кількість послідовностей з двох карт одного ранга у кожного гравця.
У колоді карт має бути 52 карти. Здача карт має виконуватися випадковим чином.

2

Re: Елементи масиву. Колода карт.

RayDenz написав:

Допоможіть, будь-ласка, програма здає по 6 карт чотирьом гравцям та підраховує
кількість послідовностей з двох карт одного ранга у кожного гравця.
У колоді карт має бути 52 карти. Здача карт має виконуватися випадковим чином.

Яка програма? І в чому питання?

3

Re: Елементи масиву. Колода карт.

Програма яку треба написати мовою C, а питання, як зробити це завдання

4

Re: Елементи масиву. Колода карт.

RayDenz написав:

Програма яку треба написати на мові C, а питання, як зробити це завдання

Ви не зрозуміли суті завдання. Воно полягає не в тому, щоб зайти на форум, і просити, аби весь код написали за вас.
Суть цього завдання, як і навчання взагалі, в тому, аби ви самі спробували то зробити. Написали код, перевірили його, переписали, якшо шось не так робе, як треба, і зрештою отримали працюючий код.
А форум треба лише тоді, коли у вас вже є написаний код, або повністю, або частково, і у вас виникає якась проблема, котру ви не змогли вирішити навіть після багатьох спроб.

Подякували: leofun01, PeSePol2

5

Re: Елементи масиву. Колода карт.

Усе погано.
Ну, іноді можна поставити питання без коду - скажімо, про те, який алгоритм краще обрати, або якщо вміння є, а немає ідей. Але тут - читайте книжку.

Подякували: FakiNyan, leofun012

6

Re: Елементи масиву. Колода карт.

RayDenz написав:

програма здає по 6 карт чотирьом гравцям та підраховує кількість послідовностей з двох карт одного ранга у кожного гравця. У колоді карт має бути 52 карти. Здача карт має виконуватися випадковим чином.
... мовою C ... як зробити це завданя

Це прекрасна задача для тих, хто починає вивчати мову C. Дуже рекомендую написати програму самостійно і тільки після того відкривати прихований блок низче.

Створеня програми покроково

Нам будуть корисними наступні абстракції, відповідні типи даних, і зразу оцінимо скільки памяті займе 1 екземпляр :

  1. Масть ( ♥, ♦, ♣, ♠, ) - Suit - 2 біт

  2. Ранґ ( 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A, ) - Rank - 4 біт

  3. Карта ( 2♥, .. , A♠, ) - Card - 6 біт

  4. Колода { 2♥, .. , A♠, } - Deck - колекція карт (елементів: до 52)

  5. Гравець ( [0], [1], [2], [3], ) - Player - тримає в себе колекцію карт (елементів: до 6)

Екземпляри зразу 3-х типів даних займатимуть 1 байт. Для цього можна взяти unsigned char, але для мене це надто багато символів, тому обізвемо його Uint.

// Uint.h

#ifndef _UINT_H
#define _UINT_H

typedef unsigned char Uint;

#endif // _UINT_H
Масть

Тут нам достатньо мати 4 іменовані глобальні екземпляри. Можна би було створити для них struct, але така структура містила би тільки 1 поле, і ми би втратили можливість порівнювати (==) екземпляри без діставаня поля. Ще можна би було створити відповідний enum, але тоді 1 екземпляр був би 4 байти, нам так багато не треба, 1 байт достатньо.

// Suit.h

#ifndef _SUIT_H
#define _SUIT_H

#include"Uint.h"

Uint const Hearts  ; // ♥ ♡ / серця / cœurs
Uint const Diamonds; // ♦ ♢ / каро  / carreaux
Uint const Clubs   ; // ♣ ♧ / трефи / trèfles
Uint const Spades  ; // ♠ ♤ / піки  / piques

#endif // _SUIT_H
// Suit.c

#include"Uint.h"

Uint const Hearts   = 0; // ♥ ♡ / серця / cœurs
Uint const Diamonds = 1; // ♦ ♢ / каро  / carreaux
Uint const Clubs    = 2; // ♣ ♧ / трефи / trèfles
Uint const Spades   = 3; // ♠ ♤ / піки  / piques
Ранґ

Є ранґи [2-10], які буде зручно в коді писати звичайними цілими числати, і є іменовані ранґи, для яких можна поставити у відповідність більші числа. Для пошуку пар карт в наступних етапах, тут теж буде корисним оператор порівняня (==), тому без struct. І для мінімум памяті (1 байт / екземпляр) - без enum.

// Rank.h

#ifndef _RANK_H
#define _RANK_H

#include"Uint.h"

Uint const Jack ; // Валет  / Valet
Uint const Queen; // Дама   / Dame
Uint const King ; // Король / Roi
Uint const Ace  ; // Туз    / As

#endif // _RANK_H
// Rank.c

#include"Uint.h"

Uint const Jack  = 11; // Валет  / Valet
Uint const Queen = 12; // Дама   / Dame
Uint const King  = 13; // Король / Roi
Uint const Ace   = 14; // Туз    / As
Карта

Тут вже цікавіше. Карта містить інфо про масть і ранґ, тобто якщо робити структуру, то буде 2 поля (мінімум). Але сам struct доведеться порівнювати складною умовою, треба уникнути цього, якщо є така можливість (є). Можна взяти union, він дозволить обєднати кілька полів і порівняня карт зведеться до порівняня одного поля, при цьому поля "масть" і "ранґ" залишаться доступними.

// Card.h

#ifndef _CARD_H
#define _CARD_H

#include<stdbool.h>
#include"Uint.h"

// Карта
typedef union {
    struct Card {
        Uint suit : 2;
        Uint rank : 4;
    };
    Uint value : 6;
} Card;

Card const NO_CARD;

bool is_named(Card const);
bool is_numbered(Card const);
int compare(Card const, Card const);

void print_card(Card const);
void print_card_with(Card const, int (*const printf)(char const *const, ...));

#endif // _CARD_H

Для карти встановимо функції порівняня (compare) і друк в термінал (print_card).

// Card.c

#include<stdbool.h>
#include<stdio.h>
#include"Card.h"
#include"Rank.h"
#include"Uint.h"

Card const NO_CARD = { 0 };

bool is_named(Card const c) {
    /*//
    switch(c.rank) {
        case Jack:
        case Queen:
        case King:
        case Ace:
            return true;
        default:
            return false;
    }
    /*/
    return Jack <= c.rank && c.rank <= Ace;
    //*/
}
bool is_numbered(Card const c) {
    return 2 <= c.rank && c.rank <= 10;
}
int compare(Card const l, Card const r) {
    return l.value - r.value;
}

void print_card(Card const c) {
    print_card_with(c, printf);
}
void print_card_with(Card const c, int (*const printf)(char const *const, ...)) {
    Uint const r = c.rank;
    printf("%c%c%c",
        10 == r ? '1' : ' ',
        10 <= r ? ("0JQKAN"[r - 10]) : (r + '0'),
        c.suit + 3
    );
}
Колода

Ще цікавіше. Для початку визначимо, що будемо називати колодою. В моїх уявленях колодою можна назвати будь-яку 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;
        }
    }
}
Гравець

Гравець теж має колекцію карт, можна вважати, що в нього своя маленька колода. Таке представленя зручне тим, що ми зможемо деякі функції колоди застосувати до колекції, яку тримає гравець. Крім того, було би добре вказівникам (top, bottom) дати псевдоніми (left, right), щоб доступ до карт гравця був інтуїтивно більш зрозумілим. Для цього беремо union.

// Player.h

#ifndef _PLAYER_H
#define _PLAYER_H

#include"Deck.h"

// Гравець
typedef union {
    struct {
        Node *left;
        Node *right;
    } *cards;
    Deck *deck;
} Player;

int get_pairs_count(Player const);
void print_player_cards(Player const);

#endif // _PLAYER_H
// Player.c

#include"Deck.h"
#include"Player.h"

int get_pairs_count(Player const player) {
    if(!player.cards) return 0;
    int count = 0;
    Node *node = player.cards->left;
    while(node) {
        Node *next = node->next;
        while(next) {
            if(node->card.rank == next->card.rank)
                ++count;
            next = next->next;
        }
        node = node->next;
    }
    return count;
}
void print_player_cards(Player const player) {
    print_deck(player.deck, 15);
}
Точка входу

Залишилось майже нічого. Збираємо все до купи:

  • Створити колоду

  • Створити 4-х гравців

  • Перемішати колоду

  • Кожний гравець бере собі карти

  • Показує нам карти

  • І пише скільки там пар

  • Якщо в колоді карт достатньо, то можна повторити роздачу

// main.c

#include<conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include"Deck.h"
#include"Player.h"

int main() {
    srand(time(0));
    int const cards_per_player = 6;
    int const players_count = 4;
    Deck *deck = new_deck_52();
    Player *p = malloc(sizeof(Player) * players_count);
    print_deck(deck, 4);
    shuffle_deck(deck);
    while(get_cards_count(deck) >= cards_per_player * players_count) {
        // print_deck(deck, 4);
        printf("\r\n");
        for(int i = 0; i < players_count; ++i) {
            Player player = p[i];
            player.deck = take_deck_from(deck, cards_per_player);
            print_player_cards(player);
            int pairs_count = get_pairs_count(player);
            printf(
                pairs_count > 0
                    ? " - %i pairs found.\r\n"
                    : " - No pairs.\r\n",
                pairs_count
            );
            free_deck(player.deck);
        }
        _getch();
    }
    free(p);
    free_deck(deck);
    return 0;
}

Якщо все це акуратно перенести в проект і зкомпілювати, то вийде щось таке:

leo@mint-pc:~$ ./cards-game.bin

  A♠  A♣  A♦  A♥
  K♠  K♣  K♦  K♥
  Q♠  Q♣  Q♦  Q♥
  J♠  J♣  J♦  J♥
 10♠ 10♣ 10♦ 10♥
  9♠  9♣  9♦  9♥
  8♠  8♣  8♦  8♥
  7♠  7♣  7♦  7♥
  6♠  6♣  6♦  6♥
  5♠  5♣  5♦  5♥
  4♠  4♣  4♦  4♥
  3♠  3♣  3♦  3♥
  2♠  2♣  2♦  2♥


  Q♣  Q♦  5♥ 10♣ 10♦ 10♥ - 4 pairs found.

  5♦  2♥  Q♥  9♥  7♣  7♦ - 1 pairs found.

  J♣  2♠ 10♠  3♠  2♣  2♦ - 3 pairs found.

  K♥  3♣  3♦  7♠  6♥  K♦ - 2 pairs found.


  3♥  A♥  A♣  A♦  K♣  4♥ - 3 pairs found.

  6♣  8♠  8♣  8♦  8♥  5♠ - 6 pairs found.

  K♠  J♦  4♣  5♣  9♦  6♠ - No pairs.

  J♥  J♠  4♦  4♠  7♥  6♦ - 2 pairs found.

Бачите, зовсім не складно.

Подякували: zxzpogoncuk1