ページ 11

Othello

Posted: 2013年11月06日(水) 21:38
by klang
http://idehideout.fc2web.com/p/rev/00.html
をみながらオセロゲームを作っている者です。

今、第四課まできました。
ここで質問が多すぎるため、この場を借りて質問することにしました。
すべて答えなくてもかまいません。(答えられると思いますが)
以下の質問の回答よろしくお願いします。

コード:

vec_y[] = {-1,-1,0,1,1,1,0,-1};
vec_x[] = {0,1,1,1,0,-1,-1,-1};

//vecで指定された向きについてひっくり返るコマがあるか確認する
int checkFlip(int y,int x,int turn,int vec)
{
	int flag = 0;
	while(1){
		y += vec_y[vec];
		x += vec_x[vec];
		
		//盤面の外に出ていたら終了
		if( x < 0 || y < 0 || x > BOARDSIZE-1 || y > BOARDSIZE-1) return 0;
		
		//空きマスだったら終了
		if(board[y][x] == NONE) return 0;
		
		//相手のコマがあったらフラグを立てる
		if(board[y][x] == (turn ? BLACK : WHITE)){
			flag = 1;
			continue;
		}
		
		//もしフラグがたっていればループ脱出。いなければ終了
		if(flag == 1) break;
		return 0;
	}
	return 1;
}
1 このcheckFlip関数のvec_y[vec]の[vec]にはなにがいつはいるのでしょうか?

2 turn ? BLACK : WHITEは、turnが0ならWHITE,turnが1ならBLACKなのはなぜでしょうか?turnは条件になっていないと思うのですが。
  条件演算子をつかわない場合どのように書いたらよいのでしょうか?

3 continue;はどこをスキップし、どこへつくのでしょうか?

Re: Othello

Posted: 2013年11月06日(水) 21:43
by みけCAT
1: 他の部分を見ないとわかりません。とりあえず保留にします。

2: 三項演算子がそういう仕様だからです。turnは条件になっています。C言語では0が偽、0以外が真を表します。
条件演算子を使わない場合、一例として

コード:

if((turn && board[y][x]==BLACK) || ((!turn) && board[y][x]==WHITE)) {
だと思います。

3:

コード:

//もしフラグがたっていればループ脱出。いなければ終了
if(flag == 1) break;
return 0;
の部分をスキップして、

コード:

while(1){
の所に戻ります。

Re: Othello

Posted: 2013年11月06日(水) 21:47
by みけCAT
1:演算子の一部だけ取り出されても、何が入るかは答えにくいです。
vecには、コマを見ていく方向を示す0~7の整数が、それぞれcheck関数から呼び出されるときに入るようです。

Re: Othello

Posted: 2013年11月06日(水) 21:59
by klang
C言語では0が偽、0以外が真を表します。
↑これが知らなくてよくわからないままいました。すっきりしました。ありがとうございます。

コード:

//その場所に置くことができるかを確認する関数
int check(int y,int x,int turn)
{
	int vec;
	
	//どれか一方向でもひっくり返るか確認
	for(vec = 0 ; vec < 8 ; ++vec){
		if(checkFlip(y,x,turn,vec) == 1) return 1;
	}
	
	return 0;
}
このような関数がつづきにあるわけですが、これとさきほどのを併用することで、
空きマスであるかと外に出ていないか、
また、八方向のうち一つ以上はひっくり返すところがある(挟んでいる)か
がわかるということでよろしいのでしょうか。

check関数のvexとcheckFlip関数のvecは同じだということでしょうか。
(checkで1をいれたらcheckFlipでも1がはいるというこですか?)


また、とても初歩的な質問かとは思いますが、
自作関数の()のなかの int ~,int ~, は変数を宣言しているということでしょうか。
だとしたら、int vecやint flagが()でないかは引数であるかないかの違いでしょうか。

Re: Othello

Posted: 2013年11月06日(水) 22:38
by みけCAT
klang さんが書きました:このような関数がつづきにあるわけですが、これとさきほどのを併用することで、
空きマスであるかと外に出ていないか、
また、八方向のうち一つ以上はひっくり返すところがある(挟んでいる)か
がわかるということでよろしいのでしょうか。
「八方向のうち一つ以上はひっくり返すところがある(挟んでいる)かがわかる」は正しいです。
「空きマスであるかと外に出ていないかがわかる」は違うと思います。
klang さんが書きました:check関数のvexとcheckFlip関数のvecは同じだということでしょうか。
(checkで1をいれたらcheckFlipでも1がはいるというこですか?)
このプログラムのcheck関数にvexという識別子は無いようですが、
check関数のvecとcheckFlip関数のvecの値は同じになるはずです。
klang さんが書きました:自作関数の()のなかの int ~,int ~, は変数を宣言しているということでしょうか。
関数の宣言の()の中でしたら仮引数の宣言ですが、
苦Cに「引数を使う場合、()の中には、渡された数値を格納する変数を宣言します。」と書いてあるので、
変数を宣言しているということでいいと思います。
klang さんが書きました:だとしたら、int vecやint flagが()でないかは引数であるかないかの違いでしょうか。
「()でないか」とはどういう意味でしょうか?

Re: Othello

Posted: 2013年11月06日(水) 23:02
by klang
checkFlip関数やFlip関数のvecの中身はいつ触れるのでしょうか。
vecで指定されたとありますが、どこで指定しているのかわかりません。
どこで呼び出しているのですか。turnやvecに値は渡さなくてよいのでしょうか。それとも渡しているのでしょうか。

コード:

#include < stdio.h >

//盤面の大きさ
#define BOARDSIZE 8

//状態を定義
#define NONE 0
#define BLACK 1
#define WHITE 2

//盤面
char board[BOARDSIZE][BOARDSIZE];

//向きごとの移動量
vec_y[] = {-1,-1,0,1,1,1,0,-1};
vec_x[] = {0,1,1,1,0,-1,-1,-1};

//初期化関数
void setBoard(void)
{
	int i;
	
	for(i = 0 ; i < BOARDSIZE*BOARDSIZE ; ++i){
		board[i/BOARDSIZE][i%BOARDSIZE] = NONE;
	}
	board[BOARDSIZE/2 - 1][BOARDSIZE/2] = BLACK;
	board[BOARDSIZE/2][BOARDSIZE/2 - 1] = BLACK;
	board[BOARDSIZE/2][BOARDSIZE/2] = WHITE;
	board[BOARDSIZE/2 - 1][BOARDSIZE/2 - 1] = WHITE;
	
}

//盤面表示関数
void disp(void){
	int i,j;
	printf(" ");
	for(i = 0 ; i < BOARDSIZE ; ++i) printf("%2d",i + 1);
	printf("\n");
	
	for(i = 0 ; i < BOARDSIZE ; ++i){
		printf("%d",(i + 1) * 10);
		for(j=0 ; j < BOARDSIZE ; ++j){
			switch(board[i][j]){
			case NONE:
				printf("・");
				break;
			case BLACK:
				printf("○");
				break;
			case WHITE:
				printf("●");
				break;
			default:
				printf("er");
				break;
			}
		}
		printf("\n");
	}
	
}

//vecで指定された向きについてひっくり返るコマがあるか確認する
int checkFlip(int y,int x,int turn,int vec)
{
	int flag = 0;
	while(1){
		y += vec_y[vec];
		x += vec_x[vec];
		
		//盤面の外に出ていたら終了
		if( x < 0 || y < 0 || x > BOARDSIZE-1 || y > BOARDSIZE-1) return 0;
		
		//空きマスだったら終了
		if(board[y][x] == NONE) return 0;
		
		//相手のコマがあったらフラグを立てる
		if(board[y][x] == (turn ? BLACK : WHITE)){
			flag = 1;
			continue;
		}
		
		//もしフラグがたっていればループ脱出。いなければ終了
		if(flag == 1) break;
		return 0;
	}
	return 1;
}

//その場所に置くことができるかを確認する関数
int check(int y,int x,int turn)
{
	int vec;
	
	//どれか一方向でもひっくり返るか確認
	for(vec = 0 ; vec < 8 ; ++vec){
		if(checkFlip(y,x,turn,vec) == 1) return 1;
	}
	
	return 0;
}

//実際に裏返す関数
void flip(int y,int x,int turn,int vec){
	while(1){
		y += vec_y[vec];
		x += vec_x[vec];
		
		//自分のコマがあったら終了
		if(board[y][x] == (turn ? WHITE : BLACK)) break;
		
		//それ以外なら自分のコマで塗りつぶす
		board[y][x] = (turn ? WHITE : BLACK);
	}
}

//入力を受けて裏返せるか確かめる関数
int put(int y,int x,int turn){
	int vec,flag=0;
	
	//空白でなければ終了
	if(board[y][x] != NONE) return 0;
	
	//全方向について確認
	for(vec=0 ; vec < 8 ; ++vec){
		if(checkFlip(y,x,turn,vec) == 1){
			//裏返す
			flip(y,x,turn,vec);
			flag = 1;
		}
	}
	if(flag == 1){
		//この場所にコマを置く
		board[y][x] = (turn ? WHITE : BLACK);
		return 1;
	}
	
	return 0;
}

//入力関数
void input(int turn)
{
	int place = 0,y,x;
	while(1){
		//入力する
		printf(">");
		if(scanf("%d",&place) == 0){
			//数値がとれなかったらバッファをクリアする
			scanf("%*[^\n]%*c");
			printf("input error\n");
			continue;
		}
		
		//数値が範囲内か確認
		if(place < 11 || place > 88){
			printf("input[%d]:error\n",place);
			place = 0;
			continue;
		}
		y = place / 10;
		x = place % 10;
		
		//もう少し詳しく確認
		if( x < 1 || y < 1 || x > 8 || y > 8){
		printf("input[%d]:error\n",place);
			place = 0;
			continue;
		}
		
		if(put(y-1,x-1,turn) == 1) break;
		else printf("input[%d]:can't flip\n",place);
		place = 0;
	}
}

int main(){
	int turn = 0;
	setBoard();
	disp();
	input(turn);
	disp();
	return 0;
	
}
なお、このプログラムがゲームをプレイする分のプログラムです。
(勝敗を決めるなどはあと。ソースをコピペしました)

Re: Othello

Posted: 2013年11月06日(水) 23:10
by みけCAT
checkFlipのvecはcheck関数およびput関数から、
Flipのvecはput関数から指定されています。
「いつ触れるか」の答えは「ソースコード中にvecという識別子が使われている場所で」でしょうか?

vecには前述の関数が勝手に値を渡しているので、前述の関数を呼び出すなら渡す必要はありません。
turnは、check関数やput関数に対し、白の番の処理をするなら真、黒の番の処理をするなら偽を渡せばいいです。
前述の関数はinput関数から呼び出されているので、input関数に前述の値を渡せばいいです。

Re: Othello

Posted: 2013年11月07日(木) 14:13
by klang
引数として値を渡してはいないが、変数そのものを渡している(併用している?)から
()のなかに値ではなく、変数でよいということでしょうか?

Re: Othello

Posted: 2013年11月08日(金) 09:23
by non
klang さんが書きました:引数として値を渡してはいないが、変数そのものを渡している(併用している?)から
()のなかに値ではなく、変数でよいということでしょうか?
なんか勘違いされているような気がします。

コード:

# include <stdio.h>
# include <stdlib.h>
 
void hoge(int val)   //1
{
	printf("%d\n",val);
}
int main(void)
{
	int val=10,a=5;
	hoge(val);		//2
	hoge(a);
	hoge(15);
	return 0;
}
2の関数を呼び出している部分の引数を実引数といい、1の関数の( )の中の引数を仮引数といいます。
関数を呼び出すときに、実引数の値が、仮引数に代入されます。(値渡し)
このとき、仮引数の名前と実引数の名前がたまたま同じだったとしても、別の変数です。同姓同名の別人だと考えて下さい。
式で書けば
仮引数=実引数 の代入が行われますから、上記プログラムでは
val=val  ・・・ちょっと説明に無理があるかも?お許しを。
val=a
val=15
がそれぞれ行われています。
左辺(仮引数)は変数名でないと代入できません。(直接数値では、拙いですね)

Re: Othello

Posted: 2013年11月09日(土) 17:27
by klang
数値扱いになって値を渡せるんですね!
ありがとうございました。