Blackjack example

Run Settings
LanguageC
Language Version
Run Command
/* Вставляем код сюда https://www.tutorialspoint.com/compile_c_online.php Сверху справа жмем Project -> Compile options В поле Compilation command вставляем: gcc -std=c99 -o main *.c -lm ^ ^ standard: c99 link math library Жмем Compile, Execute Играем НА GLOT.IO НОРМАЛЬНО ПОИГРАТЬ НЕ ПОЛУЧИТСЯ т.к. программе нужен ввод в реальном времени, а не заранее подготовленный */ #include <stdio.h> // printf, fgets #include <math.h> // floor, rand #include <stdlib.h> // srand (for rand init) #include <time.h> // time (for srand based on time) #define DECKS 8 /* Сколько колод добавить в игру */ #define DECK_SIZE 52 /* Размер одной колоды */ #define CARDS_TOTAL (DECKS * DECK_SIZE) /* Сколько всего карт будет в игре */ #define SUIT_SIZE 13 /* Сколько в колоде карт одной масти */ #define BUFFER_SIZE 255 /* Размер буфера для ввода из консоли */ /* Игрок */ typedef struct { int id; /* ID игрока, используется для отображения карт, принадлежащих ему */ char* name; /* Имя, будет отображаться в консоли */ int sum; /* Сумма карт */ int hits; /* Сколько раз игрок забрал карту */ int valued_aces; /* Сколько у игрока полноценных тузов */ } Player; /* Карта */ typedef struct { int rank; /* Ранг карты */ int suit; /* Масть карты */ int holder; /* Кто держит карту (-1, если никто) */ } Card; /* Колода карт */ typedef struct { int c; /* Текущая карта. Увеличивается после каждого hit() */ Card card[CARDS_TOTAL]; /* Все карты со всех колод. * Изначально массив пустой. * Заполняется с помощью deck_fill() */ } Deck; /* Возвращает случайное вещественное число от 0.0 до 1.0 */ double rand_double() { /* Статическая переменная сохраняет свое значение даже после выхода их функции */ static int initialized = 0; /* Используем это для инициализации рандома только при первом вызове функции */ if (!initialized) { srand(time(NULL)); initialized = 1; } return (double) rand() / RAND_MAX; } /* Возвращает случайное целое число в заданном интервале */ int rand_int(int min, int max) { return floor(rand_double() * (max - min + 1)) + min; } /* Заполняет колоду картами */ void deck_fill(Deck* deck) { for (int d = 0; d < DECKS; d++) { // 0, 1, ... for (int c = 0; c < DECK_SIZE; c++) { int rank = (c % SUIT_SIZE) + 1; // 1,2,3,4,5,6,7,8,9,10,11,12,13; 1,2,3...; ...; ...; int suit = c / SUIT_SIZE; // 0, 1, 2, 3 int holder = -1; Card card = { rank, suit, holder }; deck->card[(d * DECK_SIZE) + c] = card; } } } /* Перемешивает все карты в колоде */ void deck_shuffle(Deck* deck) { for (int i = 0; i < CARDS_TOTAL; i++) { /* Генерируем новую случайную позицию в колоде, от i до cards-1 */ int j = rand_int(i, CARDS_TOTAL-1); /* Меняем местами эти карты по позициям */ Card temp_card = deck->card[i]; deck->card[i] = deck->card[j]; deck->card[j] = temp_card; } } /* Возвращает фейс карты, если есть. Иначе возвращает пробел. */ const char face_char(int rank) { switch (rank) { case 1: return 'A'; case 11: return 'J'; case 12: return 'Q'; case 13: return 'K'; default: return ' '; } } /* Возвращает ссылку на строку с необходимой мастью * ♥ = 0, ♦ = 1, ♣ = 2, ♠ = 3 * В windows и *nix кодировка символов разная * В windows стандартная консолиь не отображает эти символы, * нужно юзать powershell * В *nix каждый символ занимает 2 байта, юзаем строки для хранения */ const char* suit_char(int suit) { static const char* chars[] = { #ifdef _WIN32 "\3", "\4", "\5", "\6" #else "\u2665", "\u2666", "\u2663", "\u2660" #endif }; if (suit < 0 ||suit > 13) return " "; return chars[suit]; } /* Распечатывает карту */ void print_card(Card card) { int rank = card.rank; int suit = card.suit; int face = face_char(rank); /* Важно поставить пробел после символа масти, т.к. если он занимает 2 байта, * то следующий за ним символ может не распечататься */ printf("%s ", suit_char(suit)); if (face != ' ') { printf("%c", face); } else { printf("%d", rank); } } /* Распечатывает все карты в колоде */ void print_deck(Player* player, int players, Deck* deck) { for (int c = 0; c < CARDS_TOTAL; c++) { print_card(deck->card[c]); int holder = deck->card[c].holder; if (holder) { for (int p = 0; p < players; p++) { if (holder != player[p].id) continue; printf(" (%s)", player[p].name); } } printf("\n"); } } /* Создает и возвращает структуру игрока, заполняя её значениями по умолчанию */ Player player_create(char* name) { /* Статическая переменная сохранит свое значение после выхода из функции */ static int id = 0; Player player = { id, name, 0, 0, 0 }; id++; return player; } /* Распечатывает (с конца) список игроков и карты на их руках */ void print_players(Player* player, int players, int current_player, Deck* deck) { /* Заранее находим максимальную сумму среди игроков, не большую 21*/ int winner_sum = -1; for (int p = players-1; p >= 0; p--) { int sum = player[p].sum; if (sum > winner_sum && sum <= 21) { winner_sum = sum; } } for (int p = players-1; p >= 0; p--) { /* Карты диллера скрыты пока очередь не дойдет до него */ int dealer_cards_hidden = player[p].id == 0 && current_player != 0; int is_current_player = player[p].id == current_player; if (is_current_player) { printf("> "); } printf("%s got ", player[p].name); if (dealer_cards_hidden) { printf("? ("); } else { printf("%d (", player[p].sum); } int hits = 0; for (int c = 0; c < CARDS_TOTAL; c++) { if (deck->card[c].holder != player[p].id) continue; hits++; if (dealer_cards_hidden && hits == 2) { printf("??"); } else { print_card(deck->card[c]); } if (hits < player[p].hits) { printf(", "); } if (hits == player[p].hits) break; } if (current_player == 0 && player[p].sum == winner_sum) { printf(") - Winner!\n"); } else { printf(")\n"); } } } /* Очищает экран путем отправки команды в командную строку. * Сама команда зависит от операционной системы. */ void clear_screen() { #ifdef _WIN32 system("cls"); #else system("clear"); #endif } /* Игрок выбрал hit */ void hit(Player* player, Deck* deck) { /* Инкрементируем итератор текущей карты */ int c = ++(deck->c); /* Увеличиваем кол-во карт на руках игрока */ player->hits++; /* Записываем владельца текущей карты */ deck->card[c].holder = player->id; int sum = player->sum; int rank = deck->card[c].rank; /* По началу пусть значение будет равно рангу карты. */ int value = rank; /* Если это туз, то его значение зависит от общей суммы значений. Если сумма еще не превысила 21, то тузы стоят 11. Иначе они стоят 1. Мы храним кол-во полноценных тузов, чтобы их обеценить при переполнении */ if (rank == 1 && sum + value <= 21) { value = 11; player->valued_aces++; } /* У карт с рангом >= 10 значение 10 */ if (rank >= 10) value = 10; sum += value; /* Если сумма превысила 21, и у игрока есть тузы с полным значением, то обесцениваем их, пока сумма больше 21 и есть необесцененные тузы */ while (sum > 21 && player->valued_aces > 0) { player->valued_aces--; sum -= 10; } player->sum = sum; } /* Имитация игры дилера */ int imitate_dealer(Player* player, int players, Deck* deck) { /* Находим минимальную сумму среди игроков */ int min_sum = 20; int second_min_sum = min_sum; for (int i = players-1; i >= 1; i--) { if (player[i].sum < min_sum) { second_min_sum = min_sum; min_sum = player[i].sum; } } /* Диллер бутет жать hit до тех пор, пока: * его сумма меньше второй наименьшей суммы * его сумма близка к 21 и все еще меньше наименьшей суммы */ int near_21 = 21 - player[0].sum < 2; int less_than_min = player[0].sum < min_sum; int less_than_second_min = player[0].sum < second_min_sum; if (less_than_second_min || (near_21 && less_than_min)) { hit(&player[0], deck); return 1; } return 0; } /* Ход игрока */ int player_control(Player* player, Deck* deck) { if (player->sum == 21) return 0; /* Здесь будет храниться строка которую ввел пользователь (hit|stay|quit) */ char buffer[BUFFER_SIZE]; printf("\nWhat would you do? (hit|stay|quit): "); /* Получаем введенную пользователем строку */ fgets(buffer, BUFFER_SIZE, stdin); /* Простое управление путем проверки первого введенного символа */ switch(buffer[0]) { /* Hit - еще карту */ case 'h': case 'H': { hit(player, deck); /* Возвращаем 0 если игрок достиг или превысил суммы в 21 очко */ return player->sum < 21; } /* Stay - след. игрок */ case 's': case 'S': { return 0; } /* Quit */ case 'q': case 'Q': { exit(0); } /* Пользователь ввел что-то другое (или ничего) */ default: { return 1; } } } /* Один раунд */ void round_start(Deck* deck, Player* player, int players) { /* В начале раунда каждому игроку дается по 2 карты */ for (int p = players - 1; p >= 0; p--) { hit(&player[p], deck); hit(&player[p], deck); } for (int p = players - 1; p >= 0; ) { int is_dealer = player[p].id == 0; do { clear_screen(); print_players(player, players, p, deck); /* Определяем какую функцию запускать (дать управление игроку, или имитировать дилера) * с помощью тернарного оператора прямо в шапке цикла. * Используем возвращаемое значение функции, чтобы определить продолжать ли цикл */ } while(is_dealer ? imitate_dealer(player, players, deck) : player_control(&player[p], deck)); p--; } } int main() { /* Создаем, заполняем и перемешиваем колоду */ Deck deck; deck_fill(&deck); deck_shuffle(&deck); /* Создаем массив игроков. Дилер всегда идет первым, на этом завязана логика игры. */ Player player[] = { player_create("Dealer"), player_create("You") }; /* Вычисляем кол-во игроков (размер всего массива в байтах / размер его элемента в байтах) */ int players = sizeof(player) / sizeof(player[0]); round_start(&deck, player, players); return 0; }
Editor Settings
Theme
Key bindings
Full width
Lines