オセロの反転の考え方について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
jun3453
記事: 22
登録日時: 14年前
住所: 東京

オセロの反転の考え方について

#1

投稿記事 by jun3453 » 9年前

今課題でオセロのプログラミングをしています。

盤の表示とコマの配置をするとこまで来ましたが、
置ける場所の特定と反転の仕組みの考え方がイマイチ理解できません。

場所の特定で考えていたのが、

黒石発見 → 
8方向の白石があるか調べる → 
白石があれば、その白石の次へ →
空きマスがあればそこに置ける。

というもので、反転はどうすればいいのかまだわかっていません。
いろんなページを見ましたが、サンプルが多く、どれを実装したらいいのかわかりませんでした。

考え方だけでもいいので教えていただければと思っています。

コード:

#include<stdio.h>
#include<math.h>

int field[8][8]; // フィールド座標
bool turn = 0;

// フィールド描画
void Draw_field(){
	int i,j;		 // for用

	// フィールド表示
	printf(" 12345678\n");
	for(i=0;i<8;i++){
		printf("%d ",i+1);
		for(j=0;j<8;j++){

			if(field[j][i] == 0){
				printf("*");
			}
			
			if(field[j][i] == 1){
				printf("○");
			}
			
			if(field[j][i] == 2){
				printf("●");
			}

			if(field[j][i] == 3){
				printf("☆");
			}
		}

		printf("\n");
	}
}

// 石探し
void Check_stone(){
}

// 石置く処理
void Put_stone(){
	int x,y;

	if(turn == 0){
		printf("○のターンです。\n");
	}else{
		printf("●のターンです。\n");
	}

	printf("石の座標Xを指定してください。\n");
	scanf("%d",&x);
	printf("石の座標Yを指定してください。\n");
	scanf("%d",&y);
	printf("\n");

	// 絶対値にする。
	x = abs(x);
	y = abs(y);

	// 数値が小さい
	if(x == 0 || y == 0){
		printf("======数値が小さすぎます!======\n");
	// 数値が大きい
	}else if(x >= 9 || y >= 9){
		printf("======数値が大きすぎます!======\n");
	// 数値が正しい
	}else{

		// 座標調整
		x = x-1;
		y = y-1;

		// 黒ターン
		if(turn == 0){
			// 0のとき
			if(field[x][y] == 0){
				field[x][y] = 1; //黒置く
				turn = 1;		 //ターン推移
			// 何かあるとき
			}else{
				printf("======そこには置けません。======\n");
			}

		// 白ターン
		}else if(turn == 1){
			// 0のとき
			if(field[x][y] == 0){
				field[x][y] = 2; //白置く
				turn = 0;		 //ターン推移
			// 何かあるとき
			}else{
				printf("======そこには置けません。======\n");
			}
		}

	}



}

void Title(){
	printf("======================\n");
	printf("=== オセロ プレイヤーVSプレイヤー ===\n");
	printf("======================\n");

	printf("マイナス入力は絶対値になります。\n\n");
	printf("文字入力したらエラーおきます。\n\n");
}


int main (void){
	
	Title();

	// 初期石追加
	field[3][3] = 1;
	field[3][4] = 2;
	field[4][4] = 1;
	field[4][3] = 2;

	for(;;){
		Draw_field();
		Put_stone();
	}

	return 0;
}

yoko
記事: 24
登録日時: 14年前

Re: オセロの反転の考え方について

#2

投稿記事 by yoko » 9年前

考え方という点でいうと、
場所の特定は逆で、

空白マス発見→
周囲8マスに自色と異なる色がある→
色のある方向を調べ、自色が出てきたら置ける、壁または空白マスの場合は置けない

これを全部のマスで行えばおける場所の特定ができ、
反転は場所の特定を応用して、

周囲8マスを1マスずつ調べていき、
反転可能な方向(異なる色の直後に自色がある)場合は、
自色が出てくるまで異なる色の個所を自色に変える

とすればできます。

プログラムを少しだけ見ましたが、
黒、白の値は1と-1にしたほうが反転のときに-1を掛けるだけで済むので楽です。
今のやり方だと反転のときに黒と白別々に処理を書かなければいけなそうでしたので。

あと、知っているかわかりませんが、定数は#defineで定数化したほうがプログラムが見やすくなりますし、
変更があっても変更箇所は#defineの箇所のみで済むので楽です。

jun3453
記事: 22
登録日時: 14年前
住所: 東京

Re: オセロの反転の考え方について

#3

投稿記事 by jun3453 » 9年前

yokoさんありがとうございます!
とりあえず縦横のみの場所特定の実装ができました!

斜めの処理より先に縦横反転も作ってしまおうと思っているのですが、
色の反転で迷走中なので、詳しく教えて頂きたいです。

コード:

#include<stdio.h>
#include<math.h>

#define BLACK 1
#define WHITE -1
#define EMPTY 0
#define PUTOK 2


int field[8][8]; // フィールド座標 X,Y
int turn = BLACK;

// フィールド描画
void Draw_field(){
	int i,j;		 // for用

	// フィールド表示
	printf(" 12345678\n");
	for(i=0;i<8;i++){
		printf("%d ",i+1);
		for(j=0;j<8;j++){

			// 空き
			if(field[j][i] == EMPTY){
				printf("*");
			}
			
			// 黒
			if(field[j][i] == BLACK){
				printf("○");
			}
			
			// 白
			if(field[j][i] == WHITE){
				printf("●");
			}

			// 置ける場所
			if(field[j][i] == PUTOK){
				printf("☆");
			}
		}

		printf("\n");
	}
} // ---------Draw_field-------------

// 石探し
void Check_stone(){
	int i=0,j=0;   // 盤面探索for
	int x=0,y=0;   // 自色探索for

	// 盤面探索
	for(i=0;i<8;i++){
		for(j=0;j<8;j++){

			// 方向用変数
				int right = field[j+1][i],
					left  = field[j-1][i],
					down  = field[j][i-1],
					up    = field[j][i+1],
					
					right_up   = field[j-1][i+1],
					right_down = field[j+1][i-1],
					left_up	   = field[j+1][i+1],
					left_down  = field[j-1][i-1];

			// 空きがあったとき
			if(field[j][i] == EMPTY){
				// 空きマスから周囲8マス探索
				// その方向の駒が相手のものであり、空きでなく、☆でないものであれば、
				// その方向の自駒が出るまで探索。あれば☆を置く。
				if(right != turn && right != EMPTY && right != PUTOK){
					for(x=0;x<8;x++){
						if(field[x][i] == turn){
							field[j][i] = PUTOK;
						}
					}
				}
				
				if(left != turn && left != EMPTY && left != PUTOK){
					for(x=0;x<8;x++){
						if(field[x][i] == turn){
							field[j][i] = PUTOK;
						}

						
					}
				}
				
				if(up != turn && up != EMPTY && up != PUTOK){
					for(y=0;y<8;y++){
						if(field[j][y] == turn){
							field[j][i] = PUTOK;
						}					
					}
				}
				
				if(down != turn && down != EMPTY && down != PUTOK){
					for(y=0;y<8;y++){
						if(field[j][y] == turn){
							field[j][i] = PUTOK;
						}
					}
				}
				
				if(right_up != turn && right_up != EMPTY && right_up != PUTOK){

				}
				if(right_down != turn && right_down != EMPTY && right_down != PUTOK){

				}
				if(left_up != turn && left_up != EMPTY && left_up != PUTOK){

				}
				if(left_down != turn && left_down != EMPTY && left_down != PUTOK){

				}

			// ☆を初期化
			}else if(field[j][i] == PUTOK){
					field[j][i] = EMPTY;
			}
		}
	}

}// ------- Check_stone ------------

void Change_stone(){
	int i,j;
	int x,y;

	bool xtrig = false,ytrig = false;

	// 盤面探索
	for(i=0;i<8;i++){
		for(j=0;j<8;j++){

			for(x=0;x<8;x++){
				if(field[x][i] == turn){
					xtrig = true;
				}
				if(xtrig == true){
					field[x][i] *= -1;
					if(field[x][i] == turn){
						xtrig = false;
					}
				}
			}

		}
	}


}// --------Change_stone ------------

// 石置く処理
void Put_stone(){
	int x,y; // 座標入力用

	if(turn == BLACK){
		printf("○のターンです。\n");
	}else{
		printf("●のターンです。\n");
	}

	printf("石の座標Xを指定してください。\n");
	scanf("%d",&x);
	printf("石の座標Yを指定してください。\n");
	scanf("%d",&y);
	printf("\n");

	// 絶対値にする。
	x = abs(x);
	y = abs(y);

	// 数値が小さい
	if(x == 0 || y == 0){
		printf("======数値が小さすぎます!======\n");
	// 数値が大きい
	}else if(x >= 9 || y >= 9){
		printf("======数値が大きすぎます!======\n");
	// 数値が正しい
	}else{

		// 座標調整
		x = x-1;
		y = y-1;

		// 黒ターン
		if(turn == BLACK){
			// 0のとき
			if(field[x][y] == PUTOK){
				field[x][y] = BLACK; //黒置く
				turn = WHITE;		 //ターン推移
			// 何かあるとき
			}else{
				printf("======そこには置けません。======\n");
			}

		// 白ターン
		}else if(turn == WHITE){
			// 0のとき
			if(field[x][y] == PUTOK){
				field[x][y] = WHITE; //白置く
				turn = BLACK;		 //ターン推移
			// 何かあるとき
			}else{
				printf("======そこには置けません。======\n");
			}
		}

	}

}// --------- Put_stone ---------

// タイトル
void Title(){
	printf("======================\n");
	printf("=== オセロ プレイヤーVSプレイヤー ===\n");
	printf("======================\n");

	printf("マイナス入力は絶対値になります。\n\n");
	printf("文字入力したらエラーおきます。\n\n");
}// --------- Title ----------

// メイン
int main (void){
	
	Title();

	// 初期石追加
	field[3][3] = BLACK;
	field[3][4] = WHITE;
	field[4][4] = BLACK;
	field[4][3] = WHITE;
	
		
	for(;;){
		Check_stone();
		Draw_field();
		Put_stone();
		Change_stone();
	}

	return 0;
}// ------- main -----------

yoko
記事: 24
登録日時: 14年前

Re: オセロの反転の考え方について

#4

投稿記事 by yoko » 9年前

反転の前に
Check_stone関数に

コード:

// ☆を初期化
}
	else if (field[j][i] == PUTOK){
		field[j][i] = EMPTY;
	}
とありますが、
これだと

白が置く番ではPUTOKだったけど、
白が別の場所に置いて黒の番になったときに
黒がPUTOK

という場合に対応できません。

改善策としては、
盤面検索の前に先に全てのPUTOKをEMPTYに変更してから盤面検索を行うと確実です。

また、今のままだと正しい石探しが出来ていません。
例えば、右を見ているときに、

コード:

for (x = 0; x<8; x++){
	if (field[x][i] == turn){
		field[j][i] = PUTOK;
	}
}
と書かれているため、
このままでは右側が相手の色でかつ左側に自分の色の石があった場合でもPUTOKになってしまいます。
for文を、現在の座標から調べたい方向の壁までにしなければなりません。

反転ですが、斜めは後回しとのことなので、
縦横についてですが、
まず、Change_stone関数にはx,y座標の引数が必要です。
置いた場所においてのみ反転させるのですから。
それで、最初の回答にも書いてありますが、反転は場所の特定を応用します。

場所の特定を応用するので、場所の特定がちゃんと書けていること前提ですが、

置いた場所のみ周りの8マスを調べ、
置ける方向であれば自色ではない色が現れるまでのマスを反転

これで反転できます。

jun3453
記事: 22
登録日時: 14年前
住所: 東京

Re: オセロの反転の考え方について

#5

投稿記事 by jun3453 » 9年前

yokoさんありがとうございます!
遅くなってしまいもうしわけありません;;

指摘箇所を訂正し、
上下左右の反転を可能にすることができました!

そして斜めなのですが、どういう風に探索していけばいいのか想像ついていないのでヒントお願いします・・・。

毎回聞いてしまって申し訳ないのですが、よろしくお願いします。

コード:

#include<stdio.h>
#include<math.h>

#define BLACK 1
#define WHITE -1
#define EMPTY 0
#define PUTOK 2


int field[8][8]; // フィールド座標 X,Y
int turn = BLACK;

// フィールド描画
void Draw_field(){
	int i,j;		 // for用

	// フィールド表示
	printf(" 12345678\n");
	for(i=0;i<8;i++){
		printf("%d ",i+1);
		for(j=0;j<8;j++){

			// 空き
			if(field[j][i] == EMPTY){
				printf("*");
			}
			
			// 黒
			if(field[j][i] == BLACK){
				printf("○");
			}
			
			// 白
			if(field[j][i] == WHITE){
				printf("●");
			}

			// 置ける場所
			if(field[j][i] == PUTOK){
				printf("☆");
			}
		}

		printf("\n");
	}
} // ---------Draw_field-------------

// 石探し
void Check_stone(){
	int i=0,j=0;   // 盤面探索for
	int x=0,y=0;   // 自色探索for

	// PUTOK初期化
	for(i=0;i<8;i++){
		for(j=0;j<8;j++){
			if(field[i][j] == PUTOK){
				field[i][j] = EMPTY;
			}
		}
	}

	// 盤面探索
	for(i=0;i<8;i++){
		for(j=0;j<8;j++){

			// 方向用変数
				int right = field[j+1][i],
					left  = field[j-1][i],
					down  = field[j][i-1],
					up    = field[j][i+1],
					
					right_up   = field[j+1][i-1],
					right_down = field[j+1][i+1],
					left_up	   = field[j-1][i-1],
					left_down  = field[j-1][i+1];

			// 空きがあったとき
			if(field[j][i] == EMPTY){
				// 空きマスから周囲8マス探索
				// その方向の駒が相手のものであり、空きでなく、☆でないものであれば、
				// その方向の自駒が出るまで探索。あれば☆を置く。
			
				// 上下左右
				if(right != turn && right != EMPTY && right != PUTOK){
					for(x=j;x<8;x++){
						if(field[x][i] == turn){
							field[j][i] = PUTOK;
						}
					}
				}
				
				if(left != turn && left != EMPTY && left != PUTOK){
					for(x=j;0<x;x--){
						if(field[x][i] == turn){
							field[j][i] = PUTOK;
						}
					}
				}
				
				if(up != turn && up != EMPTY && up != PUTOK){
					for(y=i;y<8;y++){
						if(field[j][y] == turn){
							field[j][i] = PUTOK;
						}					
					}
				}
				
				if(down != turn && down != EMPTY && down != PUTOK){
					for(y=i;0<y;y--){
						if(field[j][y] == turn){
							field[j][i] = PUTOK;
						}
					}
				}
				
				// 斜め
				//right_up   = field[j+1][i-1],
				if(right_up != turn && right_up != EMPTY && right_up != PUTOK){

				}
				if(right_down != turn && right_down != EMPTY && right_down != PUTOK){

				}
				if(left_up != turn && left_up != EMPTY && left_up != PUTOK){

				}
				if(left_down != turn && left_down != EMPTY && left_down != PUTOK){

				}
			}
		}
	}

}// ------- Check_stone ------------


// 石変更処理
// 引数: sx 置いた石の座標X
//     sy 置いた石の座標Y
void Change_stone(int sx, int sy){
	int i,j;
	int x,y;
	int dx=0,dy=0;

	// 引数置き換え
	j = sx;
	i = sy;

	// 方向用変数
	int right = field[j+1][i],
		left  = field[j-1][i],
		down  = field[j][i-1],
		up    = field[j][i+1],
					
		right_up   = field[j+1][i-1],
		right_down = field[j+1][i+1],
		left_up	   = field[j-1][i-1],
		left_down  = field[j-1][i+1];

	// 空きマスから周囲8マス探索
	// その方向の駒が相手のものであり、空きでなく、☆でないものであれば、
	// 自色が出てくるまで探索、出てきたらひっくり返していく。
	if(right != turn && right != EMPTY && right != PUTOK){
		dx = 0;
		for(x=j;x<8;x++){
			// 敵色のときdx++
			if(field[x][i] != turn){
				dx++;
			// 自色のとき、戻って反転
			}else if(field[x][i] == turn){
				while(dx!=-1){
					dx--;
					field[x-1][i] *= -1;
				}
			}
		}

	}

	if(left != turn && left != EMPTY && left != PUTOK){
		dx = 0;
		for(x=j;0<x;x--){
			// 敵色のときdx++
			if(field[x][i] != turn){
				dx++;
			// 自色のとき、戻って反転
			}else if(field[x][i] == turn){
				while(dx!=-1){
					dx--;
					field[x+1][i] *= -1;
				}
			}
		}
	}
		
	if(up != turn && up != EMPTY && up != PUTOK){
		dy=0;
		for(y=i;y<8;y++){
			// 敵色のときdx++
			if(field[j][y] != turn){
				dy++;
			// 自色のとき、戻って反転
			}else if(field[j][y] == turn){
				while(dy!=-1){
					dy--;
					field[j][y-1] *= -1;
				}
			}				
		}
	}
		
	if(down != turn && down != EMPTY && down != PUTOK){
		dy=0;
		for(y=i;0<y;y--){
			// 敵色のときdx++
			if(field[j][y] != turn){
				dy++;
			// 自色のとき、戻って反転
			}else if(field[j][y] == turn){
				while(dy!=-1){
					dy--;
					field[j][y+1] *= -1;
				}
			}
		}
	}

	if(right_up != turn && right_up != EMPTY && right_up != PUTOK){
	}

	if(right_down != turn && right_down != EMPTY && right_down != PUTOK){

	}
	if(left_up != turn && left_up != EMPTY && left_up != PUTOK){
		
	}
	if(left_down != turn && left_down != EMPTY && left_down != PUTOK){

	}
		
}// --------Change_stone ------------

// 石置く処理
void Put_stone(){
	int x,y; // 座標入力用

	if(turn == BLACK){
		printf("○のターンです。\n");
	}else{
		printf("●のターンです。\n");
	}

	printf("石の座標Xを指定してください。\n");
	scanf("%d",&x);
	printf("石の座標Yを指定してください。\n");
	scanf("%d",&y);
	printf("\n");

	// 絶対値にする。
	x = abs(x);
	y = abs(y);

	// 数値が小さい
	if(x == 0 || y == 0){
		printf("======数値が小さすぎます!======\n");
	// 数値が大きい
	}else if(x >= 9 || y >= 9){
		printf("======数値が大きすぎます!======\n");
	// 数値が正しい
	}else{

		// 座標調整
		x = x-1;
		y = y-1;

		// 黒ターン
		if(turn == BLACK){
			// 0のとき
			if(field[x][y] == PUTOK){
				Change_stone(x,y);
				field[x][y] = BLACK; //黒置く
				turn = WHITE;		 //ターン推移
			// 何かあるとき
			}else{
				printf("======そこには置けません。======\n");
			}

		// 白ターン
		}else if(turn == WHITE){
			// 0のとき
			if(field[x][y] == PUTOK){
				Change_stone(x,y);
				field[x][y] = WHITE; //白置く
				turn = BLACK;		 //ターン推移
			// 何かあるとき
			}else{
				printf("======そこには置けません。======\n");
			}
		}

	}

}// --------- Put_stone ---------

// タイトル
void Title(){
	printf("======================\n");
	printf("=== オセロ プレイヤーVSプレイヤー ===\n");
	printf("======================\n");

	printf("マイナス入力は絶対値になります。\n\n");
	printf("文字入力したらエラーおきます。\n\n");
}// --------- Title ----------

// メイン
int main (void){
	
	Title();

	// 初期石追加
	field[3][3] = BLACK;
	field[3][4] = WHITE;
	field[4][4] = BLACK;
	field[4][3] = WHITE;
	
		
	for(;;){
		Check_stone();
		Draw_field();
		Put_stone();
	}

	return 0;
}// ------- main -----------

yoko
記事: 24
登録日時: 14年前

Re: オセロの反転の考え方について

#6

投稿記事 by yoko » 9年前

斜めについて、特段難しくはありません。
右上の場合は、右と上の反転を合成すればいいだけなので。

例えば、
上の反転の処理が

コード:

for(上;上;上){
    //処理
}
で、右の反転の処理が

コード:

for(右;右;右){
    //処理
}
の場合、
右上は

コード:

for(上,右;上,右;上,右){
    //処理
}
と書けばいいのです。
当然、処理のところは右上になるように変更する必要はありますが。

カンマ演算子は知っているとは思いますが、一応簡単に説明すると、

コード:

処理1,処理2;
と書かれていたら、
処理1が終わった後に処理2を行う
という意味になります。

jun3453
記事: 22
登録日時: 14年前
住所: 東京

Re: オセロの反転の考え方について

#7

投稿記事 by jun3453 » 9年前

斜め処理追加できました!!
走査もバグがあったので治しました。
それに勝敗判定もつけ、多分完成しました!

サポートしてくださったyokoさんありがとうございました!
もしソースにこうした方が良い点などがありましたら指摘お願いします。
また困ったときにここに書き込もうと思います。
その時見かけたらよろしくお願いします。

コード:

#include<stdio.h>
#include<math.h>

#define BLACK 1
#define WHITE -1
#define EMPTY 0
#define PUTOK 2


int field[8][8]; // フィールド座標 X,Y
int turn = BLACK;

bool endflag = false;

int putokcount ;
int bcount , wcount;

// フィールド描画
void Draw_field(){
	int i,j;		 // for用

	bcount = 0;
	wcount = 0;
	putokcount = 0;

	// フィールド表示
	printf(" 12345678\n");
	for(i=0;i<8;i++){
		printf("%d ",i+1);
		for(j=0;j<8;j++){

			// 空き
			if(field[j][i] == EMPTY){
				printf("*");
			}
			
			// 黒
			if(field[j][i] == BLACK){
				printf("○");
				bcount++;
			}
			
			// 白
			if(field[j][i] == WHITE){
				printf("●");
				wcount++;
			}

			// 置ける場所
			if(field[j][i] == PUTOK){
				printf("☆");
				putokcount++;
			}
		}

		
		printf("\n");
	}

	printf("\n");

	if(putokcount == 0){
		printf("どこにも置けません。\n");
		turn *= -1;
	}


} // ---------Draw_field-------------

// 石探し
void Check_stone(){
	int i=0,j=0;   // 盤面探索for
	int x=0,y=0;   // 自色探索for

	// PUTOK初期化
	for(i=0;i<=8;i++){
		for(j=0;j<=8;j++){
			if(field[i][j] == PUTOK){
				field[i][j] = EMPTY;
			}
		}
	}

	// 盤面探索
	for(i=0;i<8;i++){
		for(j=0;j<8;j++){

			// 方向用変数
				int right = field[j+1][i],
					left  = field[j-1][i],
					down  = field[j][i-1],
					up    = field[j][i+1],
					
					right_up   = field[j+1][i-1],
					right_down = field[j+1][i+1],
					left_up	   = field[j-1][i-1],
					left_down  = field[j-1][i+1];

			// 空きがあったとき
			if(field[j][i] == EMPTY){
				// 空きマスから周囲8マス探索
				// その方向の駒が相手のものであり、空きでなく、☆でないものであれば、
				// その方向の自駒が出るまで探索。あれば☆を置く。
			
				// 上下左右
				if(right != turn && right != EMPTY && right != PUTOK){
					for(x=j;x<8;x++){
						if(field[x][i] == turn){
							if(field[x-1][i] == PUTOK || field[x-1][i] == EMPTY){
								break;
							}else{
								field[j][i] = PUTOK;
							}
						}
					}
				}
				
				if(left != turn && left != EMPTY && left != PUTOK){
					for(x=j;0<=x;x--){
						if(field[x][i] == turn){
							if(field[x+1][i] == PUTOK || field[x+1][i] == EMPTY){
								break;
							}else{
								field[j][i] = PUTOK;
							}
						}
					}
				}
				
				if(up != turn && up != EMPTY && up != PUTOK){
					for(y=i;y<=8;y++){
						if(field[j][y] == turn){
							if(field[j][y-1] == PUTOK || field[j][y-1] == EMPTY){
								break;
							}else{
								field[j][i] = PUTOK;
							}						
						}
					}
				}
				
				if(down != turn && down != EMPTY && down != PUTOK){
					for(y=i;0<=y;y--){
						if(field[j][y] == turn){
							if(field[j][y+1] == PUTOK || field[j][y+1] == EMPTY){
								break;
							}else{
								field[j][i] = PUTOK;
							}
						}
					}
				}
				
				// 斜め
				//right_up   = field[j+1][i-1],
				if(right_up != turn && right_up != EMPTY && right_up != PUTOK){
					for(x=j,y=i;x<=8,0<=y;x++,y--){
						if(field[x][y] == turn){
							if(field[x-1][y+1] == PUTOK || field[x-1][y+1] == EMPTY){
								break;
							}else{
								field[j][i] = PUTOK;
							}
						}
					}
				}

				if(right_down != turn && right_down != EMPTY && right_down != PUTOK){
					for(x=j,y=i;x<=8,y<=8;x++,y++){
						if(field[x][y] == turn){
							if(field[x-1][y-1] == PUTOK || field[x-1][y-1] == EMPTY){
								break;
							}else{
								field[j][i] = PUTOK;
							}
						}
					}
				}
				if(left_up != turn && left_up != EMPTY && left_up != PUTOK){
					for(x=j,y=i;0<=x,0<=y;x--,y--){
						if(field[x][y] == turn){
							if(field[x+1][y+1] == PUTOK || field[x+1][y+1] == EMPTY){
								break;
							}else{
								field[j][i] = PUTOK;
							}
						}
					}
				}
				if(left_down != turn && left_down != EMPTY && left_down != PUTOK){
					for(x=j,y=i;0<=x,y<=8;x--,y++){
						if(field[x][y] == turn){
							if(field[x+1][y-1] == PUTOK || field[x+1][y-1] == EMPTY){
								break;
							}else{
								field[j][i] = PUTOK;
							}
						}
					}
				}
			}
		}
	}

}// ------- Check_stone ------------


// 石変更処理
// 引数: sx 置いた石の座標X
//     sy 置いた石の座標Y
void Change_stone(int sx, int sy){
	int i,j;
	int x,y;
	int dx=0,dy=0;

	// 引数置き換え
	j = sx;
	i = sy;

	// 方向用変数
	int right = field[j+1][i],
		left  = field[j-1][i],
		down  = field[j][i-1],
		up    = field[j][i+1],
					
		right_up   = field[j+1][i-1],
		right_down = field[j+1][i+1],
		left_up	   = field[j-1][i-1],
		left_down  = field[j-1][i+1];

	// 空きマスから周囲8マス探索
	// その方向の駒が相手のものであり、空きでなく、☆でないものであれば、
	// 自色が出てくるまで探索、出てきたらひっくり返していく。
	if(right != turn && right != EMPTY && right != PUTOK){
		dx = 0;
		for(x=j;x<8;x++){
			// 敵色のときdx++
			if(field[x][i] != turn){
				dx++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x-1][i] == PUTOK || field[x-1][i] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x][i] == turn){
				while(dx!=0){
					dx--;
					field[x-dx][i] = turn;
				}
			}
		}

	}

	if(left != turn && left != EMPTY && left != PUTOK){
		dx = 0;
		for(x=j;0<x;x--){
			// 敵色のときdx++
			if(field[x][i] != turn){
				dx++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x+1][i] == PUTOK || field[x+1][i] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x][i] == turn){
				while(dx!=0){
					dx--;
					field[x+dx][i] = turn;
				}
			}
		}
	}
		
	if(up != turn && up != EMPTY && up != PUTOK){
		dy=0;
		for(y=i;y<8;y++){
			// 敵色のときdx++
			if(field[j][y] != turn){
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[j][y-1] == PUTOK || field[j][y-1] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[j][y] == turn){
				while(dy!=0){
					dy--;
					field[j][y-dy] = turn;
				}
			}				
		}
	}
		
	if(down != turn && down != EMPTY && down != PUTOK){
		dy=0;
		for(y=i;0<y;y--){
			// 敵色のときdx++
			if(field[j][y] != turn){
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[j][y+1] == PUTOK || field[j][y+1] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[j][y] == turn){
				while(dy!=0){
					dy--;
					field[j][y+dy] = turn;
				}
			}
		}
	}



	if(right_up != turn && right_up != EMPTY && right_up != PUTOK){
		dx=0;
		dy=0;
		for(x=j,y=i;x<8,0<y;x++,y--){
			// 敵色のときdx++
			if(field[x][y] != turn){
				dx++;
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x-1][y+1] == PUTOK || field[x-1][y+1] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x][y] == turn){
				while(dx!=0 && dy!=0){
					dx--;
					dy--;
					field[x-dx][y+dy] = turn;
				}
			}
		}
	}

	if(right_down != turn && right_down != EMPTY && right_down != PUTOK){
		dx=0;
		dy=0;
		for(x=j,y=i;x<8,y<8;x++,y++){
			// 敵色のときdx++
			if(field[x][y] != turn){
				dx++;
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x-1][y-1] == PUTOK || field[x-1][y-1] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x][y] == turn){
				while(dx!=0 && dy!=0){
					dx--;
					dy--;
					field[x-dx][y-dy] = turn;
				}
			}
		}
	}
	
	if(left_up != turn && left_up != EMPTY && left_up != PUTOK){
		dx=0;
		dy=0;
		for(x=j,y=i;0<x,0<y;x--,y--){
			// 敵色のときdx++
			if(field[x][y] != turn){
				dx++;
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x+1][y+1] == PUTOK || field[x+1][y+1] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x][y] == turn){
				while(dx!=0 && dy!=0){
					dx--;
					dy--;
					field[x+dx][y+dy] = turn;
				}
			}
		}
	}
	if(left_down != turn && left_down != EMPTY && left_down != PUTOK){
		dx=0;
		dy=0;
		for(x=j,y=i;0<x,y<8;x--,y++){
			// 敵色のときdx++
			if(field[x][y] != turn){
				dx++;
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x+1][y-1] == PUTOK || field[x+1][y-1] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x][y] == turn){
				while(dx!=0 && dy!=0){
					dx--;
					dy--;
					field[x+dx][y-dy] = turn;
				}
			}
		}
	}
	
}// --------Change_stone ------------

// 石置く処理
void Put_stone(){
	int x,y; // 座標入力用

	if(turn == BLACK){
		printf("○のターンです。\n");
	}else{
		printf("●のターンです。\n");
	}



	printf("石の座標Xを指定してください。\n");
	scanf("%d",&x);
	printf("石の座標Yを指定してください。\n");
	scanf("%d",&y);
	printf("\n");

	// 絶対値にする。
	x = abs(x);
	y = abs(y);

	// 数値が小さい
	if(x == 0 || y == 0){
		printf("======数値が小さすぎます!======\n");
	// 数値が大きい
	}else if(x >= 9 || y >= 9){
		printf("======数値が大きすぎます!======\n");
	// 数値が正しい
	}else{

		// 座標調整
		x = x-1;
		y = y-1;

		// 黒ターン
		if(turn == BLACK){
			// 0のとき
			if(field[x][y] == PUTOK){
				Change_stone(x,y);
				field[x][y] = BLACK; //黒置く
				turn = WHITE;		 //ターン推移
			// 何かあるとき
			}else{
				printf("======そこには置けません。======\n");
			}

		// 白ターン
		}else if(turn == WHITE){
			// 0のとき
			if(field[x][y] == PUTOK){
				Change_stone(x,y);
				field[x][y] = WHITE; //白置く
				turn = BLACK;		 //ターン推移
			// 何かあるとき
			}else{
				printf("======そこには置けません。======\n");
			}
		}

	}

}// --------- Put_stone ---------

void End(){
	int i,j;
	int bcount=0,wcount=0;

	// コマカウント
	for(i=0;i<8;i++){
		for(j=0;j<8;j++){
			// 黒
			if(field[j][i] == BLACK){
				bcount++;
			}
			
			// 白
			if(field[j][i] == WHITE){
				wcount++;
			}
		}
	}

	printf("○ %d 個\n",bcount);
	printf("● %d 個\n",wcount);

	// 黒勝ち
	if(wcount < bcount){	
		printf("======================\n");
		printf("===      ○の勝ち!!     ===\n");
		printf("======================\n");
	// 白勝ち
	}else if(bcount < wcount){
		printf("======================\n");
		printf("===      ●の勝ち!!     ===\n");
		printf("======================\n");
	}


}// ------- End -------------


// タイトル
void Title(){
	printf("======================\n");
	printf("=== オセロ プレイヤーVSプレイヤー ===\n");
	printf("======================\n");

	printf("マイナス入力は絶対値になります。\n\n");
	printf("文字入力したらエラーおきます。\n\n");
}// --------- Title ----------

// メイン
int main (void){
	
	Title();

	// 初期石追加
	field[3][3] = WHITE;
	field[3][4] = BLACK;
	field[4][4] = WHITE;
	field[4][3] = BLACK;
	
	
	while(endflag == false){
		Check_stone();
		Draw_field();
	
		printf("○%d\n●%d\nputok%d\n",bcount,wcount,putokcount);
		if(bcount == 0 || wcount == 0 || bcount+wcount == 0){
			endflag = true;
			break;
		
		}
		Put_stone();
	}

	End();

	return 0;
}// ------- main -----------

yoko
記事: 24
登録日時: 14年前

Re: オセロの反転の考え方について

#8

投稿記事 by yoko » 9年前

1つだけバグがありました。
どこにも置けません
の場合、パスされて次のターンへ移るのですが、
その際に置ける場所に置いても
そこには置けません
と表示され、
再度入力のときは問題ないのですが。

プログラムを細かく見ていませんが、
どこにも置けません
のときに、ターンが移ったあとの
盤面の状態が表示されなかったので、
そこらへんを見るといいかもしれません。

yoko
記事: 24
登録日時: 14年前

Re: オセロの反転の考え方について

#9

投稿記事 by yoko » 9年前

追記です

終了判定が間違っていると思われます。
終了判定は以下のようになります
・8×8の64マスなので、白の数+黒の数≧64のとき終了
・片方がパスのとき、もう片方もパスの場合
・石の色が一色になった場合

これを踏まえて修正するとプログラムは完成すると思います。

※プログラムは自分で何回か試してみてバグがあるかなどを探すと自分で改善点見つけられますよ

jun3453
記事: 22
登録日時: 14年前
住所: 東京

Re: オセロの反転の考え方について

#10

投稿記事 by jun3453 » 9年前

指摘ありがとうございます。

最後の最後でまた躓いちゃいました。

☆→置ける場所
●→黒
○→白

☆●●●●●☆○
こういった並びのところの一番左のおける場所へ置いたら、
○○○○○○○○
のように、無視してひっくりかえってしまいました。

// 初期石追加
field[3][3] = WHITE;
field[3][4] = BLACK;
field[4][4] = WHITE;
field[4][3] = BLACK;

field[1][3] = BLACK;
field[2][3] = BLACK;
field[6][3] = BLACK;

これの後攻で再現できます。
Check_stone と Change_stone
の走査条件がいけないんだと思いますが、わかりませんでした。

もしなにかわかればお願いします。

yoko
記事: 24
登録日時: 14年前

Re: オセロの反転の考え方について

#11

投稿記事 by yoko » 9年前

返信遅くなりました。

バグの原因はここではないかというのは分かりましたが、
プログラムをもう一度全体的に見直すと
修正箇所、改善箇所がありましたので、
修正したプログラムを載せます。

※時間がなかったため、一部しか修正していません。
 また、盤面探索、反転のところ以外の機能(勝者判定等)については
 未実装(未修整)です。
 おそらく作成済みと思いますので、そちらと合体させてください。

修正箇所についてはコメントにて記載しています。
/**/でかかれたコメント部分が該当箇所(もともとのコメントと区別するため)です。

※コードタグで囲んでいますが、BBCodeの仕様なのか、#defineのコメントが正しく色分けされていません。
 Visual Studio2015ではコメントとして問題なく色分け、認識されています。

コード:

#include<stdio.h>
#include<math.h>

#define BLACK 1
#define WHITE -1
#define EMPTY 0
#define PUTOK 2
#define MASU 8 /*盤面の大きさも定義←これに従い各所修正
			     妥当な英語がSQUAREで長いためローマ字表記
			     これにより盤面のサイズ変更が容易に*/


int field[MASU][MASU]; // フィールド座標 X,Y
int turn = BLACK;

bool endflag = false;

int putokcount;
int bcount, wcount;

// フィールド描画
void Draw_field(){
	int i, j;         // for用

	bcount = 0;
	wcount = 0;
	putokcount = 0;

	// フィールド表示
	printf(" 12345678\n");
	for (i = 0; i<MASU; i++){
		printf("%d ", i + 1);
		for (j = 0; j<MASU; j++){

			// 空き
			if (field[j][i] == EMPTY){
				printf("*");
			}

			// 黒
			if (field[j][i] == BLACK){
				printf("○");
				bcount++;
			}

			// 白
			if (field[j][i] == WHITE){
				printf("●");
				wcount++;
			}

			// 置ける場所
			if (field[j][i] == PUTOK){
				printf("☆");
				putokcount++;
			}
		}


		printf("\n");
	}

	printf("\n");

	if (putokcount == 0){
		printf("どこにも置けません。\n");
		turn *= -1;
	}


} // ---------Draw_field-------------

// 石探し
void Check_stone(){
	int i = 0, j = 0;   // 盤面探索for
	int x = 0, y = 0;   // 自色探索for

	// PUTOK初期化
	for (i = 0; i <= MASU; i++){
		for (j = 0; j <= MASU; j++){
			if (field[i][j] == PUTOK){
				field[i][j] = EMPTY;
			}
		}
	}

	// 盤面探索
	for (i = 0; i<MASU; i++){
		for (j = 0; j<MASU; j++){

			// 方向用変数
			int right = field[j + 1][i],
				left = field[j - 1][i],
				down = field[j][i + 1], /*意味的にdownが下隣りを指すのであれば+と-が逆*/
				up = field[j][i - 1],

				right_up = field[j + 1][i - 1],
				right_down = field[j + 1][i + 1],
				left_up = field[j - 1][i - 1],
				left_down = field[j - 1][i + 1];

			// 空きがあったとき
			if (field[j][i] == EMPTY){
				// 空きマスから周囲8マス探索
				// その方向の駒が相手のものであり、空きでなく、☆でないものであれば、
				// その方向の自駒が出るまで探索。あれば☆を置く。

				// 上下左右
				if (right == -1*turn &&!((j+2)>7)){ /*right != turn && right != EMPTY && right != PUTOKは隣(右)が相手の石であると同じ
													  石の捜索は自分の2つ隣からでOKだが、そこが壁だとNG
													  以下同じ*/
					for (x = j+2; x<MASU; x++){
						if (field[x][i] == turn){
							if (field[x + 1][i] == PUTOK || field[x + 1][i] == EMPTY){ /*方向としては++なので-ではなく+*/
								break;
							}
							else if (field[x + 1][i] == turn){ /*自石であればPUTOK*/
								field[j][i] = PUTOK;
								break; /*PUTOKなのだからこの方向の探索終了すべき。ここがバグの原因では?*/
							}
						}
					}
				}

				if (left == -1*turn && !((j-2)<0)){
					for (x = j-2; 0 <= x; x--){
						if (field[x][i] == turn){
							if (field[x + 1][i] == PUTOK || field[x + 1][i] == EMPTY){
								break;
							}
							else{
								field[j][i] = PUTOK;
								break;
							}
						}
					}
				}

				if (up == -1*turn && !((i-2)<0)){
					for (y = i-2; y <= MASU; y--){
						if (field[j][y] == turn){
							if (field[j][y - 1] == PUTOK || field[j][y - 1] == EMPTY){
								break;
							}
							else{
								field[j][i] = PUTOK;
								break;
							}
						}
					}
				}

				if (down != turn && down != EMPTY && down != PUTOK){
					for (y = i; 0 <= y; y--){
						if (field[j][y] == turn){
							if (field[j][y + 1] == PUTOK || field[j][y + 1] == EMPTY){
								break;
							}
							else{
								field[j][i] = PUTOK;
								break;
							}
						}
					}
				}

				// 斜め
				//right_up   = field[j+1][i-1],
				if (right_up != turn && right_up != EMPTY && right_up != PUTOK){
					for (x = j, y = i; x <= MASU, 0 <= y; x++, y--){
						if (field[x][y] == turn){
							if (field[x - 1][y + 1] == PUTOK || field[x - 1][y + 1] == EMPTY){
								break;
							}
							else{
								field[j][i] = PUTOK;
							}
						}
					}
				}

				if (right_down != turn && right_down != EMPTY && right_down != PUTOK){
					for (x = j, y = i; x <= MASU, y <= MASU; x++, y++){
						if (field[x][y] == turn){
							if (field[x - 1][y - 1] == PUTOK || field[x - 1][y - 1] == EMPTY){
								break;
							}
							else{
								field[j][i] = PUTOK;
							}
						}
					}
				}
				if (left_up != turn && left_up != EMPTY && left_up != PUTOK){
					for (x = j, y = i; 0 <= x, 0 <= y; x--, y--){
						if (field[x][y] == turn){
							if (field[x + 1][y + 1] == PUTOK || field[x + 1][y + 1] == EMPTY){
								break;
							}
							else{
								field[j][i] = PUTOK;
							}
						}
					}
				}
				if (left_down != turn && left_down != EMPTY && left_down != PUTOK){
					for (x = j, y = i; 0 <= x, y <= MASU; x--, y++){
						if (field[x][y] == turn){
							if (field[x + 1][y - 1] == PUTOK || field[x + 1][y - 1] == EMPTY){
								break;
							}
							else{
								field[j][i] = PUTOK;
							}
						}
					}
				}
			}
		}
	}

}// ------- Check_stone ------------


// 石変更処理
// 引数: sx 置いた石の座標X
//     sy 置いた石の座標Y
void Change_stone(int sx, int sy){
	int i, j;
	int x, y;
	int dx = 0, dy = 0;

	// 引数置き換え
	j = sx;
	i = sy;

	// 方向用変数
	int right = field[j + 1][i],
		left = field[j - 1][i],
		down = field[j][i - 1],
		up = field[j][i + 1],

		right_up = field[j + 1][i - 1],
		right_down = field[j + 1][i + 1],
		left_up = field[j - 1][i - 1],
		left_down = field[j - 1][i + 1];

	// 空きマスから周囲8マス探索
	// その方向の駒が相手のものであり、空きでなく、☆でないものであれば、
	// 自色が出てくるまで探索、出てきたらひっくり返していく。
	if (right != turn && right != EMPTY && right != PUTOK){
		dx = 0;
		for (x = j; x<MASU; x++){
			// 敵色のときdx++
			if (field[x][i] != turn){
				dx++;
				// 次のがPUTOKだったら抜ける。
			}
			else if (field[x - 1][i] == PUTOK || field[x - 1][i] == EMPTY){
				break;
				// 自色のとき、戻って反転
			}
			else if (field[x][i] == turn){
				while (dx != 0){
					dx--;
					field[x - dx][i] = turn;
				}
			}
		}

	}

	if (left != turn && left != EMPTY && left != PUTOK){
		dx = 0;
		for (x = j; 0<x; x--){
			// 敵色のときdx++
			if (field[x][i] != turn){
				dx++;
				// 次のがPUTOKだったら抜ける。
			}
			else if (field[x + 1][i] == PUTOK || field[x + 1][i] == EMPTY){
				break;
				// 自色のとき、戻って反転
			}
			else if (field[x][i] == turn){
				while (dx != 0){
					dx--;
					field[x + dx][i] = turn;
				}
			}
		}
	}

	if (up != turn && up != EMPTY && up != PUTOK){
		dy = 0;
		for (y = i; y<MASU; y++){
			// 敵色のときdx++
			if (field[j][y] != turn){
				dy++;
				// 次のがPUTOKだったら抜ける。
			}
			else if (field[j][y - 1] == PUTOK || field[j][y - 1] == EMPTY){
				break;
				// 自色のとき、戻って反転
			}
			else if (field[j][y] == turn){
				while (dy != 0){
					dy--;
					field[j][y - dy] = turn;
				}
			}
		}
	}

	if (down != turn && down != EMPTY && down != PUTOK){
		dy = 0;
		for (y = i; 0<y; y--){
			// 敵色のときdx++
			if (field[j][y] != turn){
				dy++;
				// 次のがPUTOKだったら抜ける。
			}
			else if (field[j][y + 1] == PUTOK || field[j][y + 1] == EMPTY){
				break;
				// 自色のとき、戻って反転
			}
			else if (field[j][y] == turn){
				while (dy != 0){
					dy--;
					field[j][y + dy] = turn;
				}
			}
		}
	}



	if (right_up != turn && right_up != EMPTY && right_up != PUTOK){
		dx = 0;
		dy = 0;
		for (x = j, y = i; x<MASU, 0<y; x++, y--){
			// 敵色のときdx++
			if (field[x][y] != turn){
				dx++;
				dy++;
				// 次のがPUTOKだったら抜ける。
			}
			else if (field[x - 1][y + 1] == PUTOK || field[x - 1][y + 1] == EMPTY){
				break;
				// 自色のとき、戻って反転
			}
			else if (field[x][y] == turn){
				while (dx != 0 && dy != 0){
					dx--;
					dy--;
					field[x - dx][y + dy] = turn;
				}
			}
		}
	}

	if (right_down != turn && right_down != EMPTY && right_down != PUTOK){
		dx = 0;
		dy = 0;
		for (x = j, y = i; x<MASU, y<MASU; x++, y++){
			// 敵色のときdx++
			if (field[x][y] != turn){
				dx++;
				dy++;
				// 次のがPUTOKだったら抜ける。
			}
			else if (field[x - 1][y - 1] == PUTOK || field[x - 1][y - 1] == EMPTY){
				break;
				// 自色のとき、戻って反転
			}
			else if (field[x][y] == turn){
				while (dx != 0 && dy != 0){
					dx--;
					dy--;
					field[x - dx][y - dy] = turn;
				}
			}
		}
	}

	if (left_up != turn && left_up != EMPTY && left_up != PUTOK){
		dx = 0;
		dy = 0;
		for (x = j, y = i; 0<x, 0<y; x--, y--){
			// 敵色のときdx++
			if (field[x][y] != turn){
				dx++;
				dy++;
				// 次のがPUTOKだったら抜ける。
			}
			else if (field[x + 1][y + 1] == PUTOK || field[x + 1][y + 1] == EMPTY){
				break;
				// 自色のとき、戻って反転
			}
			else if (field[x][y] == turn){
				while (dx != 0 && dy != 0){
					dx--;
					dy--;
					field[x + dx][y + dy] = turn;
				}
			}
		}
	}
	if (left_down != turn && left_down != EMPTY && left_down != PUTOK){
		dx = 0;
		dy = 0;
		for (x = j, y = i; 0<x, y<MASU; x--, y++){
			// 敵色のときdx++
			if (field[x][y] != turn){
				dx++;
				dy++;
				// 次のがPUTOKだったら抜ける。
			}
			else if (field[x + 1][y - 1] == PUTOK || field[x + 1][y - 1] == EMPTY){
				break;
				// 自色のとき、戻って反転
			}
			else if (field[x][y] == turn){
				while (dx != 0 && dy != 0){
					dx--;
					dy--;
					field[x + dx][y - dy] = turn;
				}
			}
		}
	}

}// --------Change_stone ------------

// 石置く処理
void Put_stone(){
	int x, y; // 座標入力用

	if (turn == BLACK){
		printf("○のターンです。\n");
	}
	else{
		printf("●のターンです。\n");
	}



	printf("石の座標Xを指定してください。\n");
	scanf("%d", &x);
	printf("石の座標Yを指定してください。\n");
	scanf("%d", &y);
	printf("\n");

	// 絶対値にする。
	x = abs(x);
	y = abs(y);

	// 数値が小さい
	if (x == 0 || y == 0){
		printf("======数値が小さすぎます!======\n");
		// 数値が大きい
	}
	else if (x >= MASU+1 || y >= MASU+1){
		printf("======数値が大きすぎます!======\n");
		// 数値が正しい
	}
	else{

		// 座標調整
		x = x - 1;
		y = y - 1;

		// 黒ターン
		if (turn == BLACK){
			// 0のとき
			if (field[x][y] == PUTOK){
				Change_stone(x, y);
				field[x][y] = BLACK; //黒置く
				turn = WHITE;        //ターン推移
				// 何かあるとき
			}
			else{
				printf("======そこには置けません。======\n");
			}

			// 白ターン
		}
		else if (turn == WHITE){
			// 0のとき
			if (field[x][y] == PUTOK){
				Change_stone(x, y);
				field[x][y] = WHITE; //白置く
				turn = BLACK;        //ターン推移
				// 何かあるとき
			}
			else{
				printf("======そこには置けません。======\n");
			}
		}

	}

}// --------- Put_stone ---------

void End(){
	int i, j;
	int bcount = 0, wcount = 0;

	// コマカウント
	for (i = 0; i<MASU; i++){
		for (j = 0; j<MASU; j++){
			// 黒
			if (field[j][i] == BLACK){
				bcount++;
			}

			// 白
			if (field[j][i] == WHITE){
				wcount++;
			}
		}
	}

	printf("○ %d 個\n", bcount);
	printf("● %d 個\n", wcount);

	// 黒勝ち
	if (wcount < bcount){
		printf("======================\n");
		printf("===      ○の勝ち!!     ===\n");
		printf("======================\n");
		// 白勝ち
	}
	else if (bcount < wcount){
		printf("======================\n");
		printf("===      ●の勝ち!!     ===\n");
		printf("======================\n");
	}


}// ------- End -------------


// タイトル
void Title(){
	printf("======================\n");
	printf("=== オセロ プレイヤーVSプレイヤー ===\n");
	printf("======================\n");

	printf("マイナス入力は絶対値になります。\n\n");
	printf("文字入力したらエラーおきます。\n\n");
}// --------- Title ----------

// メイン
int main(void){

	Title();

	// 初期石追加
	/*MASU導入のため計算式化*/
	field[MASU/2-1][MASU/2-1] = WHITE;
	field[MASU/2-1][MASU/2]   = BLACK;
	field[MASU/2][MASU/2]     = WHITE;
	field[MASU/2][MASU/2-1]   = BLACK;


	while (endflag == false){
		Check_stone();
		Draw_field();

		printf("○%d\n●%d\nputok%d\n", bcount, wcount, putokcount);
		if (bcount == 0 || wcount == 0 || bcount + wcount == 0){
			endflag = true;
			break;

		}
		Put_stone();
	}

	End();

	return 0;
}// ------- main -----------

jun3453
記事: 22
登録日時: 14年前
住所: 東京

Re: オセロの反転の考え方について

#12

投稿記事 by jun3453 » 9年前

お忙しい中返信、修正ありがとうございます!
今度こそ解決しました。

色々と試してみたら、

コード:

// Check_stone
				if(right == -1 * turn && !((j+2)>7)){
					for(x=j;x<MASU;x++){	
						if(field[x+1][i] == PUTOK || field[x+1][i] == EMPTY){
							break;
						}
						if(field[x+1][i] == turn){
							field[j][i] = PUTOK;
							break;
						}
					}
				}

コード:

// Change_stone
	if(right = -1 * turn && !((j+2)>7)){
		dx = 0;
		for(x=j;x<MASU;x++){
			// 敵色のときdx++
			if(field[x+1][i] == -1*turn){
				dx++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x+1][i] == PUTOK || field[x][i] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x+1][i] == turn){
				while(dx!=0){
					dx--;
					field[x-dx][i] = turn;
				}
				break;
			}
		}

	}
これでバグの修正が完了しました!
一応完成させたプログラムに単純AIを搭載させたものを完成品のコードとして貼っておきます。
これから色々と修正し、綺麗にして提出したいと思います。

yokoさん、長い間本当にありがとうございました!

コード:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>

#define BLACK 1		// 黒
#define WHITE -1	// 白
#define EMPTY 0		// 盤面に何もない
#define PUTOK 2		// 盤面に何か置ける
#define MASU 8		// 8x8マスの定数


//グローバル変数
int field[MASU][MASU]; // フィールド座標 X,Y
int turn = BLACK; // 初期ターン

int menu_select = 0;	// メニューセレクト用

bool endflag = false;	// 終了フラグ

int putokcount;		// 置ける場所数える
int bcount , wcount;// 黒白数える
// -------- グローバル変数 -------------


// フィールド描画
void Draw_field(){
	int i,j;		 // for用

	// フィールド表示
	printf(" 12345678\n");
	for(i=0;i<MASU;i++){
		printf("%d ",i+1);
		for(j=0;j<MASU;j++){

			// 空き
			if(field[j][i] == EMPTY){
				printf("*");
			}
			
			// 黒
			if(field[j][i] == BLACK){
				printf("○");
				bcount++;
			}
			
			// 白
			if(field[j][i] == WHITE){
				printf("●");
				wcount++;
			}

			// 置ける場所
			if(field[j][i] == PUTOK){
				printf("☆");
				putokcount++;
			}
		}

		
		printf("\n");
	}

	printf("\n");
} // ---------Draw_field-------------

// 石探し
void Check_stone(){
	int i=0,j=0;   // 盤面探索for
	int x=0,y=0;   // 自色探索for

	// PUTOK初期化
	for(i=0;i<MASU;i++){
		for(j=0;j<MASU;j++){
			if(field[i][j] == PUTOK){
				field[i][j] = EMPTY;
			}
		}
	}

	// 盤面探索
	for(i=0;i<MASU;i++){
		for(j=0;j<MASU;j++){

			// 方向用変数
				int right = field[j+1][i],
					left  = field[j-1][i],
					up    = field[j][i-1],
					down  = field[j][i+1],
					
					right_up   = field[j+1][i-1],
					right_down = field[j+1][i+1],
					left_up	   = field[j-1][i-1],
					left_down  = field[j-1][i+1];

			// 空きがあったとき
			if(field[j][i] == EMPTY){
				// 空きマスから周囲8マス探索
				// その方向の駒が相手のものであり、空きでなく、☆でないものであれば、
				// その方向の自駒が出るまで探索。あれば☆を置く。			
				// 上下左右
				if(right == -1 * turn && !((j+2)>7)){
					for(x=j;x<MASU;x++){	
						if(field[x+1][i] == PUTOK || field[x+1][i] == EMPTY){
							break;
						}
						if(field[x+1][i] == turn){
							field[j][i] = PUTOK;
							break;
						}
					}
				}
				
				if(left == -1 * turn && !((j-2)<=0)){
					for(x=j;0<=x;x--){
						if(field[x-1][i] == PUTOK || field[x-1][i] == EMPTY){
							break;
						}
						if(field[x-1][i] == turn){
							field[j][i] = PUTOK;
							break;
						}
					}
				}
				
			
				if(down != turn && down != EMPTY && down != PUTOK){	
					for(y=i;y<=MASU;y++){
						if(field[j][y+1] == PUTOK || field[j][y+1] == EMPTY){
							break;
						}
						if(field[j][y+1] == turn){
							field[j][i] = PUTOK;
							break;
						}
					}
				}
								
				if(up != turn && up != EMPTY && up != PUTOK){			
					for(y=i;0<=y;y--){
						if(field[j][y-1] == PUTOK || field[j][y-1] == EMPTY){
							break;
						}
						if(field[j][y-1] == turn){
							field[j][i] = PUTOK;
							break;
						}
					}
				}
				
				// 斜め
				//right_up   = field[j+1][i-1],
				if(right_up != turn && right_up != EMPTY && right_up != PUTOK){
					for(x=j,y=i;x<=MASU,0<=y;x++,y--){
						if(field[x+1][y-1] == PUTOK || field[x+1][y-1] == EMPTY){
							break;
						}
						if(field[x+1][y-1] == turn){
							field[j][i] = PUTOK;
							break;
						}
					}
				}

				if(right_down != turn && right_down != EMPTY && right_down != PUTOK){
					for(x=j,y=i;x<MASU,y<MASU;x++,y++){
						if(field[x+1][y+1] == PUTOK || field[x+1][y+1] == EMPTY){
							break;
						}
						if(field[x+1][y+1] == turn){
							field[j][i] = PUTOK;
							break;
						}
					}
				}

				if(left_up != turn && left_up != EMPTY && left_up != PUTOK){
					for(x=j,y=i;0<=x,0<=y;x--,y--){
						if(field[x-1][y-1] == PUTOK || field[x-1][y-1] == EMPTY){
							break;
						}
						if(field[x-1][y-1] == turn){
							field[j][i] = PUTOK;
							break;
						}
					}
				}

				if(left_down != turn && left_down != EMPTY && left_down != PUTOK){
					for(x=j,y=i;0<=x,y<MASU;x--,y++){
						if(field[x-1][y+1] == PUTOK || field[x-1][y+1] == EMPTY){
							break;
						}
						if(field[x-1][y+1] == turn){
							field[j][i] = PUTOK;
							break;
						}
					}
				}
			}
		}
	}

}// ------- Check_stone ------------


// 石変更処理
// 引数: sx 置いた石の座標X
//     sy 置いた石の座標Y
void Change_stone(int sx, int sy){
	int i,j;
	int x,y;
	int dx=0,dy=0;

	// 引数置き換え
	j = sx;
	i = sy;

	// 方向用変数
	int right = field[j+1][i],
		left  = field[j-1][i],
		up    = field[j][i-1],
		down  = field[j][i+1],
					
		right_up   = field[j+1][i-1],
		right_down = field[j+1][i+1],
		left_up	   = field[j-1][i-1],
		left_down  = field[j-1][i+1];

	// 空きマスから周囲8マス探索
	// その方向の駒が相手のものであり、空きでなく、☆でないものであれば、
	// 自色が出てくるまで探索、出てきたらひっくり返していく。
	if(right = -1 * turn && !((j+2)>7)){
		dx = 0;
		for(x=j;x<MASU;x++){
			// 敵色のときdx++
			if(field[x+1][i] == -1*turn){
				dx++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x+1][i] == PUTOK || field[x][i] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x+1][i] == turn){
				while(dx!=0){
					dx--;
					field[x-dx][i] = turn;
				}
				break;
			}
		}

	}

	if(left == -1 * turn && !((j-2)<0)){
		dx = 0;
		for(x=j;0<=x;x--){
			// 敵色のときdx++
			if(field[x-1][i] == -1*turn){
				dx++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x-1][i] == PUTOK || field[x+1][i] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x-1][i] == turn){
				while(dx!=0){
					dx--;
					field[x+dx][i] = turn;
				}
				break;
			}
		}
	}
		
	if(down != turn && down != EMPTY && down != PUTOK){
		dy=0;
		for(y=i;y<MASU;y++){
			// 敵色のときdx++
			if(field[j][y+1] == -1*turn){
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[j][y+1] == PUTOK || field[j][y] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[j][y+1] == turn){
				while(dy!=0){
					dy--;
					field[j][y-dy] = turn;
				}
				break;
			}
		}
	}

	if(up != turn && up != EMPTY && up != PUTOK){
		dy=0;
		for(y=i;0<=y;y--){
			// 敵色のときdx++
			if(field[j][y-1] == -1*turn){
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[j][y-1] == PUTOK || field[j][y] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[j][y-1] == turn){
				while(dy!=0){
					dy--;
					field[j][y+dy] = turn;
				}
				break;
			}
		}
	}


	if(right_up != turn && right_up != EMPTY && right_up != PUTOK){
		dx=0;
		dy=0;
		for(x=j,y=i;x<MASU,0<=y;x++,y--){
			// 敵色のときdx++
			if(field[x+1][y-1] == -1*turn){
				dx++;
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x+1][y-1] == PUTOK || field[x][y] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x+1][y-1] == turn){
				while(dx!=0 && dy!=0){
					dx--;
					dy--;
					field[x-dx][y+dy] = turn;
				}
				break;
			}
		}
	}

	if(right_down != turn && right_down != EMPTY && right_down != PUTOK){
		dx=0;
		dy=0;
		for(x=j,y=i;x<MASU,y<MASU;x++,y++){
			// 敵色のときdx++
			if(field[x+1][y+1] == -1*turn){
				dx++;
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x+1][y+1] == PUTOK || field[x][y] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x+1][y+1] == turn){
				while(dx!=0 && dy!=0){
					dx--;
					dy--;
					field[x-dx][y-dy] = turn;
				}
				break;
			}
		}
	}
	
	if(left_up != turn && left_up != EMPTY && left_up != PUTOK){
		dx=0;
		dy=0;
		for(x=j,y=i;0<=x,0<=y;x--,y--){
			// 敵色のときdx++
			if(field[x-1][y-1] == -1*turn){
				dx++;
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x-1][y-1] == PUTOK || field[x-1][y-1] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x-1][y-1] == turn){
				while(dx!=0 && dy!=0){
					dx--;
					dy--;
					field[x+dx][y+dy] = turn;
				}
				break;
			}
		}
	}
	if(left_down != turn && left_down != EMPTY && left_down != PUTOK){
		dx=0;
		dy=0;
		for(x=j,y=i;0<=x,y<MASU;x--,y++){
			// 敵色のときdx++
			if(field[x-1][y+1] == -1*turn){
				dx++;
				dy++;
			// 次のがPUTOKだったら抜ける。
			}else if(field[x-1][y+1] == PUTOK || field[x-1][y+1] == EMPTY){
				break;
			// 自色のとき、戻って反転
			}else if(field[x-1][y+1] == turn){
				while(dx!=0 && dy!=0){
					dx--;
					dy--;
					field[x+dx][y-dy] = turn;
				}
				break;
			}
		}
	}
}// --------Change_stone ------------

// 石置く処理
void Put_stone(){
	int x,y; // 座標入力用

	
	// どこにも置けないとき
	if(putokcount == 0){
		printf("どこにも置けません。\n");
		turn *= -1;
	}

	if(turn == BLACK){
		printf("○のターンです。\n");
	}else{
		printf("●のターンです。\n");
	}

	printf("石の座標Xを指定してください。\n");
	scanf("%d",&x);
	printf("石の座標Yを指定してください。\n");
	scanf("%d",&y);
	printf("\n");

	// 絶対値にする。
	x = abs(x);
	y = abs(y);

	// 数値が小さい
	if(x == 0 || y == 0){
		printf("======数値が小さすぎます!======\n");
	// 数値が大きい
	}else if(x >= MASU+1 || y >= MASU+1){
		printf("======数値が大きすぎます!======\n");
	// 数値が正しい
	}else{

		// 座標調整
		x = x-1;
		y = y-1;

		// 黒ターン
		if(turn == BLACK){
			// どこにも置けないとき
			if(putokcount == 0){
				printf("どこにも置けません。\n");
				turn = WHITE;
			}else{
				// 0のとき
				if(field[x][y] == PUTOK){
					Change_stone(x,y);
					field[x][y] = BLACK; //黒置く
					turn = WHITE;		 //ターン推移
				// 何かあるとき
				}else{
					printf("======そこには置けません。======\n");
				}
			}
		// 白ターン
		}else if(turn == WHITE){
			// どこにも置けないとき
			if(putokcount == 0){
				printf("どこにも置けません。\n");
				turn = BLACK;
			}else{
				// 0のとき
				if(field[x][y] == PUTOK){
					Change_stone(x,y);
					field[x][y] = WHITE; //白置く
					turn = BLACK;		 //ターン推移
				// 何かあるとき
				}else{
					printf("======そこには置けません。======\n");
				}
			}
		}
	}
}// --------- Put_stone ---------


// 単純AI
void tanjunAI(){
	int i,j;
	int rnd;  // 乱数
	bool putflag = false;

	// rnd ==1 になるまで、(置かれるまで)回し続ける。
	while(putflag == false){
		// 置ける場所探索
		for(i=0;i<MASU;i++){
			for(j=0;j<MASU;j++){
				// 8x8上に☆があり、ランダムが1生成されたとき
				if(field[j][i] == PUTOK){

					// srand 毎回起動の際に乱数を変える
					srand((unsigned)time(NULL));
				
					// 乱数を 0 ~ 1 の間で作る
					rnd = rand() % 2;
					// printf("%d",rnd); // 確認用printf
 				
					// ランダムで1が出たとき、PVPと同じような操作をする。
					if(rnd == 1){
						printf("X %d ,Y %d に置きました。\n",j+1,i+1);
						Change_stone(j,i);
						field[j][i] = turn;
						putflag = true;
						i=9;
						j=9;
						break;
					}
				}
			}
		}
	}

}// -------- tanjunAI ---------


// 単純AI戦 石置く処理
void Put_stone_tanjun(){
	int x=0,y=0; // 座標入力用

	// ターン推移
	if(turn == BLACK){
		printf("○のターンです。\n");
	}else{
		printf("●のターンです。\n");
	}

	if(turn == BLACK){
		printf("石の座標Xを指定してください。\n");
		scanf("%d",&x);
		printf("石の座標Yを指定してください。\n");
		scanf("%d",&y);
		printf("\n");

		// 絶対値にする。
		x = abs(x);
		y = abs(y);
			
	}
	// 黒ターン
	if(turn == BLACK){
		// 数値が小さい
		if(x == 0 || y == 0){
			printf("======数値が小さすぎます!======\n");
		// 数値が大きい
		}else if(x >= MASU+1 || y >= MASU+1){
			printf("======数値が大きすぎます!======\n");
		// 数値が正しい
		}else{
			// どこにも置けないとき
			if(putokcount == 0){
				printf("どこにも置けません。\n");
				turn = WHITE;
			}else{
				// 座標調整
				x = x-1;
				y = y-1;

				// 0のとき
				if(field[x][y] == PUTOK){
					Change_stone(x,y);
					field[x][y] = turn; //黒置く
					turn = WHITE;		 //ターン推移
				// 何かあるとき
				}else{
					printf("======そこには置けません。======\n");
				}
			}
		}
	// 白ターン
	}else if(turn == WHITE){
		// どこにも置けないとき
		if(putokcount == 0){
			printf("どこにも置けません。\n");
			turn = BLACK;
		}else{
			tanjunAI();
			turn = BLACK;
		}
	}

}//------- Put_stone_tanjun --------------

void End(){
	int i,j;
	int bcount=0,wcount=0;

	// コマカウント
	for(i=0;i<MASU;i++){
		for(j=0;j<MASU;j++){
			// 黒
			if(field[j][i] == BLACK){
				bcount++;
			}
			
			// 白
			if(field[j][i] == WHITE){
				wcount++;
			}
		}
	}

	printf("○ %d 個\n",bcount);
	printf("● %d 個\n",wcount);

	// 黒勝ち
	if(wcount < bcount){	
		printf("======================\n");
		printf("===      ○の勝ち!!     ===\n");
		printf("======================\n");
	// 白勝ち
	}else if(bcount < wcount){
		printf("======================\n");
		printf("===      ●の勝ち!!     ===\n");
		printf("======================\n");
	}


}// ------- End -------------


// タイトル
void Title(){
	printf("======================\n");
	printf("=== オセロ プレイヤーVSプレイヤー ===\n");
	printf("======================\n");

	printf("マイナス入力は絶対値になります。\n");
	printf("文字入力したらエラーおきます。\n\n");

	printf("~~ ゲームメニュー ~~\n");
	printf("番号を選択してください。\n");
	printf("1  プレイヤーVSプレイヤー\n");
	printf("2 プレイヤーVS単純AI\n");
	
	printf("番号を入力してください→ ");
	scanf("%d",&menu_select);


}// --------- Title ----------

// 先攻か後攻か決める
void senkou(){
	int senkouFlag;

	printf("先攻、後攻を決めてください。\n");
	printf("1 先攻\n");
	printf("2 後攻\n");

	scanf("%d",&senkouFlag);

	// 先攻であれば黒から
	if(senkouFlag == 1){
		turn = 1;
	}else{
		turn = -1;
	}


}// --------- senkou ----------

// メイン
int main (void){
	
	Title();

	// 初期石追加
	field[MASU/2-1][MASU/2-1] = WHITE;
    field[MASU/2-1][MASU/2]   = BLACK;
    field[MASU/2][MASU/2]     = WHITE;
    field[MASU/2][MASU/2-1]   = BLACK;

	
	field[1][3] = BLACK;
	field[2][3] = BLACK;
	field[6][3] = BLACK;
	
	// メニュー1 プレイヤーVSプレイヤー
	if(menu_select == 1){
		// 終了フラグが立っていない間ループ
		while(endflag == false){
			bcount = 0;
			wcount = 0;
			putokcount = 0;
			
			Check_stone();
			Draw_field();
	
			printf("○%d\n●%d\nputok%d\n",bcount,wcount,putokcount);

			// 白黒が0、白黒合計で64のとき終了
			if(bcount == 0 || wcount == 0 || bcount+wcount >= 64){
				endflag = true;
			}

			Put_stone();
		}
	}//---------- menu1 PVP -------------

	// メニュー2 プレイヤーVS単純AI
	if(menu_select == 2){
		senkou();
		// 終了フラグが立っていない間ループ
		while(endflag == false){
			bcount = 0;
			wcount = 0;
			putokcount = 0;
		
			Check_stone();
			Draw_field();
	
			printf("○%d\n●%d\nputok%d\n",bcount,wcount,putokcount);

			// 白黒が0、白黒合計で64のとき終了
			if(bcount == 0 || wcount == 0 || bcount+wcount >= 64){
				endflag = true;
			}

			Put_stone_tanjun();
		}
	}//-------- menu2 PV単純AI -------------

	End();

	return 0;
}// ------- main -----------

閉鎖

“C言語何でも質問掲示板” へ戻る