リバーシゲームについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
にゃんこ

リバーシゲームについて

#1

投稿記事 by にゃんこ » 18年前

学校の課題でリバーシ(オセロ)ゲームを作成せよという問題が出たんですが、自分で作ってみてどうしてもうまく動作しないのでどなたか教えてください。お願いします。。
課題の内容は以下の通りです。

リバーシゲームのためのモジュールとその動作を確認するプログラムを作成せよ。ルールは以下の通り。
1.最初に盤の初期状態として8×8の盤上の(4,4)、(5,5)に白の石、(5,4)、(4,5)に黒の石を置き、黒が先手で打ち始める。
2.自分の番がきたら、相手の石を1石以上はさめるところに自分の石を置き、挟んだ相手の石をすべて返して自分の色にする。
3.打てる箇所がない場合はパスをする。
4.盤上に空きがなくなるか双方がパスとなったとき試合は終了。石数が多い方が勝ちとする。

・各マスの状態を定義する列挙型のPointState型を定義すること。定義する状態は以下の3つ。
    NONE,BLACK_STANE,WHITE_STANE
・盤の構造体をBorad型として定義すること。Board型には以下の情報を含めること。
    盤の状態を保持する8×8のPointstate型配列:StateArray[8][8]
    現在の手番を保持するPointState型変数:currentStone
・Board型はboard.hというヘッダファイルにて定義すること。
・board型に対する以下の関数プロトタイプ宣言をboard.hで行うこと。
    盤の初期化を行う関数
    盤の状態表示を行う関数
    盤のx列目y行目のマスの状態を取得する関数
    盤のx列目y行目に石が置けるかどうかを確認し、可能な場合盤を更新する関数
    盤のx+dx列目y+dy行目の増すの石を返せるかどうかを確認し、可能な場合に盤を更新する関数
    盤のx列目y行目のマスの状態を更新する関数
    相手(次の手番)の色を取得する関数
・ユーザに座標を問い合わせること
・座標(0,0)の入力はパス、座標(9,9)の入力は終了とすること。

にゃんこ

Re:リバーシゲームについて

#2

投稿記事 by にゃんこ » 18年前

上の課題のソースはこんな感じです↓↓↓

/* board.h */

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

/* 真(TRUE)か偽(FALSE)かの論理表す boolean 型(列挙型) */
typedef enum{
FALSE, TRUE
}boolean;

/* 盤のマス目の状態を表すPointState型(列挙型)*/
typedef enum{
NONE, BLACK_STONE, WHITE_STONE
}PointState;

/* 現在の手番と盤の状態を表すBoard型(構造体)*/
typedef struct{
PointState stateArray[8][8];
PointState currentStone;
}Board;

/* 盤の初期化(プロトタイプ宣言)*/
void initializeBoard(Board* pBoard);

/* 盤の状態の表示(プロトタイプ宣言)*/
void displayState(Board* pBoard);

/* 盤のx列目y行目のマスの状態を取得(プロトタイプ宣言)*/
PointState getState(Board* pBoard, int x, int y);

/*
* 盤のx列目y行目に石が置けるかどうかを確認し、
* 可能な場合に盤を更新
* (プロトタイプ宣言)
*/
boolean tryPlaceStone(Board* pBoard, int x, int y);

/*
* 盤のx+dx列目y+dy行目のマスの石を返せるかどうかの確認、
* 可能な場合に盤を更新
*(プロトタイプ宣言)
*/
boolean tryReverseNext(Board* pBoard, int x, int y, int dx, int dy);

/* 盤のx列目y行目のマスの状態を更新(プロトタイプ宣言)*/
void setState(Board* pBoard, int x, int y, PointState state);

/* 相手(次の手番)の色の取得(プロトタイプ宣言)*/
PointState getEnemyStone(Board*);




/* 動作を確認するプログラム */

#include "board.h"

void inquiry(Board board);

main()
{
/* Board型の変数boardを宣言 */
Board board;
int x=1,y=1;

/* 盤(board)を初期状態に設定 */
initializeBoard(&board);
/* 盤(board)の状態を表示 */
displayState(&board);

while((x!=0 && y!=0) || (x!=9 && y!=9)){
/* 手番の石(Black)を置く位置を問い合わせる */
printf("黒の石を置く座標を入力してください\n(x,y) = ");
inquiry(board);

/* 手番の石(White)を置く位置を問い合わせる */
printf("白の石を置く座標を入力してください\n(x,y) = ");
inquiry(board);

printf("\n");

}
}

/* 石を置く座標の問い合わせをする関数 */
void inquiry(Board board){
int x, y;

scanf("%d,%d",&x,&y);
if(x==9 && y==9)
exit(EXIT_SUCCESS);
else if(x==0 && y==0)
getEnemyStone;
else{
tryPlaceStone(&board,x,y);
/* 盤(board)の状態を表示 */
displayState(&board);
}
}

にゃんこ

Re:リバーシゲームについて

#3

投稿記事 by にゃんこ » 18年前

同じく上のモジュールです↓↓↓(長くてすいません……!!)
/* board.c */

#include "board.h"

/*
* 盤の初期化(実装)
*/
void initializeBoard(Board* pBoard) {
int x, y;

/* 最初の手番を黒に設定 */
pBoard->currentStone = BLACK_STONE;

/* 盤上の石を全てクリア(NONEと設定) */
for(x=1; x<9; x++)
for(y=1; y<9; y++)
setState(pBoard, x, y, NONE);
/* 盤を初期状態にするために石を置く */
setState(pBoard, 4, 4, WHITE_STONE);
setState(pBoard, 5, 4, BLACK_STONE);
setState(pBoard, 4, 5, BLACK_STONE);
setState(pBoard, 5, 5, WHITE_STONE);
}

/*
* 盤の状態の表示(実装)
*/
void displayState(Board* pBoard) {
int x, y;
char stone;

printf("\n 12345678\n"); /* 列番号の表示 */
for (y = 1; y < 9; y++) {
printf("%d", y); /* 行番号の表示 */
for (x = 1; x < 9; x++) {
/* x列目、y行目のマスの状態 */
PointState state = getState(pBoard, x, y);
switch (state) {/* 状態に応じて表示を切り替え */
case NONE: stone = '.'; break;
case BLACK_STONE: stone = 'B'; break;
case WHITE_STONE: stone = 'W'; break;
default: stone = 'E';
}
printf("%c",stone);
}
printf("\n");
}
printf("\n");
/* 現在の手番の色の表示 */
printf("現在の手番: %s",
pBoard->currentStone == BLACK_STONE ? "黒(B)" : "白(W)" );
printf("\n");
}

/*
* 盤のx列目y行目のマスの状態を取得(実装)
*/
PointState getState(Board* pBoard, int x, int y){

if (x < 1 || x > 8 || y < 1 || y > 8) {/* マスの範囲外であればエラー */
printf("xとyは、0 以上かつ9以下の値でなければなりません。\n");
exit(EXIT_FAILURE);
}

/* 盤のx列目、y行目の状態をリターン */
return pBoard->stateArray[x-1][y-1];
}

/*
* 盤のx列目y行目に石が置けるかどうかを確認し、
* 可能な場合に盤を更新(実装)
*/
boolean tryPlaceStone(Board* pBoard, int x, int y) {

boolean trial;

if ( getState(pBoard, x, y) != NONE )
return FALSE;

/* 補足:ボードの範囲を考慮にいれて、境界処理も行うと良い。*/

/* 石を置こうとする場所の隣を返せるかどうかを確認 */
trial = FALSE;
trial = trial | tryReverseNext(pBoard, x, y, +1, 0); /* 右 */
trial = trial | tryReverseNext(pBoard, x, y, 0, +1); /* 下 */
trial = trial | tryReverseNext(pBoard, x, y, -1, 0); /* 左 */
trial = trial | tryReverseNext(pBoard, x, y, 0, -1); /* 上 */
trial = trial | tryReverseNext(pBoard, x, y, +1, +1); /* 右上 */
trial = trial | tryReverseNext(pBoard, x, y, +1, -1); /* 右下 */
trial = trial | tryReverseNext(pBoard, x, y, -1, +1); /* 左上 */
trial = trial | tryReverseNext(pBoard, x, y, -1, -1); /* 左下 */

if (trial) { /* 隣の石を返すことができる場合*/
/* 石を置いてボードの状態を更新する。*/
setState(pBoard, x, y, pBoard->currentStone);
/* 手番を相手に渡す */
pBoard->currentStone = getEnemyStone(pBoard);
}

return trial;
}

/*
* 盤のx+dx列目y+dy行目のマスの石を返せるかどうかの確認、
* 可能な場合に盤を更新(実装)
*/
boolean tryReverseNext(Board* pBoard, int x, int y, int dx, int dy) {

/* 隣の石が相手の石であるかどうかの確認 */
if(getState(pBoard, x + dx, y + dy) == getEnemyStone(pBoard)) {
/* 隣の隣の石が自分の石であるかどうかの確認 */

if((x+dx)<1 || (x+dx)>8 || (y+dy)<1 || (y+dy>8)){
return FALSE;
} /* x+dx、y+dyがマスの範囲内にあるかどうかの判定 */

if (getState(pBoard, x+dx+dx, y+dy+dy) == pBoard->currentStone ) {
/* 隣の石が相手で、隣の隣の石が自分である場合、
石を返して盤を更新 */

if((x+dx+dx)<1 || (x+dx+dx)>8 || (y+dy+dy)<1 || (y+dy+dy)>8){
return FALSE;
} /* x+dx+dx、y+dy+dy1がマスの範囲内のあるかどうかの判定 */

setState(pBoard, x+dx, y+dy, pBoard->currentStone);
return TRUE;
}
else
return tryReverseNext(pBoard, x+dx, y+dy ,dx, dy);
}
else
return FALSE;
}

/*
* 盤のx列目y行目のマスの状態を更新(実装)
*/
void setState(Board* pBoard, int x, int y, PointState state) {
/* 盤のx列目y行目のマスの状態を与えられた状態に更新 */
pBoard->stateArray[x-1][y-1] = state;
}

/*
* 相手(次の手番)の色の取得(実装)
*/
PointState getEnemyStone(Board* pBoard) {
/* 相手(次の手番)の色の取得 */
return pBoard->currentStone == BLACK_STONE ? WHITE_STONE : BLACK_STONE;
}

にゃんこ

Re:リバーシゲームについて

#4

投稿記事 by にゃんこ » 18年前

えと、上のプログラムで実行すると黒の石はうまく置けるんですが、白石を置く座標を入力しても何故か初期状態の盤が表示されてしまいます。
どこがいけないんでしょうか??

組木紙織

Re:リバーシゲームについて

#5

投稿記事 by 組木紙織 » 18年前

ざっと見ただけですが、
>void inquiry(Board board);

この関数はポインタを渡すべきではないでしょうか?
リバーシの版面はコピーで渡す必要は無いはずです。

//リバーシは、コンソールではないですが、私も作りました。
//完成頑張ってください。

にゃんこ

Re:リバーシゲームについて

#6

投稿記事 by にゃんこ » 18年前

回答ありがとうございます!!
確かにポインタ渡しにしたら白石もちゃんと表示されるようになりました♪♪

あと、また別の部分の問題になってしまって申し訳ないんですが、
白も黒も間に挟むのが1石ならちゃんとひっくり返してくれるんですが、2石以上になると

 12345678
1 ........
2 ........
3 ........
4 ..BBB...
5 ..BBW...
6 ..B.....
7 ........
8 ........

の状態から(2,5)に白石を置こうとすると

 12345678
1 ........
2 ........
3 ........
4 ..BBB...
5 .WBWW...
6 ..B.....
7 ........
8 ........

とゆう風になってうまく返せません。
上のboard.cのtryReverseNext関数の中がいけないというのは分かるんですが、
やっぱりここもどんな風に直せばいいのかよく分かりません。

分からないとこばっかりですいませんが、教えてください!!
お願いします。。

組木紙織

Re:リバーシゲームについて

#7

投稿記事 by 組木紙織 » 18年前

再帰を使っているのですね。
慣れないと結構難しいですよ。
tryReverseNextの関数の中の、

return tryReverseNext(pBoard, x+dx, y+dy ,dx, dy);
の部分を少し変更すれば、よくなります。

このままだと、ひっくり返す関数が1度しか呼ばれないので、return で返す前に
もう一工夫必要です。

閉鎖

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