#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;
}