以前の記事で自作オセロ(人vs人)を投稿して、コメントでアドバイスをいただけたので、改良して再投稿。
<改良点>
・変数名を分かりやすくした
・Bの番専用の関数、Wの番専用の関数があった
→if文を使って一つにまとめた
合計の行数が100行くらい減り、関数の数も減ったのでより分かりやすくなりました。
実はぐちゃぐちゃな方のプログラムを元手に、深層学習ナシでオセロのコツを学ばせただけの、打ち返してくるオセロAIを作ってました。なんと行数800。
今度はこっちを元に作って、より高度なものかつ分かりやすいものに挑戦します。
↓ソースコード↓
オセロ(改良版)
#include <stdio.h>
/*実行する関数(どっちの番か、ボード配列)戻り値→置けたら0,置ける場所がなかったら1*/
int run(char user,char board[12][12]);
/*配置を出力する関数*/
void outputBoard(char board[12][12]);
/*裏返す関数(どっちの番か、ベクトル、初期位置(縦、横)、配列)*/
int revers(char user,int i, int j, int vertical, int horizon, char board[12][12]);
/*まだ打てるか確認する関数*/
int check(char user,char board[12][12]);
/*そこに打てるか確認だけする関数*/
int spe_check(char user,int i, int j, int vertical, int horizon, char board[12][12]);
/*ベクトル(dir_ver,dir_hor)
(-1,-1)(-1,0)(-1,1)
(0,-1)(vertical,horizon)(0,1)
(1,-1)(1,0)(1,1)*/
int main(void) {
int i, j, cntNoRev;
int cntB = 0;
int cntW = 0;
char user = 'B';
char partner = 'W';
char board[12][12];
/*最初の配置を配列に代入(縦がi)*/
for (i = 0; i < 12; i++) {
for (j = 0; j < 12; j++) {
if ((i == 4 && j == 4) || (i == 5 && j == 5)) {
board[i][j] = 'B';
continue;
}
if ((i == 4 && j == 5) || (i == 5 && j == 4)) {
board[i][j] = 'W';
continue;
}
board[i][j] = ':';
}
}
/*配置を出力*/
outputBoard(board);
printf("Bが先行です。\n");
/*----実行-----*/
while(1){
/*---------Bの番---------*/
user = 'B';
cntNoRev = run(user, board);
if(cntNoRev==0){
outputBoard(board);
}
cntB = 0;
cntW = 0;
/*決着の確認*/
for (i = 1; i <= 8; i++) {
for (j = 1; j <= 8; j++) {
if (board[i][j] == 'B')
cntB++;
if (board[i][j] == 'W')
cntW++;
}
}
if ((cntB + cntW == 64) || cntB == 0 || cntW == 0)
break;
cntB = 0;
cntW = 0;
/*---------Wの番---------*/
user = 'W';
cntNoRev = run(user, board);
if (cntNoRev == 0){
outputBoard(board);
}
cntB = 0;
cntW = 0;
/*決着の確認*/
for (i = 1; i <= 8; i++) {
for (j = 1; j <= 8; j++) {
if (board[i][j] == 'B')
cntB++;
if (board[i][j] == 'W')
cntW++;
}
}
if ((cntB + cntW == 64) || cntB == 0 || cntW == 0)
break;
cntB = 0;
cntW = 0;
}
/*勝敗の確認*/
cntB = 0;
cntW = 0;
for (i = 1; i <= 8; i++) {
for (j = 1; j <= 8; j++) {
if (board[i][j] == 'B')
cntB++;
if (board[i][j] == 'W')
cntW++;
}
}
if (cntB > cntW)
printf("%d対%dでBの勝ちです", cntB, cntW);
if (cntB == cntW)
printf("引き分けです");
if (cntB < cntW)
printf("%d対%dでWの勝ちです", cntB, cntW);
return 0;
}
/*配置を出力する関数*/
void outputBoard(char board[12][12])
{
int i, j;
for (i = 1; i <= 8; i++) {
printf("\t%d", i);
}
printf("\n");
for (i = 1; i <= 8; i++) {
printf("%d\t", i);
for (j = 1; j <= 8; j++) {
printf("%c\t", board[i][j]);
}
printf("\n\n");
}
}
/*実行する関数(どっちの番か、ボード配列)戻り値→置けたら0,置ける場所がなかったら1*/
int run(char user, char board[12][12])
{
int i, j, horizon, vertical, dir_ver, dir_hor, tmp1, tmp2;
int cntRev = 0;//revers_to_Aを実行した回数をカウント
int cntNoPut = 0;//revers_to_Aを実行したけど置けなかった回数をカウント
int cntNoRev = 0;//どこにも置けない→1
char partner='W';
if (user == 'B') {
partner = 'W';
}
else if (user == 'W') {
partner = 'B';
}
while (cntRev == 0 || cntRev == cntNoPut) {
tmp1 = check(user,board);
if (tmp1 == 64) {
printf("%cをどこにも置けません\n",user);
cntNoRev++;
break;
}
printf("%cの番です。どこに打ちますか?(上の数字が先)\n",user);
scanf_s("%d", &horizon);
scanf_s("%d", &vertical);
j = 0;
if (board[vertical][horizon] != ':') {
printf("そこには打てません\n");
continue;
}
if (board[vertical][horizon] == ':') {
/*置いて裏返す*/
for (i = vertical - 1; i <= vertical + 1; i++) {
for (j = horizon - 1; j <= horizon + 1; j++) {
if (board[i][j] == partner) {
dir_ver = i - vertical;
dir_hor = j - horizon;
tmp2 = revers(user,dir_ver, dir_hor, vertical, horizon, board);
cntRev++;
if (tmp2 != 0)
cntNoPut++;
}
}
}
if (cntRev == 0 || cntRev == cntNoPut) {
printf("そこには打てません\n");
board[vertical][horizon] = ':';
}
}
}
return cntNoRev;
}
/*裏返す関数(ベクトル、初期位置(縦、横)、配列),裏返せた→0、裏返せない→1*/
int revers(char user,int dir_ver, int dir_hor, int vertical, int horizon, char board[12][12])
{
int move = 1;
int i, tmp_hor, tmp_ver;
tmp_hor = horizon;
tmp_ver = vertical;
int isReversed = 0;
char partner='W';
if (user == 'B') {
partner = 'W';
}
else if (user == 'W') {
partner = 'B';
}
board[vertical][horizon] = user;
while (board[tmp_ver + dir_ver][tmp_hor + dir_hor] == partner) {
if (board[vertical + dir_ver * 2][horizon + dir_hor* 2] == ':') {
isReversed++;
break;
}
if (board[vertical + dir_ver * 2][horizon + dir_hor * 2] == partner) {
horizon += dir_hor;
vertical += dir_ver;
move++;
}
if (board[vertical + dir_ver * 2][horizon + dir_hor * 2] == user) {
for (i = 0; i <= move; i++) {
if (dir_ver != 0 && dir_hor != 0)
board[vertical + dir_ver - dir_ver * i][horizon + dir_hor - dir_hor * i] = user;
if (dir_ver == 0)
board[vertical][horizon + dir_hor- dir_hor * i] = user;
if (dir_hor == 0)
board[vertical + dir_ver - dir_ver * i][horizon] = user;
}
}
}
return isReversed;
}
/*まだ打てるか確認する関数、戻り値→置けない場所の数*/
int check(char user,char board[12][12])
{
int i,j,dir_ver,dir_hor,is,horizon, vertical;
int cntRev = 0;
int cntNoPut = 0;
int cnt = 0;//打てなかった数をカウント
char partner='W';
if (user == 'B') {
partner = 'W';
}
else if (user == 'W') {
partner = 'B';
}
for (vertical = 1; vertical <= 8; vertical++) {
for (horizon = 1; horizon <= 8; horizon++) {
if (board[vertical][horizon] != ':') {
cnt++;
continue;
}
for (i = vertical - 1; i <= vertical + 1; i++) {
for (j = horizon - 1; j <= horizon + 1; j++) {
if (board[i][j] == 'W') {
dir_ver = i - vertical;
dir_hor = j - horizon;
is = spe_check(user,dir_ver, dir_hor, vertical, horizon, board);
cntRev++;
if (is != 0)
cntNoPut++;
}
}
}
if (cntRev == 0 || cntRev == cntNoPut) {
cnt++;
}
cntRev = 0;
cntNoPut = 0;
}
}
return cnt;
}
/*そこに打てるか確認だけする関数(打てる→0、打てない→1)*/
int spe_check(char user,int dir_ver, int dir_hor, int vertical, int horizon, char board[12][12])
{
int tmp_ver, tmp_hor;
tmp_hor = horizon;
tmp_ver = vertical;
int is = 0;
char partner='W';
if (user == 'B') {
partner = 'W';
}
else if (user == 'W') {
partner = 'B';
}
while (board[tmp_ver + dir_ver][tmp_hor + dir_hor] == partner) {
if (board[vertical + dir_ver * 2][horizon + dir_hor * 2] == ':') {
is++;
break;
}
if (board[vertical + dir_ver * 2][horizon + dir_hor * 2] == partner) {
horizon += dir_hor;
vertical += dir_ver;
}
if (board[vertical + dir_ver * 2][horizon + dir_hor * 2] == user) {
break;
}
}
return is;
}