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

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

#define BUFFER_SIZE 256

char *ENTER_GREETINGS[3] = {"Hi", "Hello", "Hey"};
char *LEAVE_GREETINGS[3] = {"Damn it", "Good bye", "Bye"};

int main()
{
    // 乱数の初期化
    srand((unsigned int)time(NULL));
    displayTitle();
    if (getTerminalHeight() < 10)
    {
        fprintf(stderr, "Error: Terminal height is too small. Please adjust the terminal height.\n");
        return 1;
    }
    Config *config = loadConfig("config.txt");
    if (config == NULL)
    {
        fprintf(stderr, "Error: Failed to load config file 'config.txt'\n");
        return 1;
    }
    int playerCount, botCount;
    long initChips, minBets, maxBets;
    double bjRate;
    int botDoubleDownMin, botDoubleDownMax, botDoubleDownRate;
    int botHitSoftRate, botHitMiddleRate, botHitHardRate;
    if (
        getConfigValueInt(config, "player_count", &playerCount) ||
        getConfigValueInt(config, "bot_count", &botCount) ||
        getConfigValueLong(config, "init_chips", &initChips) ||
        getConfigValueLong(config, "min_bets", &minBets) ||
        getConfigValueLong(config, "max_bets", &maxBets) ||
        getConfigValueDouble(config, "bj_rate", &bjRate) ||
        getConfigValueInt(config, "bot_double_down_min", &botDoubleDownMin) ||
        getConfigValueInt(config, "bot_double_down_max", &botDoubleDownMax) ||
        getConfigValueInt(config, "bot_double_down_rate", &botDoubleDownRate) ||
        getConfigValueInt(config, "bot_hit_soft_rate", &botHitSoftRate) ||
        getConfigValueInt(config, "bot_hit_middle_rate", &botHitMiddleRate) ||
        getConfigValueInt(config, "bot_hit_hard_rate", &botHitHardRate))
    {
        fprintf(stderr, "Error: Invalid value in config file\n");
        return 1;
    }
#ifdef _WIN32
    if (playerCount < 0 || botCount < 0 || (playerCount + botCount) > 4 || (playerCount + botCount) < 1)
    {
        fprintf(stderr, "Error: Player count must be 0-4 and total players must not exceed 4\n");
        return 1;
    }
#else
    const int maxPlayerCount = getTerminalWidth() / 20;
    if (playerCount < 0 || botCount < 0 || (playerCount + botCount) > maxPlayerCount || (playerCount + botCount) < 1)
    {
        fprintf(stderr, "Error: Player count must be 0-%d and total players must not exceed %d. Please adjust the terminal width.\n", maxPlayerCount, maxPlayerCount);
        return 1;
    }
#endif
    if (initChips <= 0 || minBets <= 0 || maxBets <= 0 || minBets > maxBets || initChips < minBets || initChips > MAX_CHIPS || maxBets > MAX_CHIPS)
    {
        fprintf(stderr, "Error: The chip settings are invalid\n");
        return 1;
    }
    if (bjRate < 1.0)
    {
        fprintf(stderr, "Error: Black Jack rate is invalid\n");
    }
    if (botDoubleDownMin < 0 || botDoubleDownMax < 0 || botDoubleDownMin > botDoubleDownMax || botDoubleDownMax > 13 || botDoubleDownRate < 0 || botDoubleDownRate > 100 ||
        botHitSoftRate < 0 || botHitSoftRate > 100 || botHitMiddleRate < 0 || botHitMiddleRate > 100 || botHitHardRate < 0 || botHitHardRate > 100)
    {
        fprintf(stderr, "Error: Bot settings are invalid\n");
        return 1;
    }
    Game game;
    initPlayers(&game, initChips, playerCount, botCount);
    // プレイヤー名の設定
    for (int i = 0; i < game.playerCount; i++)
    {
        // プレイヤーの場合は名前入力を受け付ける
        if (!game.players[i].isBot)
        {
            for (;;)
            {
                printf("Enter name for Player %d (or press Enter to keep %s): ", i + 1, game.players[i].name);
                char buffer[BUFFER_SIZE];
                if (fgets(buffer, BUFFER_SIZE, stdin) == NULL)
                {
                    printf("SYSTEM> Invalid input!\n");
                    continue;
                }
                if (buffer[0] == '\n')
                {
                    break;
                }
                buffer[strcspn(buffer, "\n")] = 0;
                size_t len = strlen(buffer);
                if (len == 0)
                {
                    printf("SYSTEM> Name cannot be empty!\n");
                    continue;
                }
                if (len >= MAX_NAME_LEN)
                {
                    printf("SYSTEM> Name is too long! Maximum %d characters.\n", MAX_NAME_LEN - 1);
                    continue;
                }
                strncpy(game.players[i].name, buffer, MAX_NAME_LEN - 1);
                game.players[i].name[MAX_NAME_LEN - 1] = '\0';
                break;
            }
        }
        else
        {
            printf("%s> %s\n", game.players[i].name, ENTER_GREETINGS[rand() % 3]);
            csleep(0.5);
        }
    }
    // ゲームのメインループ
    for (;;)
    {
        initGame(&game);
        // ベット額の設定
        for (int i = 0; i < game.playerCount; i++)
        {
            if (!game.players[i].isBot)
            {
                printf("SYSTEM> %ld Minimum, %ld Maximum\n", minBets, maxBets);
                printf("SYSTEM> %s have %ld chips.\n", game.players[i].name, game.players[i].chips);
                printf("Dealer> Place your bet.\n");
                long bet;
                for (;;)
                {
                    printf("%s> I bet ", game.players[i].name);
                    char buffer[BUFFER_SIZE];
                    if (fgets(buffer, BUFFER_SIZE, stdin) == NULL)
                    {
                        continue;
                    }
                    char *tole;
                    bet = strtol(buffer, &tole, 10);
                    while (*tole == ' ' || *tole == '\n' || *tole == '\r')
                        tole++;
                    if (*tole != '\0')
                    {
                        printf("SYSTEM> Invalid chips!\n");
                        continue;
                    }
                    if (bet < minBets || bet > maxBets)
                    {
                        printf("SYSTEM> Invalid chips!\n");
                        continue;
                    }
                    if (!placeBet(&game.players[i], bet, 0))
                    {
                        printf("SYSTEM> %s don't have enough chips!\n", game.players[i].name);
                        continue;
                    }
                    break;
                }
                displayBoard(&game, 1);
                printf("SYSTEM> %s bet %ld chips.\n", game.players[i].name, bet);
            }
            else
            {
                // Botのベット額を計算(100単位でランダムに設定)
                long botBet = (rand() % ((min(game.players[i].chips, maxBets) - minBets) / 100 + 1)) * 100 + minBets;
                placeBet(&game.players[i], botBet, 0);
                displayBoard(&game, 1);
                printf("SYSTEM> %s bet %ld chips.\n", game.players[i].name, botBet);
            }
            csleep(0.5);
        }
        printf("Dealer> No more bets.\n");
        csleep(1);
        // カードを配る
        for (int i = 0; i < 2; i++)
        {
            for (int j = 0; j < game.playerCount; j++)
            {
                addCardToHand(&game.players[j], drawCard(&game));
                displayBoard(&game, 1);
                csleep(0.5);
            }
            addCardToHand(&game.dealer, drawCard(&game));
            displayBoard(&game, 1);
            csleep(1);
        }
        // プレイヤーの行動
        for (int i = 0; i < game.playerCount; i++)
        {
            if (!game.players[i].isBot)
            {
                for (int split = 0; split < 2; split++)
                {
                    for (;;)
                    {
                        if (canSurrender(&game.players[i]))
                        {
                            if (canSplit(&game.players[i]))
                            {
                                printf("Dealer> (H)it, (D)ouble, S(p)lit, Su(r)render or (S)tand?\n%s> ", game.players[i].name);
                            }
                            else if (canDoubleDown(&game.players[i], split))
                            {
                                printf("Dealer> (H)it, (D)ouble, Su(r)render or (S)tand?\n%s> ", game.players[i].name);
                            }
                            else
                            {
                                printf("Dealer> (H)it, Su(r)render or (S)tand?\n%s> ", game.players[i].name);
                            }
                        }
                        else if (canDoubleDown(&game.players[i], split))
                        {
                            printf("Dealer> (H)it, (D)ouble or (S)tand?\n%s> ", game.players[i].name);
                        }
                        else
                        {
                            printf("Dealer> (H)it or (S)tand?\n%s> ", game.players[i].name);
                        }
                        char buffer[BUFFER_SIZE];
                        if (fgets(buffer, BUFFER_SIZE, stdin) == NULL)
                        {
                            continue;
                        }
                        if (processAction(&game, i, split, buffer[0]))
                        {
                            break;
                        }
                    }
                    if (!game.players[i].hasSplit)
                    {
                        break;
                    }
                }
            }
            else
            {
                for (;;)
                {
                    char action = 's';
                    // Botの行動決定ロジック
                    // ディーラーが強いカード(10以上またはA)を持っていて、自分の手札が16の場合はサレンダー
                    if (game.players[i].handValue == 16 && canSurrender(&game.players[i]))
                    {
                        if (game.dealer.hand[0].rank >= 10 || game.dealer.hand[0].rank == 1)
                        {
                            action = 'r';
                        }
                    }
                    if (action == 's' && canDoubleDown(&game.players[i], 0))
                    {
                        int decision = rand() % 100;
                        if (game.players[i].handValue <= botDoubleDownMax &&
                            game.players[i].handValue >= botDoubleDownMin &&
                            decision < botDoubleDownRate)
                        {
                            action = 'd';
                        }
                    }
                    if (action == 's')
                    {
                        int decision = rand() % 100;
                        if (game.players[i].handValue <= 12 && decision < botHitSoftRate)
                        {
                            action = 'h';
                        }
                        else if (game.players[i].handValue <= 16 && decision < botHitMiddleRate)
                        {
                            action = 'h';
                        }
                        else if (game.players[i].handValue <= 18 && decision < botHitHardRate)
                        {
                            action = 'h';
                        }
                    }
                    if (processAction(&game, i, 0, action))
                    {
                        break;
                    }
                    csleep(1);
                }
                csleep(1);
            }
        }
        displayBoard(&game, 0);
        csleep(1);
        // ディーラーの行動(17以上になるまでヒット)
        while (game.dealer.handValue <= 16)
        {
            addCardToHand(&game.dealer, drawCard(&game));
            displayBoard(&game, 0);
            csleep(1);
            if (game.dealer.handValue > 21)
            {
                printf("SYSTEM> %s busted!\n", game.dealer.name);
                break;
            }
        }
        // 勝敗判定
        for (int i = 0; i < game.playerCount; i++)
        {
            processBet(&game, &game.players[i], bjRate);
            if (game.players[i].hasSplit)
            {
                processSplitBet(&game, &game.players[i], bjRate);
            }
        }
        displayStatus(&game);
        csleep(1);
        // プレイヤーのチップがなくなった場合はゲーム終了
        for (int i = 0; i < game.playerCount; i++)
        {
            if (game.players[i].chips < minBets)
            {
                if (!game.players[i].isBot)
                {
                    printf("SYSTEM> No chips for %s.\n", game.players[i].name);
                    printf("Dealer> See you/~\n");
                    return 0;
                }
                else
                {
                    printf("SYSTEM> No chips for %s.\n", game.players[i].name);
                    printf("%s> %s\n", game.players[i].name, LEAVE_GREETINGS[rand() % 3]);
                    csleep(1);
                    initBot(&game.players[i], initChips);
                    printf("%s> %s\n", game.players[i].name, ENTER_GREETINGS[rand() % 3]);
                }
            }
            else if (game.players[i].chips >= MAX_CHIPS)
            {
                if (!game.players[i].isBot)
                {
                    printf("SYSTEM> %s has reached the maximum chips.\n", game.players[i].name);
                    printf("Dealer> Now please leave.\n");
                    return 0;
                }
                else
                {
                    printf("SYSTEM> %s has reached the maximum chips.\n", game.players[i].name);
                    printf("%s> %s\n", game.players[i].name, LEAVE_GREETINGS[rand() % 3]);
                    csleep(1);
                    initBot(&game.players[i], initChips);
                    printf("%s> %s\n", game.players[i].name, ENTER_GREETINGS[rand() % 3]);
                }
            }
        }
        // 次のゲームへ
        for (;;)
        {
            printf("Dealer> (N)ext game or (Q)uit?\nYou all> ");
            char buffer[BUFFER_SIZE];
            if (fgets(buffer, BUFFER_SIZE, stdin) == NULL)
            {
                continue;
            }
            if (buffer[0] == 'Q' || buffer[0] == 'q')
            {
                printf("Dealer> Bye bye/~\n");
                return 0;
            }
            else if (buffer[0] == 'N' || buffer[0] == 'n')
            {
                break;
            }
            else
            {
                printf("SYSTEM> Invalid call.\n");
            }
        }
    }
    return 0;
}