ページ 11

助けてください

Posted: 2012年10月03日(水) 17:06
by daisuke
このC言語のオセロプログラムで盤に評価値をつけるにはどうすればよいのでしょうか?お手数ですが教えてもらえませんか?

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

#define M 10 //盤の横の長さ 実際は壁の分をひいた M - 2が実際の長さ
#define N 10 //盤の縦の長さ 実際の長さは N -2
#define BLACK 1 //黒の石をdataでは1と表す
#define EMPTY 0 //石なし
#define WHITE -1//白の石をdataでは-1と表す
//各関数の宣言
void AI_comp(int data[M][N] , int pl, int op);
void turn(int x, int y, int data[M][N] , int pl, int op);
int place_ability(int x, int y, int data[M][N], int pl, int op);
void board_output(int data[M][N]);
int pass(int data[M][N], int pl, int op);




int main()
{
int i, j,k;
int player_pass_flag = 0, comp_pass_flag = 0;
int player, comp, fors, count, bl = 0, wh = 0;
int data[M][N];
FILE *fp;
fp = fopen("test.dat","a");

printf("●(白)が先行ならば0、○(黒)が先行なら1を入力してください。");
scanf_s("%d", &fors);

for(k = 1 ; k < 2; k++){

player_pass_flag = 0;
comp_pass_flag = 0;

for(i = 0; i < M; i ++){
for(j = 0; j < N; j ++){
if(i != 0 && j != 0 && i != M - 1 && j != N - 1)
data[j] = 0;
else data[j] = 5;
}
}
// 初めに黒白2つずつの石を配置
data[M / 2 - 1][M / 2 - 1] = data[M / 2][M / 2] = BLACK;
data[M / 2][M / 2 - 1] = data[M / 2 - 1][M / 2] = WHITE;

//初期状態の盤の状態を表示
board_output(data);

if(fors == 0){
player = BLACK;
comp = WHITE;
count = 0;
}
else{
player = WHITE;
comp = BLACK;
count = 0;
}

for(i = 0; i < (M -2) * (N -2) - 4; i ++){
INPUT:
if(player_pass_flag == 1 && comp_pass_flag == 1)
goto END; //二人ともパスだったら終了
if(count == 0){ //プレーヤー(対戦相手)の手
if(pass(data, player, comp) == 1){
printf("Computer パスします\n");
comp_pass_flag = 1; //パスしたときはこの変数を1とする
count = 0;
goto INPUT;
}

player_pass_flag =1;
printf("computer\n");
AI_comp(data, player, comp);
count = 1;
}


else{ //コンピュータの手
if(pass(data, comp, player) == 1){
printf("Computer パスします\n");
comp_pass_flag = 1; //パスしたときはこの変数を1とする
count = 1;
goto INPUT;
}

player_pass_flag =1;
printf("computer\n");
AI_comp(data, comp, player); //意思決定関数
count = 0;
}

//盤の状態の表示
board_output(data);
}
//勝敗がついたら盤の状態の表示
END:
printf("\n***最終局面***\n\n");
board_output(data);

for(i = 1; i < M - 1; i ++){
for(j = 1; j < N -1; j ++){

if (data[j] == WHITE) wh ++;
else if (data[j] == BLACK) bl ++;
}
}

fprintf(fp,"%4.d :",k);
printf("● = %2d, ○ = %2d\n\n", bl, wh);
fprintf(fp,"● = %2d, ○ = %2d", bl, wh);

if(bl == wh){
printf("引き分けです。\n");
fprintf(fp," 引き分けです。");
if(fors==0){
fprintf(fp," 先行は●(白)\n");
} else {
fprintf(fp," 先行は○(黒)\n");
}
}

else if((fors == 0 && bl > wh) || (fors == 1 && bl > wh)){
if(fors==0){
fprintf(fp," 先行は●(白)");
} else {
fprintf(fp," 先行は○(黒)");
}
printf("●(白)の勝ちです。\n");
fprintf(fp," ●(白)の勝ちです。\n");
}
else {
if(fors==1){
fprintf(fp," 先行は○(黒)");
} else {
fprintf(fp," 先行は●(白)");
}
printf("○(黒)の勝ちです。\n");
fprintf(fp," ○(黒)の勝ちです。\n");
}
bl = 0;
wh = 0;
}
fclose(fp);
return 0;
}

//次の一手を決定する関数
void AI_comp(int data[M][N], int pl, int op)
{

/*実際にはここからの部分を改変する。
下のソースは置けるマスの中から次の一手をランダムに選択している。*/

int xc = 0, yc = 0;
long long int t;
time(&t);
srand((unsigned int)t);



while(place_ability(xc, yc, data, pl, op) == 0){

//xc,ycにコンピュータの置く石の座標を代入する
xc = 1 + (double)rand() / RAND_MAX * (M - 2);
yc = 1 + (double)rand() / RAND_MAX * (N - 2);
}

//ここまで
turn(xc, yc, data, pl, op);
printf("(%d, %d)\n", xc, yc);
}
//石をひっくり返す関数 石を置く座標(x,y)を引数とする。
void turn(int x, int y, int data[M][N], int pl, int op)
{
int p, q, i, j;
int x2, y2; //(x,y)と(x2,y2)の二つの味方の石で、相手の石をはさむ。

data[x][y] = pl; /*まず、(x,y)に味方の石を置く。
plは、この関数がplayerから呼び出されればplayerの石の色
Computerから呼び出されればComputerの石の色のこと*/



for (p = -1; p <= 1; p ++){
for(q = -1; q <=1; q++){
if (p == 0 && q == 0) continue;
if (data [x + p] [y + q] != op) continue;
i = x + p;
j = y + q;
while (data[j] == op){
i += p;
j += q;
}

x2 = i;
y2 = j;

//(x2,y2)に味方の石があれば、間の相手の石がとれる。
//(x,y)から出発して、(p,q)ずつ進みながら、(x2,y2)に到達するまで、
//相手の石をひっくり返していく。

if (data[x2][y2] == pl){
for(i= x + p, j= y + q; !((i == x2) && (j == y2)) ; i += p, j += q){
data[j] = pl;
}
}
}
}

if (data[i = x + p][j = y + q] != op) return;
while (data[j] == op){
i = i + p;
j = j + q;
}
}

int place_ability(int x, int y, int data[M][N], int pl, int op)
{
int i, j, p, q;

//そこが空欄でないなら、置けない。
if(data[x][y] != 0) return 0;
//空欄で、1枚でもとれるなら置ける。

else{

//上下左右斜めの、8方向について、とれるか調べる
for(p = -1; p <= 1; p++){
for(q = -1; q <= 1; q ++){
if(p == 0 && q == 0) continue;

//隣りに相手の石がないと、とれない。
if (data[x + p][y + q] != op) continue;
//相手の石が続く限り、(p,q)の方向に進んでいく。
i = x + p; j = y + q;
while(data[j] == op) {i += p; j += q;}

//今、(i,j)は(p,q)方向は端の座標。
//そこに味方の石があれば、間の敵石をとれる=(x,y)における。
if (data[j] == pl) return 1;
}
}
//8方向調べたものの、敵石がとれなかった場合、
//(x,y)には置けないことになる。

return 0;
}
}
// 盤の状態の表示
void board_output(int data[M][N])
{
int i, j;

printf(" 1 2 3 4 5 6 7 8\n");
for(i = 1; i < M - 1; i ++){
printf("%d", i);
for(j = 1; j < N - 1; j ++){
switch (data[j]){
case 1: printf("●");
break;
case -1: printf("○");
break;
default: printf("・");
break;
}
}
printf("\n");
}
printf("\n");
}

int pass(int data[M][N], int pl, int op) {
//返り値が0: パスしない。どこかにおける。
// 1: パスする。どこにもおけない。


int i, j;

for (i = 1; i < M - 1; i ++){
for(j = 1; j < N - 1; j++){
if (place_ability(i, j, data, pl, op) == 1){
return 0; //置ける場所が見つかった = パスしない
}
}
}
return 1;
}

Re: 助けてください

Posted: 2012年10月03日(水) 19:23
by softya(ソフト屋)
フォーラムルールの確認とcodeタグの利用をお願いします。
それと評価値とはどういうものを考えているのか説明してもらえますか?
あと課題かどうかも書き込んで頂けますか。

Re: 助けてください

Posted: 2012年10月03日(水) 20:36
by daisuke
漠然な質問で申し訳ありませんでした。私は現在オセロのプログラムに興味を持っていて、下のソースのようなランダム関数を用いたプログラムでコンピューター同士で対局させるところまではできたのですが、片方のコンピューターに10×10のマスそれぞれに点数をつけて、そこに自分の石があればその点数を足して、相手の点数があれば点数を引くとういう評価値をつけたいのですが、この先どうしていいのかわかりません。お手数ですがアドバイスをお願いします。ちなみにC++で作っています。

コード:

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

#define M 10     //盤の横の長さ 実際は壁の分をひいた M - 2が実際の長さ
#define N 10     //盤の縦の長さ 実際の長さは N -2
#define BLACK 1 //黒の石をdataでは1と表す
#define EMPTY 0  //石なし
#define WHITE -1//白の石をdataでは-1と表す
//各関数の宣言
void AI_comp(int data[M][N] , int pl, int op);
void turn(int x, int y, int data[M][N] , int pl, int op);
int place_ability(int x, int y, int data[M][N], int pl, int op);
void board_output(int data[M][N]);
int pass(int data[M][N], int pl, int op);

 // from AI_comp


int board_output[M][N] = {
			{0,   0,    0,   0,   0,   0,    0,  0,      0,  0},
			{0, 120, -20,  20,   5,   5,  20, -20, 120,  0},
			{0, -20, -40,  -5,  -5,  -5,  -5, -40,  -20,  0},
			{0,  20,  -5,  15,   3,   3,  15,   -5,   20,  0},
			{0,    5,  -5,   3,   3,    3,   3,   -5,    5,  0},
			{0,    5,  -5,   3,   3,    3,   3,   -5,    5,  0},
			{0,  20,  -5,  15,   3,    3,  15,   -5,  20,  0},
			{0, -20, -40,  -5,  -5,  -5,  -5,  -40, -20,  0},
			{0, 120, -20,  20,   5,   5,  20, -20, 120,  0},
		        {0,    0,    0,   0,   0,    0,   0,    0,    0,  0},
		
		};
		


int main()
{
	int i, j,k;
	int player_pass_flag = 0, comp_pass_flag = 0;
	int player, comp, fors, count, bl = 0, wh = 0;
    int data[M][N];
	FILE *fp;
	fp = fopen("test.dat","a");

	printf("●(白)が先行ならば0、○(黒)が先行なら1を入力してください。");
	scanf_s("%d", &fors);

	for(k = 1 ; k < 2; k++){

		player_pass_flag = 0;
		comp_pass_flag = 0;

	for(i = 0; i < M; i ++){
		for(j = 0; j < N; j ++){
			if(i != 0 && j != 0 && i != M - 1 && j != N - 1)
				data[i][j] = 0;
			else data[i][j] = 5;
		}
	}
    // 初めに黒白2つずつの石を配置
	data[M / 2 - 1][M / 2 - 1] = data[M / 2][M / 2] = BLACK;
	data[M / 2][M / 2 - 1] = data[M / 2 - 1][M / 2] = WHITE;
    
	//初期状態の盤の状態を表示
	board_output(data);

	if(fors == 0){
		player = BLACK;
		comp = WHITE;
		count = 0;
	}
	else{
	player = WHITE;
	comp   = BLACK;
	count  = 0;
	}

	for(i = 0; i < (M -2) * (N -2) - 4; i ++){
    INPUT:
		if(player_pass_flag == 1 && comp_pass_flag == 1)
			goto END; //二人ともパスだったら終了
		if(count == 0){ //プレーヤー(対戦相手)の手
			if(pass(data, player, comp) == 1){
			  printf("Computer パスします\n");
			  comp_pass_flag = 1; //パスしたときはこの変数を1とする
			  count = 0;
			  goto INPUT;
			}
		
		player_pass_flag =1;
			printf("computer\n");
		AI_comp(data, player, comp);
		count = 1;
		}

	
	else{       //コンピュータの手
			if(pass(data, comp, player) == 1){
			  printf("Computer パスします\n");
			  comp_pass_flag = 1; //パスしたときはこの変数を1とする
			  count = 1;
			  goto INPUT;
			}
		
		player_pass_flag =1;
			printf("computer\n");
		AI_comp(data, comp, player); //意思決定関数
		count = 0;
		}
	       
           //盤の状態の表示
			board_output(data);
		}
       //勝敗がついたら盤の状態の表示
        END:
		printf("\n***最終局面***\n\n");
		board_output(data);

		for(i = 1; i < M - 1; i ++){
			for(j = 1; j < N -1; j ++){
	
				if (data[i][j] == WHITE) wh ++;
				else if (data[i][j] == BLACK) bl ++;
			}
		}

		fprintf(fp,"%4.d :",k);
		printf("● = %2d, ○ = %2d\n\n", bl, wh);
		fprintf(fp,"● = %2d, ○ = %2d", bl, wh);

		if(bl == wh){
			printf("引き分けです。\n");
			fprintf(fp," 引き分けです。");
			if(fors==0){
				fprintf(fp," 先行は●(白)\n");
			} else {
				fprintf(fp," 先行は○(黒)\n");
			}
		}

		else if((fors == 0 && bl > wh) || (fors == 1 && bl > wh)){
			if(fors==0){
				fprintf(fp," 先行は●(白)");
			} else {
				fprintf(fp," 先行は○(黒)");
			}
			printf("●(白)の勝ちです。\n");
			fprintf(fp," ●(白)の勝ちです。\n");
		}
		else {
			if(fors==1){
				fprintf(fp," 先行は○(黒)");
			} else {
				fprintf(fp," 先行は●(白)");
			}
			printf("○(黒)の勝ちです。\n");
			fprintf(fp," ○(黒)の勝ちです。\n");
		}
		bl = 0;
		wh = 0;
	}
		fclose(fp);
		return 0;
	}

	//次の一手を決定する関数
	void AI_comp(int data[M][N], int pl, int op)
	{
		
		/*実際にはここからの部分を改変する。
		  下のソースは置けるマスの中から次の一手をランダムに選択している。*/
		
		int xc = 0, yc = 0;
        long long int t;
		time(&t);
		srand((unsigned int)t);



         while(place_ability(xc, yc, data, pl, op) == 0){ 
         
         //xc,ycにコンピュータの置く石の座標を代入する
	    xc = 1 + (double)rand() / RAND_MAX * (M - 2);
		yc = 1 + (double)rand() / RAND_MAX * (N - 2);
		}
       
		//ここまで
		turn(xc, yc, data, pl, op);
		printf("(%d, %d)\n", xc, yc);
	}
	//石をひっくり返す関数 石を置く座標(x,y)を引数とする。
	void turn(int x, int y, int data[M][N], int pl, int op)
	{
		int p, q, i, j;
		int x2, y2;        //(x,y)と(x2,y2)の二つの味方の石で、相手の石をはさむ。

		data[x][y] = pl; /*まず、(x,y)に味方の石を置く。
						 plは、この関数がplayerから呼び出されればplayerの石の色
						 Computerから呼び出されればComputerの石の色のこと*/
						 


		for (p = -1; p <= 1; p ++){
			for(q = -1; q <=1; q++){
				if (p == 0 && q == 0) continue;
				if (data [x + p] [y + q] != op) continue;
				i = x + p;
				j = y + q;
				while (data[i][j] == op){
					i += p;
					j += q;
				}

				x2 = i;
				y2 = j;

				//(x2,y2)に味方の石があれば、間の相手の石がとれる。
				//(x,y)から出発して、(p,q)ずつ進みながら、(x2,y2)に到達するまで、
				//相手の石をひっくり返していく。

				if (data[x2][y2] == pl){
					for(i= x + p, j= y + q; !((i == x2) && (j == y2)) ; i += p, j += q){
						data[i][j] = pl;
					}
				}
			}
		}

		if (data[i = x + p][j = y + q] != op) return;
		while (data[i][j] == op){
			i = i + p;
			j = j + q;
		}
	}

	int place_ability(int x, int y, int data[M][N], int pl, int op)
	{
		int i, j, p, q;
        
       //そこが空欄でないなら、置けない。
		if(data[x][y] != 0) return 0;
       //空欄で、1枚でもとれるなら置ける。

		else{

			//上下左右斜めの、8方向について、とれるか調べる
			for(p = -1; p <= 1; p++){
				for(q = -1; q <= 1; q ++){
					if(p == 0 && q == 0) continue;
                    
					//隣りに相手の石がないと、とれない。
					if (data[x + p][y + q] != op) continue;
                    //相手の石が続く限り、(p,q)の方向に進んでいく。
					i = x + p; j = y + q;
					while(data[i][j] == op) {i += p; j += q;}
                     
					//今、(i,j)は(p,q)方向は端の座標。
					//そこに味方の石があれば、間の敵石をとれる=(x,y)における。
					if (data[i][j] == pl) return 1;
				}
			}
			//8方向調べたものの、敵石がとれなかった場合、
			//(x,y)には置けないことになる。

			return 0;
		}
	}
    // 盤の状態の表示
	void board_output(int data[M][N])
	{
		int i, j;

		printf(" 1 2 3 4 5 6 7 8\n");
		for(i = 1; i < M - 1; i ++){
			printf("%d", i);
			for(j = 1; j < N - 1; j ++){
				switch (data[i][j]){
				case 1:  printf("●");
				         break;
				case -1: printf("○");
					     break;
				default: printf("・");
					     break;
				}
			}
			printf("\n");
		}
		printf("\n");
	}

	int pass(int data[M][N], int pl, int op) {
		//返り値が0: パスしない。どこかにおける。
		//        1: パスする。どこにもおけない。


		int i, j;

		for (i = 1; i < M - 1; i ++){
			for(j = 1; j < N - 1; j++){
				if (place_ability(i, j, data, pl, op) == 1){
					return 0;    //置ける場所が見つかった = パスしない
				}
			}
		}
		return 1;
	}

Re: 助けてください

Posted: 2012年10月03日(水) 21:24
by softya(ソフト屋)
このプログラムはなにかベースとなっているものがありますか?サイトか書籍名を教えて下さい。

あとお聞きしたいこととお願い、
・C++ではなくC言語の文法で書かれていますが自覚されていますか?
・関数名と変数名が衝突しています。ぜひ直してください。
・インデント(字下げ)が狂っている部分があります。正確なインデントを行なってください。
・ここまで出来てわからないのはどの部分でしょうか?
「10×10のマスそれぞれに点数をつけて、そこに自分の石があればその点数を足して、相手の点数があれば点数を引く」
をさらに細かく説明を分解してみてください。書く時の注意点は誰が何に対して行うことか不明確なことを無くすことです。
「そこ」「その点数」「相手の点数」これらは場所が不明確だったり、情報の作られる経緯が不明確です。
なので、ここからプログラムを作ることはできません。ここをもっと掘り下げましょう。