✓当ブログのコンテンツには広告を含むことがあります

【C言語】簡単なオセロAIをプログラミングしてみた【サンプルコード】

C/C++
C/C++
この記事は約19分で読めます。

こんにちは!

前回は人間同士で戦えるところまで作ったので、今回は単純思考のコンピュータ(AI)と対戦できるところまでを作りました!

ランダムで駒を置くだけのAIですが(笑)

AIを作った…と聞くと僕がメチャクチャすごい人に聞こえますが、実際の僕はただの素人で、コードの書き方とかすごく汚いです。タイトルに【サンプルコード】ってつけてるのが恥ずかしいくらいなんですよね。

しかし、「コード汚くてもとりあえず動けばいいよ」って人もいると思うので、その方に向けて残します。※動作が変だったりしたら対処するので知らせて下さい。

追加した関数

前回までのコードに新しく↓のコードを追加しました。

AIの手をランダムで生成してくれます。

void tanjunAI() {//単純AIの手を作成してcheck関数に遷移する
	int AIteX[20];//適当に20手分格納できる配列を生成
	int AIteY[20];
	int cnt = 0;//ランダムで生成する数を入れていく
	int r;//ランダム

	for (int i = 1; i < L; i++) {//置ける場所を探して配列に格納
		for (int j = 1; j < L; j++) {
			aiueo = 1;//while関数で必要
			if (3 == check(j, i)) {
				AIteX[cnt] = j;
				AIteY[cnt] = i;
				cnt++;
			}
		}
	}
	
	srand(time(NULL));//乱数の初期化
        r = rand() % cnt; //手を選んでに格納 0~N個
	aiueo = 0;//while関数で必要

	Sleep(3*1000);

	check(AIteX[r],AIteY[r]);
}

コマンドプロントで実行するとこんな感じ

メニュー画面から「2」を入力してゲームスタート

先手か後手を決めて遊ぶことができます。

 

全体のコード

先に次回予告!

次回は盤面に評価値を付けて、強い手(良い手)を指せるAIを頑張って作ります。

コピペだとなぜがエラーが発生するそうです。。。

ファイルをアップロードしたので、ダウンロードして使ってください。

オセロのコードをダウンロード

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <time.h>//単純AIで手を選ぶときに使う
#include <stdlib.h>//単純AIで使う
#include <windows.h>//sleep関数を使うために必要。Macだと使えないので450行目あたりにあるSleepの行とこのwindows.hを消してください。

#define L 9 //盤の広さ
#define EMPTY 0//何も置いてない場所を示す
#define BLACK -2//1だとバグが出る
#define WHITE -1

void tanjunAI();//単純AI
void game();//手を入力して check関数に遷移
void menu();//人間同士で戦うかAIと戦うか選択する関数

int AIturn = 0;
int whichAI;
int AIx, AIy;//AIのx座標とy座標
int turn = BLACK;
int I_stone = BLACK;//ひっくり返す処理で使う。turn_change()でWHITEと入れ替わる
int You_stone = WHITE;//ひっくり返す処理で使う。turn_change()でBLACKと入れ替わる
int board[L][L];//盤を表示するときに使う
int board_next[L][L];//駒をひっくり返すときに使う。boardのコピー
int cnt;//駒をひっくり返す時に使う
int sum;//駒が置けるかチェックするときに使う
int p;//while関数で駒が置けるかチェックするときに使う
int flag1 = 1;//単純AIの関数とall_check関数で使用

int angle_x[] = { 0,1,1,1,0,-1,-1,-1};//八方面を表す
int angle_y[] = { -1,-1,0,1,1,1,0,-1};//八方面を表す

void shokika() {//盤面リセット
	for (int i = 0; i < L; i++) {
		for (int j = 0; j < L; j++) {
			board[i][j] = EMPTY;
		}
	}
	board[4][4] = WHITE;
	board[4][5] = BLACK;
	board[5][4] = BLACK;
	board[5][5] = WHITE;
}

void turn_change() {//手番の変更
	if (turn == WHITE) {
		turn = BLACK;
		I_stone = BLACK;
		You_stone = WHITE;
	}
	else {
		turn = WHITE;
		I_stone = WHITE;
		You_stone = BLACK;
	}
}

void display() {//盤面表示・更新
	for (int i = 0; i < L; i++) {
		for (int j = 0; j < L; j++) { switch (board[i][j]) { case WHITE: printf(" ●"); break; case BLACK: printf(" 〇"); break; default: if (i==0||j==0) { if (j == 0) { board[i][j]=i;//縦の数字を代入 } if (i == 0) { switch (j){ case 1: printf("  1 2 3 4 5 6 7 8");//横の数字を表示 break; } } else{ printf(" %d", board[i][j]);//縦の数字を表示 } break; } else { printf(" *"); } } if (j == L - 1) {//改行 printf("\n"); } } } game(); } 


int rolling(int i,int w_x,int w_y,int *flag){//石をひっくり返す関数 
    do { if (i == 0) {//上方向の時 
          if (board_next[w_y - cnt][w_x] == You_stone) { 
              board_next[w_y - cnt][w_x] = I_stone; 
              cnt++; flag[0] = 1; 
              if (board_next[w_y - cnt][w_x] == EMPTY || w_y - cnt == 1 && board_next[w_y - cnt][w_x] == You_stone || w_y - cnt == 0) { 
                  int a = cnt - 1; flag[0] = 0; 
                  do { board_next[w_y - a][w_x] = You_stone; a--; } while (a >= 1);
				}
			}
			else {
				break;
			}
		}
		else if (i == 1) {//右上
			if (board_next[w_y - cnt][w_x + cnt] == You_stone) {
				board_next[w_y - cnt][w_x + cnt] = I_stone;
				cnt++;
				flag[1] = 1;
				if (board_next[w_y - cnt][w_x + cnt] == EMPTY || w_y - cnt == 1 && board_next[w_y - cnt][w_x + cnt] == You_stone || w_x + cnt == 8 && board_next[w_y - cnt][w_x + cnt] == You_stone || w_y - cnt == 0 || w_x + cnt == 9) {
					int a = cnt - 1;
					flag[1] = 0;
					do {
						board_next[w_y - a][w_x + a] = You_stone;
						a--;
					} while (a >= 1);
				}
			}
			else {
				break;
			}
		}
		else if (i == 2) {//右
			if (board_next[w_y][w_x + cnt] == You_stone) {
				board_next[w_y][w_x + cnt] = I_stone;
				cnt++;
				flag[2] = 1;
				if (board_next[w_y][w_x + cnt] == EMPTY || w_x + cnt == 8 && board_next[w_y][w_x + cnt] == You_stone || w_x + cnt == 9) {
					int a = cnt - 1;
					flag[2] = 0;
					do {
						board_next[w_y][w_x + a] = You_stone;
						a--;
					} while (a >= 1);
				}
			}
			else {
				break;
			}
		}
		else if (i == 3) {//右下
			if (board_next[w_y + cnt][w_x + cnt] == You_stone) {
				board_next[w_y + cnt][w_x + cnt] = I_stone;
				cnt++;
				flag[3] = 1;
				if (board_next[w_y + cnt][w_x + cnt] == EMPTY || w_y + cnt == 8 && board_next[w_y + cnt][w_x + cnt] == You_stone || w_x + cnt == 8 && board_next[w_y + cnt][w_x + cnt] == You_stone || w_y + cnt == 9 || w_x + cnt == 9) {
					int a = cnt - 1;
					flag[3] = 0;
					do {
						board_next[w_y + a][w_x + a] = You_stone;
						a--;
					} while (a >= 1);
				}
			}
			else {
				break;
			}
		}
		else if (i == 4) {//下
			if (board_next[w_y + cnt][w_x] == You_stone) {
				board_next[w_y + cnt][w_x] = I_stone;
				cnt++;
				flag[4] = 1;
				if (board_next[w_y + cnt][w_x] == EMPTY || w_y + cnt == 8 && board_next[w_y + cnt][w_x] == You_stone || w_y + cnt == 9) {
					int a = cnt - 1;
					flag[4] = 0;
					do {
						board_next[w_y + a][w_x] = You_stone;
						a--;
					} while (a >= 1);
				}
			}
			else {
				break;
			}
		}
		else if (i == 5) {//左下
			if (board_next[w_y + cnt][w_x - cnt] == You_stone) {
				board_next[w_y + cnt][w_x - cnt] = I_stone;
				cnt++;
				flag[5] = 1;
				if (board_next[w_y + cnt][w_x - cnt] == EMPTY || w_y + cnt == 8 && board_next[w_y + cnt][w_x - cnt] == You_stone || w_x - cnt == 1 && board_next[w_y + cnt][w_x - cnt] == You_stone || w_y + cnt == 9 || w_x - cnt == 0) {
					int a = cnt - 1;
					flag[5] = 0;
					do {
						board_next[w_y + a][w_x - a] = You_stone;
						a--;
					} while (a >= 1);
				}
			}
			else {
				break;
			}
		}
		else if (i == 6) {//左
			if (board_next[w_y][w_x - cnt] == You_stone) {
				board_next[w_y][w_x - cnt] = I_stone;
				cnt++;
				flag[6] = 1;
				if (board_next[w_y][w_x - cnt] == EMPTY || w_x - cnt == 1 && board_next[w_y][w_x - cnt] == You_stone || w_x - cnt == 0) {
					int a = cnt - 1;
					flag[6] = 0;
					do {
						board_next[w_y][w_x - a] = You_stone;
						a--;
					} while (a >= 1);
				}
			}
			else {
				break;
			}
		}
		else if (i == 7) {//左上
			if (board_next[w_y - cnt][w_x - cnt] == You_stone) {
				board_next[w_y - cnt][w_x - cnt] = I_stone;
				cnt++;
				flag[7] = 1;
				if (board_next[w_y - cnt][w_x - cnt] == EMPTY || w_y - cnt == 1 && board_next[w_y - cnt][w_x - cnt] == You_stone || w_x - cnt == 1 && board_next[w_y - cnt][w_x - cnt] == You_stone || w_y - cnt == 0 || w_x - cnt == 0) {
					int a = cnt - 1;
					flag[7] = 0;
					do {
						board_next[w_y - a][w_x - a] = You_stone;
						a--;
					} while (a >= 1);
				}
			}
			else {
				break;
			}
		}
		if (flag[i] == 0) {
			break;
		}
	} while (1);
	if (cnt == 1) { board_next[w_y][w_x] = EMPTY; return 2; }
	p = 1;
}

int check(int w_x, int w_y) {//石を置けるかチェック
	if (EMPTY != board[w_y][w_x]) {
		return 0;
	}
	int flag[8] = { 0,0,0,0,0,0,0,0 };
	memcpy((void*)board_next, (void*)board, sizeof(board));
	board_next[w_y][w_x] = I_stone;
	for (int i = 0; i < 8; i++) {
		cnt = 1;
		switch (board[w_y + angle_y[i]][w_x + angle_x[i]]) {//置きたい場所の周りに敵の石があるか確認
		case BLACK:
			if (turn == BLACK) { break; }//黒のターンなら処理しない
			rolling(i, w_x, w_y, flag);
			break;
		case WHITE:
			if (turn == WHITE) { break; }
			rolling(i, w_x, w_y, flag);
			break;
		default:
			break;
		}
	}
	if (p == EMPTY) {
		board_next[w_y][w_x] = EMPTY;
		return 2;
	}

	for (int i = 0; i < 8; i++) {
		sum += flag[i];
	}
	if (sum == EMPTY) {
		board_next[w_y][w_x] = EMPTY;
		return 2;
	}
	sum = EMPTY;

	if (flag1 == 1) { flag1 = 0; return 3; }//all_check中なのでここで終了

	memcpy((void*)board, (void*)board_next, sizeof(board_next));
	turn_change();
	if(whichAI == 2){AIturn *= -1;}//AIのターンを変更
	p = 0;
	display();//再描画
	return 3;
}

void finish() {
	int cntb = 0, cntw = 0;
	for (int i = 1; i < L; i++) {
		for (int j = 1; j < L; ++j) { switch (board[i][j]) { case BLACK: cntb++; break; case WHITE: cntw++; break; default: break; } } } printf("置ける場所が無くなったのでゲーム終了です。\n結果\n黒 %d個\n白 %d個\n", cntb, cntw); if (cntb > cntw) {
		printf("黒の勝ち\n\n");
	}
	else if (cntw > cntb) {
		printf("白の勝ち\n\n");
	}
	else {
		printf("引き分け\n\n");
	}
	menu();
}

void all_check() {
	flag1 = 1;
	for (int w = 0; w < 2; w++) {//二重パス
		for (int i = 1; i < L; i++) {
			for (int j = 1; j < L; j++) { if (3 == check(j, i)) { if (w == 0) { return; }else{ printf("どこにも置けないのでパスします。\nもう一度"); return; } } } } turn_change(); } finish(); } void game() {//手を入力させる int x; int y; all_check(); if (AIturn == 1) {//AI対戦用 printf("AIの手番です。\n"); tanjunAI(); } if (turn == BLACK) { printf("黒の手番です。手を入力してください。\nXを入力\n"); } else { printf("白の手番です。手を入力してください。\nXを入力\n"); } while (1) { if (scanf("%d", &x) >= 1) {//数値だけを入力させる
			break;
		}
		scanf("%*s");
		printf("もう一度入力してください。\n");
	}

		printf("Yを入力");

	while (1) {
		if (scanf("%d", &y) >= 1) {//数値だけを入力させる
			break;
		}
		scanf("%*s");
		printf("もう一度入力してください。\n");
	}

	if (1 != check(x, y)) {//while関数の変数pと繋がってる
		printf("そこには置けません。\n");
		game();
	}
}

void menu() {//メニューを表示し、選択したゲームモードで遊べるようにする関数
	int start = 0;
        whichAI = 0;
        AIturn = 0;
        whichAI = 0;
	printf("Main menu:\n1)人間vs人間\n2)人間vs単純AI\nQ)Quit program\nPlease enter your selection:");
		while(1) {
			if (scanf("%d", &start) != 1) {//数値だけを入力させる
				scanf("%*s");
				printf("もう一度入力してください。\n");
				continue;
			}
			if (start == 1 || start == 2) {
				break;
			}
			printf("もう一度入力してください。\n");
		}
        
        whichAI = start;
	switch (start) {
	case 1:
		AIturn = -1;
		shokika();
		display();
		game();
		break;
	case 2:
		printf("先手で指す:1を入力\n後手で指す:2を入力\n");
		while (1) {
			scanf("%d", &start);
			if(start < 1 && start >8) {//数値だけを入力させる
				scanf("%*s");
				printf("もう一度入力してください。\n");
				continue;
			}
				if (start == 2) {//後手でスタート
					AIturn = 1;
				}else{ AIturn = -1; }
				shokika();
				display();
				game();
				break;
		}
	default:
		printf("もう一度入力してください。\n");
		menu();
	}
}

void tanjunAI() {//単純AIの手を作成してcheck関数に遷移する
	int AIteX[20];//適当に20手分格納できる配列を生成
	int AIteY[20];
	int cnt = 0;//ランダムで生成する数を入れていく
	int r;//ランダム

	for (int i = 1; i < L; i++) {//置ける場所を探して配列に格納
		for (int j = 1; j < L; j++) {
			flag1 = 1;//while関数で必要
			if (3 == check(j, i)) {
				AIteX[cnt] = j;
				AIteY[cnt] = i;
				cnt++;
			}
		}
	}
	
	srand(time(NULL));//乱数の初期化
    r = rand() % cnt; //手を選んでに格納 0~N個
	flag1 = 0;//while関数で必要

	Sleep(2*1000);//2秒停止させてから表示

	check(AIteX[r],AIteY[r]);//選んだ手を入力
}

int main(int argc, char* argv[])
{
	menu();
	return 0;
}

ではまた次回お会いしましょう!

コメント