prg-lang-2 / final / BlackJack / game.c
game.c
Raw
#include <stdio.h>
#include <stdlib.h>

#include "game.h"
#include "player.h"
#include "util.h"

void initPlayers(Game *game, const int initChips, const int playerCount, const int botCount)
{
    // プレイヤー数の設定(人間プレイヤー + ボット)
    game->playerCount = playerCount + botCount;
    initDealer(&game->dealer);
    game->players = (Player *)malloc(game->playerCount * sizeof(Player));
    int *indices = (int *)malloc(game->playerCount * sizeof(int));
    for (int i = 0; i < game->playerCount; i++)
    {
        indices[i] = i;
    }
    // プレイヤーの順番をランダムにシャッフル
    for (int i = game->playerCount - 1; i > 0; i--)
    {
        int j = rand() % (i + 1);
        int temp = indices[i];
        indices[i] = indices[j];
        indices[j] = temp;
    }
    for (int i = 0; i < game->playerCount; i++)
    {
        if (indices[i] < playerCount)
        {
            char defaultName[MAX_NAME_LEN];
            snprintf(defaultName, MAX_NAME_LEN, "User%.2d", (indices[i] + 1) % 100);
            initPlayer(&game->players[i], initChips, defaultName);
        }
        else
        {
            initBot(&game->players[i], initChips);
        }
    }
    free(indices);
}

void initGame(Game *game)
{
    initDeck(game->deck);
    shuffleDeck(game->deck);
    game->deckIndex = 0;
    initDealer(&game->dealer);
    for (int i = 0; i < game->playerCount; i++)
    {
        game->players[i].surrendered = 0;
        game->players[i].bet = 0;
        game->players[i].handSize = 0;
        game->players[i].handValue = 0;
        game->players[i].splitBet = 0;
        game->players[i].splitHandSize = 0;
        game->players[i].splitHandValue = 0;
        game->players[i].hasSplit = 0;
    }
}

Card drawCard(Game *game)
{
    return game->deck[game->deckIndex++];
}

void displayTitle()
{
    int startX = (getTerminalWidth() - 52) / 2;
    if (startX < 1)
        startX = 1;
    printf("\033[2J\033[H");
    printf("\033[1m\033[47m");
    printf("\033[2;%dH", startX);
    printf("\033[31m  ____  _            _     ");
    printf("\033[30m      _            _     ");
    printf("\033[3;%dH", startX);
    printf("\033[31m |  _ \\| |          | |    ");
    printf("\033[30m     | |          | |    ");
    printf("\033[4;%dH", startX);
    printf("\033[31m | |_) | | __ _  ___| | __ ");
    printf("\033[30m     | | __ _  ___| | __ ");
    printf("\033[5;%dH", startX);
    printf("\033[31m |  _ <| |/ _` |/ __| |/ / ");
    printf("\033[30m _   | |/ _` |/ __| |/ / ");
    printf("\033[6;%dH", startX);
    printf("\033[31m | |_) | | (_| | (__|   <  ");
    printf("\033[30m| |__| | (_| | (__|   <  ");
    printf("\033[7;%dH", startX);
    printf("\033[31m |____/|_|\\__,_|\\___|_|\\_\\ ");
    printf("\033[30m \\____/ \\__,_|\\___|_|\\_\\ ");
    printf("\033[8;%dH", startX);
    printf("\033[31m                           ");
    printf("\033[30m                         ");
    printf("\033[39m\033[49m\033[0m");
    printf("\033[10;1H");
    fflush(stdout);
}

void _displayBoard(const Game *game, const int init, const int status, const int playerIndex, const char playerAction, const int splitAction)
{
    // プレイヤー人数に応じて表示幅を計算
    int contentWidth = 20 * game->playerCount;
    // ディーラーの手札表示(初期状態では2枚目以降は裏向き)
    int startX = (getTerminalWidth() - contentWidth) / 2;
    if (startX < 1)
        startX = 1;
    printf("\033[2J\033[H");
    printf("\033[1;%dH", startX + (contentWidth - 20) / 2);
    printf("%s", game->dealer.name);
    printf("\033[2;%dH", startX + (contentWidth - 20) / 2);
    for (int i = 0; i < game->dealer.handSize; i++)
    {
        if (init == 1 && i != 0)
        {
            printCardBack();
        }
        else
        {
            printCard(game->dealer.hand[i]);
        }
        printf(" ");
    }
    // プレイヤーの表示
    for (int i = 0; i < game->playerCount; i++)
    {
        int x = 4;
        int y = startX + (i * 20);
        printf("\033[%d;%dH", x, y);
        if (game->players[i].bet > 0)
        {
            printf("[%ld]", game->players[i].bet);
        }
        else
        {
            printf("[]");
        }
        if (game->players[i].hasSplit)
        {
            printf("\033[%d;%dH", x, y + 9);
            if (game->players[i].splitBet > 0)
            {
                printf("[%ld]", game->players[i].splitBet);
            }
            else
            {
                printf("[]");
            }
        }
        for (int j = 0; j < game->players[i].handSize; j++)
        {
            if (game->players[i].hasSplit && j % 2 == 0)
            {
                printf("\033[%d;%dH", x + 1 + (j / 2), y);
            }
            else if (!game->players[i].hasSplit && j % 4 == 0)
            {
                printf("\033[%d;%dH", x + 1 + (j / 4), y);
            }
            else
            {
                printf(" ");
            }
            printCard(game->players[i].hand[j]);
        }
        if (game->players[i].hasSplit)
        {
            for (int j = 0; j < game->players[i].splitHandSize; j++)
            {
                if (j % 2 == 0)
                {
                    printf("\033[%d;%dH", x + 1 + (j / 2), y + 9);
                }
                else
                {
                    printf(" ");
                }
                printCard(game->players[i].splitHand[j]);
            }
        }
        printf("\033[%d;%dH", x + 3, y);
        if (game->players[i].isBot)
        {
            printf("%s (%ld)", game->players[i].name, game->players[i].chips);
        }
        else
        {
            printf("\033[4m%s\033[0m (%ld)", game->players[i].name, game->players[i].chips);
        }
        if (status)
        {
            for (int t = 0; t <= game->players[i].hasSplit; t++)
            {
                printf("\033[%d;%dH", x + 4, y + 9 * t);
                printLastGame(&game->players[i], t);
            }
        }
        else
        {
            if (playerIndex == i)
            {
                printf("\033[%d;%dH", x + 4, y + splitAction * 9);
                if (playerAction == 'H' || playerAction == 'h')
                {
                    printf("Hit");
                }
                else if (playerAction == 'D' || playerAction == 'd')
                {
                    printf("Double");
                }
                else if (playerAction == 'R' || playerAction == 'r')
                {
                    printf("Surr");
                }
                else
                {
                    printf("Stand");
                }
            }
        }
    }
    printf("\033[10;1H");
    fflush(stdout);
}

void displayBoard(const Game *game, const int init)
{
    _displayBoard(game, init, 0, -1, 'n', 0);
}

void displayStatus(const Game *game)
{
    _displayBoard(game, 0, 1, -1, 'n', 0);
}

void displayAction(const Game *game, const int playerIndex, char action, int split)
{
    _displayBoard(game, 1, 0, playerIndex, action, split);
}

void _processBet(Game *game, Player *player, GameStatus *lastGame, long *bet, const int handValue, const int handSize, const double bjRate)
{
    // サレンダーの場合の処理
    if (player->surrendered)
    {
        *lastGame = GameSurrender;
    }
    // ディーラーがバーストした場合の処理
    else if (game->dealer.handValue > 21)
    {
        if (handValue <= 21)
        {
            // ブラックジャック(21で2枚)の場合は高配当
            if (handValue == 21 && handSize == 2)
            {
                *lastGame = GameBlackJack;
                player->chips += (long)(*bet * (bjRate + 1.0));
                printf("SYSTEM> %s won. %ld chips back.\n", player->name, (long)(*bet * 2.5));
            }
            else
            {
                *lastGame = GameWin;
                player->chips += *bet * 2;
                printf("SYSTEM> %s won. %ld chips back.\n", player->name, *bet * 2);
            }
        }
        else
        {
            *lastGame = GameLose;
            printf("SYSTEM> %s lose.\n", player->name);
        }
    }
    // ディーラーがバーストしなかった場合の処理
    else
    {
        if (
            handValue > 21 ||
            game->dealer.handValue > handValue)
        {
            *lastGame = GameLose;
            printf("SYSTEM> %s lose.\n", player->name);
        }
        else if (game->dealer.handValue < handValue)
        {
            if (handValue == 21 && handSize == 2)
            {
                *lastGame = GameBlackJack;
                player->chips += (long)(*bet * 2.5);
                printf("SYSTEM> %s won. %ld chips back.\n", player->name, (long)(*bet * 2.5));
            }
            else
            {
                *lastGame = GameWin;
                player->chips += *bet * 2;
                printf("SYSTEM> %s won. %ld chips back.\n", player->name, *bet * 2);
            }
        }
        else
        {
            *lastGame = GameDraw;
            player->chips += *bet;
            printf("SYSTEM> %s is draw. %ld chips back.\n", player->name, *bet);
        }
    }
    *bet = 0;
}

void processBet(Game *game, Player *player, const double bjRate)
{
    _processBet(game, player, &player->lastGame, &player->bet, player->handValue, player->handSize, bjRate);
}

void processSplitBet(Game *game, Player *player, const double bjRate)
{
    _processBet(game, player, &player->splitLastGame, &player->splitBet, player->splitHandValue, player->splitHandSize, bjRate);
}

int processAction(Game *game, const int playerIndex, const int split, const char action)
{
    Player *player = &game->players[playerIndex];
    // Hit
    if (action == 'H' || action == 'h')
    {
        addCardToHand(player, drawCard(game));
        displayAction(game, playerIndex, action, split);
        if (player->handValue > 21)
        {
            printf("SYSTEM> %s busted!\n", player->name);
            return 1;
        }
    }
    // Split
    else if ((action == 'P' || action == 'p') && canSplit(player))
    {
        processSplit(player);
        displayAction(game, playerIndex, action, 0);
        csleep(0.5);
        addCardToHand(player, drawCard(game));
        displayAction(game, playerIndex, action, 0);
        csleep(0.5);
        addCardToSplitHand(player, drawCard(game));
        displayAction(game, playerIndex, action, 0);
    }
    // Double Down
    else if ((action == 'D' || action == 'd') && canDoubleDown(player, split))
    {
        if (split)
        {
            placeBet(player, player->splitBet, split);
            addCardToSplitHand(player, drawCard(game));
        }
        else
        {
            placeBet(player, player->bet, split);
            addCardToHand(player, drawCard(game));
        }
        displayAction(game, playerIndex, action, split);
        if (player->handValue > 21)
        {
            printf("SYSTEM> %s busted!\n", player->name);
        }
        return 1;
    }
    // Surrender
    else if ((action == 'R' || action == 'r') && canSurrender(player))
    {
        processSurrender(player);
        displayAction(game, playerIndex, action, split);
        printf("SYSTEM> %s surrendered.\n", player->name);
        csleep(1);
        return 1;
    }
    // Stand
    else if (action == 'S' || action == 's')
    {
        displayAction(game, playerIndex, action, split);
        csleep(1);
        return 1;
    }
    else
    {
        printf("SYSTEM> Invalid call\n");
        return 0;
    }
    return 0;
}