ホームに戻る
C言語でリバーシ(AI対戦版)
bcc32用のC言語で書いたコンソールで動くリバーシです。
AI同士で対戦できるようにしました。
試合数とAIの使用できる制限時間を設定できます。
制限時間は1手が制限時間以上になるとその試合は負けになります。
以下の例は AI_1 をランダム、 AI_2 を盤面優先度で書いています。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
// 試合数
#define MATCH 100
// 制限時間(ミリ秒)
#define TIME_LIMIT 10
// 経過を表示する 1 しない 0
#define PRINT 0
// 白は後手、黒は先手
#define SIRO 1 // ●
#define KURO 2 // ○
// AI の名前
#define AI1_NAME "ai_1"
#define AI2_NAME "ai_2"
int get_k(int y, int x);
int check_k(int k, int y, int x);
// AI1のアルゴリズム
void ai1(int k, int *py, int *px){
// k は自分のコマであり if(k == KURO) で確認できます。
// コマ情報は get_k(y, x) で得ることができます。
// 何も置いていない:0 白いコマ:SIRO 黒いコマ:KURO
// コマが置けるかを if(check_k(k, y, x) == 1) でチェックし、
// 置く位置が決まったら *py と *px に値をセット
int count = 0;
for(int y = 0; y < 8; y++){
for(int x = 0; x < 8; x++){
if(check_k(k, y, x) == 1){
count++;
}
}
}
int c = 0;
int t = rand() % count;
for(int y = 0; y < 8; y++){
for(int x = 0; x < 8; x++){
if(check_k(k, y, x) == 1){
if(t == c){
*py = y;
*px = x;
}
c++;
}
}
}
}
// AI2のアルゴリズム
void ai2(int k, int *py, int *px){
// k は自分のコマであり if(k == KURO) で確認できます。
// コマ情報は get_k(y, x) で得ることができます。
// 何も置いていない:0 白いコマ:SIRO 黒いコマ:KURO
// コマが置けるかを if(check_k(k, y, x) == 1) でチェックし、
// 置く位置が決まったら *py と *px に値をセット
static int d[8][8] = {
{5, 1, 4, 4, 4, 4, 1, 5},
{1, 1, 2, 2, 2, 2, 1, 1},
{4, 2, 3, 3, 3, 3, 2, 4},
{4, 2, 3, 0, 0, 3, 2, 4},
{4, 2, 3, 0, 0, 3, 2, 4},
{4, 2, 3, 3, 3, 3, 2, 4},
{1, 1, 4, 4, 4, 4, 1, 1},
{5, 1, 2, 2, 2, 2, 1, 5}
};
int mx = 0;
for(int y = 0; y < 8; y++){
for(int x = 0; x < 8; x++){
if(check_k(k, y, x) == 1){
if(mx < d[y][x]){
mx = d[y][x];
}
}
}
}
int count = 0;
for(int y = 0; y < 8; y++){
for(int x = 0; x < 8; x++){
if(check_k(k, y, x) == 1){
if(d[y][x] == mx){
count++;
}
}
}
}
int c = 0;
int t = rand() % count;
for(int y = 0; y < 8; y++){
for(int x = 0; x < 8; x++){
if(check_k(k, y, x) == 1){
if(d[y][x] == mx){
if(t == c){
*py = y;
*px = x;
}
c++;
}
}
}
}
}
// 盤の初期状態 白を 1 黒を 2 とする
static int map_array[10][10] = {
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{-1, 0, 0, 0, 0, 0, 0, 0, 0, -1},
{-1, 0, 0, 0, 0, 0, 0, 0, 0, -1},
{-1, 0, 0, 0, 0, 0, 0, 0, 0, -1},
{-1, 0, 0, 0, 1, 2, 0, 0, 0, -1},
{-1, 0, 0, 0, 2, 1, 0, 0, 0, -1},
{-1, 0, 0, 0, 0, 0, 0, 0, 0, -1},
{-1, 0, 0, 0, 0, 0, 0, 0, 0, -1},
{-1, 0, 0, 0, 0, 0, 0, 0, 0, -1},
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
};
// 8方向のリスト
static int dir[8][2] = {
{-1, -1},
{-1, 0},
{-1, 1},
{ 0, -1},
{ 0, 1},
{ 1, -1},
{ 1, 0},
{ 1, 1}
};
static void init_map(int map[10][10]);
static void set_map(int map[10][10]);
static int get(int map[10][10], int y, int x);
static void put(int map[10][10], int k, int y, int x);
static void print_map(int map[10][10]);
static int check_dir(int map[10][10], int k, int y,int x, int dy, int dx, int flag);
static int check_koma(int map[10][10], int k, int y, int x);
static int count_enable_point(int map[10][10], int k);
static int count_point(int map[10][10], int k);
static void put_dir(int map[10][10], int k, int y, int x, int dy, int dx);
static void put_koma(int map[10][10], int k, int y, int x);
static int select_first_player();
static void input_point(int k, int *py, int *px);
static void no_point(int k);
static int jibun_turn(int map[10][10], int k);
static int teki_turn(int map[10][10], int k);
static void play_game(int map[10][10]);
// map を初期状態にセット
void init_map(int map[10][10]){
for(int y = 0; y < 10; y++){
for(int x = 0; x < 10; x++){
map[y][x] = map_array[y][x];
}
}
}
static int (*map_c)[10];
void set_map(int map[10][10]){
map_c = map;
}
int get_k(int y, int x){
return get(map_c, y, x);
}
// 盤の y 座標 x 座標の値を得る
int get(int map[10][10], int y, int x){
return map[y + 1][x + 1];
}
// 盤の y 座標 x 座標に値を入れる
void put(int map[10][10], int k, int y, int x){
map[y + 1][x + 1] = k;
}
// 盤を表示
void print_map(int map[10][10],int f){
static char *c = "ABCDEFGH";
static char *r = "123456789";
if(f == KURO){
if(PRINT == 1)printf("○:%s ●:%s\n", AI1_NAME, AI2_NAME);
}
else if(f == SIRO){
if(PRINT == 1)printf("○:%s ●:%s\n", AI2_NAME, AI1_NAME);
}
printf(" %s\n", c);
for(int y = 0; y < 8; y++){
printf("%c%c", r[y * 2], r[y * 2 + 1]);
for(int x = 0; x < 8; x++){
if(get(map, y, x) == 0){
printf("・");
}
else if(get(map, y, x) == SIRO){
printf("●");
}
else if(get(map, y, x) == KURO){
printf("○");
}
}
printf("\n");
}
get_char();
}
// dy, dx 方向を再帰で探索し、コマが置けるかチェック
int check_dir(int map[10][10], int k, int y, int x, int dy, int dx, int flag){
if(get(map, y, x) == 0 || get(map, y, x) == -1){
return 0;
}
// 反するコマがあれば flag = 1 し、その後自分のコマに出会えば可能
if(k == SIRO){
if(get(map, y, x) == KURO){
flag = 1;
}
else if(get(map, y, x) == SIRO){
return flag;
}
}
else if(k == KURO){
if(get(map, y, x) == SIRO){
flag = 1;
}
else if(get(map, y, x) == KURO){
return flag;
}
}
return check_dir(map, k, y + dy, x + dx, dy, dx, flag);
}
int check_k(int k, int y, int x){
return check_koma(map_c, k, y, x);
}
// 現在のコマと8方向を探索し、コマが置けるかチェック
int check_koma(int map[10][10], int k, int y, int x){
int result = 0;
if(get(map, y, x) != 0){
return 0;
}
for(int i = 0; i < 8; i++){
result |= check_dir(map, k, y + dir[i][0], x + dir[i][1], dir[i][0], dir[i][1], 0);
}
return result;
}
// 盤の中に k のコマが置ける箇所がいくつあるか数える
int count_enable_point(int map[10][10], int k){
int count = 0;
for(int y = 0; y < 8; y++){
for(int x = 0; x < 8; x++){
count += check_koma(map, k, y, x);
}
}
return count;
}
// 盤の中に k のコマはいくつあるか数える
int count_point(int map[10][10], int k){
int count = 0;
for(int y = 0; y < 8; y++){
for(int x = 0; x < 8; x++){
if(get(map, y, x) == k){
count++;
}
}
}
return count;
}
// dy, dx 方向を再帰で探索しコマを裏返していく
void put_dir(int map[10][10], int k, int y, int x, int dy, int dx){
if(k == SIRO){
if(get(map, y, x) == KURO){
put(map, SIRO, y, x);
}
else if(get(map, y, x) == SIRO){
return;
}
}
else if(k == KURO){
if(get(map, y, x) == SIRO){
put(map, KURO, y, x);
}
else if(get(map, y, x) == KURO){
return;
}
}
put_dir(map, k, y + dy, x + dx, dy, dx);
}
// コマを置いて、8方向を探索しコマを裏返す
void put_koma(int map[10][10], int k, int y, int x){
if(get(map, y, x) != 0){
return;
}
put(map, k, y, x);
for(int i = 0; i < 8; i++){
int result = check_dir(map, k, y + dir[i][0], x + dir[i][1], dir[i][0], dir[i][1], 0);
if(result == 1){
put_dir(map, k, y + dir[i][0], x + dir[i][1], dir[i][0], dir[i][1]);
}
}
}
// 敵ターン
int turn(int map[10][10], int k, void (*ai)(int, int *, int *)){
if(count_enable_point(map, k) == 0){
return -1;
}
else{
int y, x;
for(;;){
LARGE_INTEGER nFreq, nBefore, nAfter;
DWORD dwTime;
memset(&nFreq, 0x00, sizeof nFreq);
memset(&nBefore, 0x00, sizeof nBefore);
memset(&nAfter, 0x00, sizeof nAfter);
dwTime = 0;
QueryPerformanceFrequency(&nFreq);
QueryPerformanceCounter(&nBefore);
ai(k, &y, &x);
QueryPerformanceCounter(&nAfter);
dwTime = (DWORD)((nAfter.QuadPart - nBefore.QuadPart) * 1000 / nFreq.QuadPart);
if(dwTime >= TIME_LIMIT){
if(PRINT == 1)printf("タイムアップ: %dミリ秒\n", dwTime);
return -2;
}
if(check_koma(map, k, y, x) == 1){
put_koma(map, k, y, x);
break;
}
}
}
return 1;
}
// ゲーム1回の流れ
int play_game(int map[10][10], int f){
void (*sente_ai)(int, int *, int *);
void (*gote_ai)(int, int *, int *);
if(f == KURO){
sente_ai = ai1;
gote_ai = ai2;
}
else if(f == SIRO){
sente_ai = ai2;
gote_ai = ai1;
}
if(PRINT == 1)print_map(map, f);
int pass_count = 0; // パスした回数をカウント
for(;;){
int res1 = turn(map, KURO, sente_ai);
if(res1 == -1){
pass_count++;
}
else if(res1 == -2){
if(f == KURO){
printf("時間切れ:%s の勝ち %s の負け。\n", AI2_NAME, AI1_NAME);
return -2;
}
else if(f == SIRO){
printf("時間切れ:%s の勝ち %s の負け。\n", AI1_NAME, AI2_NAME);
return 2;
}
}
else{
pass_count = 0;
}
if(PRINT == 1)print_map(map, f);
if(pass_count == 2 || count_point(map, 0) == 0){
break;
}
int res2 = turn(map, SIRO, gote_ai);
if(res2 == -1){
pass_count++;
}
else if(res2 == -2){
if(f == SIRO){
printf(" %s の勝ち(時間切れ)。\n", AI2_NAME);
return -2;
}
else if(f == KURO){
printf("%s の勝ち(時間切れ)。\n", AI1_NAME);
return 2;
}
}
else{
pass_count = 0;
}
if(PRINT == 1)print_map(map, f);
if(pass_count == 2 || count_point(map, 0) == 0){
break;
}
}
int siro = count_point(map, SIRO);
int kuro = count_point(map, KURO);
if(PRINT == 1)printf("○:%d ●:%d ", kuro, siro);
if ((f == SIRO && siro > kuro) || (f == KURO && siro < kuro)){
printf("%s の勝ち。\n", AI1_NAME);
return 1;
}
else if((f == SIRO && siro < kuro) || (f == KURO && siro > kuro)){
printf(" %s の勝ち。\n", AI2_NAME);
return -1;
}
else{
printf(" 引き分け。\n");
return 0;
}
}
int main(){
int map[10][10];
srand((unsigned)time(NULL));
set_map(map);
int ai1_point = 0;
int ai2_point = 0;
int ai1_time_up = 0;
int ai2_time_up = 0;
int draw = 0;
for(int i = 0; i < MATCH; i++){
init_map(map);
printf("%03d回:", i+1);
int res = play_game(map, i % 2 + 1);
if(res == 2){
ai1_point++;
ai2_time_up++;
}
else if(res == 1){
ai1_point++;
}
else if(res == -1){
ai2_point++;
}
else if(res == -2){
ai2_point++;
ai1_time_up++;
}
else{
draw++;
}
}
printf("\n%s %d勝 %s %d勝 引き分け %d\n", AI1_NAME, ai1_point, AI2_NAME, ai2_point, draw);
printf("タイムアップ:%s %d回 %s %d回\n", AI1_NAME, ai1_time_up, AI2_NAME, ai2_time_up);
return 0;
}