#include <string.h>
#include <limits.h>
#include "config.h"
const ConfigItem DEFAULT_CONFIG[] = {
{"player_count", "1"}, // プレイヤー数
{"bot_count", "3"}, // ボットの数
{"init_chips", "10000"}, // 初期チップ数
{"min_bets", "100"}, // 最小ベット額
{"max_bets", "10000"}, // 最大ベット額
{"bj_rate", "1.5"}, // ブラックジャック時の配当倍率
{"bot_double_down_min", "9"}, // ボットがダブルダウンする最小値
{"bot_double_down_max", "11"}, // ボットがダブルダウンする最大値
{"bot_double_down_rate", "75"}, // ボットがダブルダウンを選択する確率
{"bot_hit_soft_rate", "80"}, // ボットが12以下でヒットする確率
{"bot_hit_middle_rate", "40"}, // ボットが13-16でヒットする確率
{"bot_hit_hard_rate", "10"}, // ボットが17-18でヒットする確率
};
const int DEFAULT_CONFIG_COUNT = 12;
int _createConfig(const char *filename)
{
FILE *file = fopen(filename, "w");
if (!file)
{
return 0;
}
for (int i = 0; i < DEFAULT_CONFIG_COUNT; i++)
{
fprintf(file, "%s=%s\n", DEFAULT_CONFIG[i].key, DEFAULT_CONFIG[i].value);
}
fclose(file);
return 1;
}
char *_trimLine(char *line)
{
char *end;
// 行頭の空白文字をスキップ
while (*line == ' ' || *line == '\t' || *line == '\n' || *line == '\r')
{
line++;
}
if (*line == '\0')
{
return line;
}
// 行末の空白文字を削除
end = line + strlen(line) - 1;
while (end > line && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r'))
{
*end = '\0';
end--;
}
return line;
}
Config *loadConfig(const char *filename)
{
FILE *file = fopen(filename, "r");
if (!file)
{
// ファイルが存在しない場合、デフォルト設定で作成
if (!_createConfig(filename))
{
fprintf(stderr, "Error: Failed to create config file '%s'\n", filename);
return NULL;
}
file = fopen(filename, "r");
if (!file)
{
fprintf(stderr, "Error: Failed to load config file '%s'\n", filename);
return NULL;
}
}
Config *config = (Config *)malloc(sizeof(Config));
if (!config)
{
fprintf(stderr, "Error: Failed to allocate memory for config\n");
return NULL;
}
config->items = NULL;
config->count = 0;
// 設定ファイルを1行ずつ読み込み
char line[CONFIG_MAX_KEY_LENGTH + CONFIG_MAX_VALUE_LENGTH + 2];
while (fgets(line, sizeof(line), file))
{
char *trimmedLine = _trimLine(line);
if (*trimmedLine == '\0')
{
continue;
}
// key=value形式をパース
char *equalsSign = strchr(trimmedLine, '=');
if (!equalsSign)
{
continue;
}
*equalsSign = '\0';
char *key = _trimLine(trimmedLine);
char *value = _trimLine(equalsSign + 1);
// 設定項目を追加
ConfigItem *newItems = (ConfigItem *)realloc(config->items, (config->count + 1) * sizeof(ConfigItem));
if (!newItems)
{
fprintf(stderr, "Error: Failed to allocate memory for config items\n");
free(config->items);
free(config);
return NULL;
}
config->items = newItems;
strncpy(config->items[config->count].key, key, CONFIG_MAX_KEY_LENGTH);
strncpy(config->items[config->count].value, value, CONFIG_MAX_VALUE_LENGTH);
config->count++;
}
fclose(file);
// 必要な設定項目が全て存在するか確認
int missingKey = 0;
for (int i = 0; i < DEFAULT_CONFIG_COUNT; i++)
{
if (getConfigValue(config, DEFAULT_CONFIG[i].key) == NULL)
{
missingKey = 1;
break;
}
}
// 不足している項目がある場合は設定ファイルを作り直し
if (missingKey)
{
fprintf(stderr, "Error: Missing required keys in config file '%s'\n", filename);
free(config->items);
free(config);
if (!_createConfig(filename))
{
fprintf(stderr, "Error: Failed to create config file '%s'\n", filename);
return NULL;
}
return loadConfig(filename);
}
return config;
}
const char *getConfigValue(const Config *config, const char *key)
{
for (int i = 0; i < config->count; i++)
{
if (strcmp(config->items[i].key, key) == 0)
{
return config->items[i].value;
}
}
return NULL;
}
int getConfigValueInt(const Config *config, const char *key, int *target)
{
char *e;
const char *value = getConfigValue(config, key);
if (value == NULL)
return 1;
const long t = strtol(value, &e, 10);
if (*e != '\0')
return 1;
// intの範囲外の値はエラー
if (t > INT_MAX || t < INT_MIN)
return 1;
*target = (int)t;
return 0;
}
int getConfigValueLong(const Config *config, const char *key, long *target)
{
char *e;
const char *value = getConfigValue(config, key);
if (value == NULL)
return 1;
const long t = strtol(value, &e, 10);
if (*e != '\0')
return 1;
*target = t;
return 0;
}
int getConfigValueDouble(const Config *config, const char *key, double *target)
{
char *e;
const char *value = getConfigValue(config, key);
if (value == NULL)
return 1;
const double t = strtod(value, &e);
if (*e != '\0')
return 1;
*target = t;
return 0;
}