ページ 1 / 1
C言語のオセロプログラムについて
Posted: 2014年7月31日(木) 18:52
by わらわら
[1] 質問文
[1.1] 自分が今行いたい事は何か
いずれRTSのゲームを作れるようになりたいと思い、こちらのトピックを参考にしてオセロプログラムを組むことにしています。
http://dixq.net/forum/viewtopic.php?f=3&t=8520
[1.2]
http://idehideout.fc2web.com/p/rev/00.html
こちらのサイトのオセロプログラムを参考にしながらDXライブラリで実行できるように改造しつつプログラムを作っています。
素材
コード:
#include "DxLib.h"
//盤面の大きさ
#define BOARDSIZE 8
//状態を定義
#define NONE 0
#define BLACK 1
#define WHITE 2
int put(int x, int y, int turn);
int checkFlip(int sx, int sy, int turn, int vec);
void flip(int x, int y, int turn, int vec);
int sx = 0, sy = 0;
int Green;
int sentaku,sentaku_se;
int i, j;
int chip[3];
int vec_y[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
int vec_x[] = { 0, 1, 1, 1, 0, -1, -1, -1 };
int moveflag = 0;
//白のターン:0 黒のターン:1
int turn = 0;
int vec = 0;
int flag = 0;
int Key[256];
//盤面
int board[BOARDSIZE][BOARDSIZE];
// キーの入力状態を更新する
int gpUpdateKey(){
char tmpKey[256]; // 現在のキーの入力状態を格納する
GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
for (int i = 0; i<256; i++){
if (tmpKey[i] != 0){ // i番のキーコードに対応するキーが押されていたら
Key[i]++; // 加算
}
else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
//初期化関数
void setBoard(void)
{
int i,j;
for (i = 0; i < BOARDSIZE; i++){
for (j = 0; j < BOARDSIZE; j++){
board[i][j] = NONE;
board[BOARDSIZE / 2 - 1][BOARDSIZE / 2] = BLACK; //[3][4]
board[BOARDSIZE / 2][BOARDSIZE / 2 - 1] = BLACK; //[4][3]
board[BOARDSIZE / 2][BOARDSIZE / 2] = WHITE; //[4][4]
board[BOARDSIZE / 2 - 1][BOARDSIZE / 2 - 1] = WHITE; //[3][3]
}
}
}
//盤面表示関数
void disp(void){
int i, j;
for (i = 0; i < BOARDSIZE; i++){
for (j = 0; j < BOARDSIZE;j++){
switch (board[i][j]){
case NONE:
DrawGraph(32 * i, 32 * j,chip[0],TRUE);
break;
case BLACK:
DrawGraph(32 * i, 32 * j,chip[1],TRUE);
break;
case WHITE:
DrawGraph(32 * i, 32 * j,chip[2],TRUE);
break;
default:
DrawString(32 * i, 32 * j, "er",TRUE);
break;
}
}
}
DrawGraph(sx * 32, sy * 32, sentaku, TRUE);
DrawFormatString(270, 32, GetColor(255, 255, 255), "座標X:%d 座標Y:%d", sx+1,sy+1);
if (turn == 0){
DrawFormatString(270, 64, GetColor(255, 255, 255), "白のターン");
}else{
DrawFormatString(270, 64, GetColor(255, 255, 255), "黒のターン");
}
}
void Choose(void){
int kx = sx;
int ky = sy;
if (Key[KEY_INPUT_LEFT] == 1) { kx--; moveflag = 1; }
if (Key[KEY_INPUT_RIGHT] == 1) { kx++; moveflag = 1; }
if (Key[KEY_INPUT_UP] == 1) { ky--; moveflag = 1; }
if (Key[KEY_INPUT_DOWN] == 1) { ky++; moveflag = 1; }
if ((0 <= kx && kx <= 7) && (0 <= ky && ky <= 7)){
sx = kx;
sy = ky;
}
if (moveflag == 1){
PlaySoundMem(sentaku_se, DX_PLAYTYPE_BACK, TRUE);
moveflag = 0;
}
}
void input(int turn){
if (Key[KEY_INPUT_Z] == 1) {
put(sx, sy, turn);
}
}
int check(int sx, int sy, int turn){
int vec;
//どれか一方向でもひっくり返るか確認
for (vec = 0; vec < 8; ++vec){
if (checkFlip(sx, sy, turn, vec) == 1) return 1;
}
}
int checkFlip(int sx,int sy,int turn,int vec ){
int flag = 0;
int x = sx;
int y = sy;
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[x][y] == NONE) return 0;
//相手のコマがあったらフラグを立てる
if (board[x][y] == (turn ? BLACK : WHITE)){
flag = 1;
continue;
}
//もしフラグがたっていればループ脱出。いなければ終了
if (flag == 1) break;
return 0;
}
return 1;
}
int put(int x, int y, int turn){
//空白でなければ終了
if (board[x][y] != NONE) return 0;
//全方向について確認
for (vec = 0; vec < 8; ++vec){
if (checkFlip(x, y, turn, vec) == 1){
//裏返す
flip(x, y, turn, vec);
flag = 1;
}
}
if (flag == 1){
//この場所にコマを置く
board[x][y] = (turn ? WHITE : BLACK);
return 1;
}
return 0;
}
void flip(int x, int y,int turn, int vec){
while (1){
y += vec_y[vec];
x += vec_x[vec];
//自分のコマがあったら終了
if (board[x][y] == (turn ? WHITE : BLACK)) {
turn = (turn + 1) % 2;
break;
}
//それ以外なら自分のコマで塗りつぶす
board[x][y] = (turn ? WHITE : BLACK);
}
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){
ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen(DX_SCREEN_BACK); //ウィンドウモード変更と初期化と裏画面設定
Green = GetColor(0, 256, 0);
LoadDivGraph("chip.bmp", 3, 3, 1, 32, 32, chip);
sentaku = LoadGraph("sentaku.png", TRUE);
sentaku_se = LoadSoundMem("se01.mp3");
setBoard();
while (!ProcessMessage()){
ClearDrawScreen();
gpUpdateKey();
Choose();
disp();
input(turn);
ScreenFlip();
}
DxLib_End(); // DXライブラリ終了処理
return 0;
}
[1.3] コンパイルエラーはないのですが、白のターンのままでターンが変わらず、一度駒を打つとどこでも打てるようになってしまいます。
[1.4] input関数→put関数→checkFlip関数でチェックしてflip関数で反転という流れはわかるのですが、ターンを変えるタイミングとあちこちにあるvec変数をよくわかっていないのでご教授お願いできたらと思います
[2] 環境
[2.1] OS : Windows8、64bit
[2.2] コンパイラ名 : Microsoft Visual Studio Express 2013 for Windows Desktop
[3] その他
・どの程度C言語を理解しているか
Cの基礎知識の本を読んで一週間です。
初心者だと思ってます。
・ライブラリを使っている場合は何を使っているか
DXライブラリ"DxLib.h"
Re: C言語のオセロプログラムについて
Posted: 2014年7月31日(木) 19:30
by softya(ソフト屋)
いきなりオセロは難しいと思いますので、こちらのひーやんさんみたいに三目並べから始めていませんか?
http://dixq.net/forum/viewtopic.php?f=3&t=15393
ひーやんさんのコードを参考にせずにZEROから作られるのが一番良いと思います。
Re: C言語のオセロプログラムについて
Posted: 2014年7月31日(木) 20:53
by わらわら
softyaさん返信有難うございます。
こちらのひーやんさんのように僕も三目並べから作りたいと思います。
三目並べのソースができたらその時はまたここにソースを貼りたいと思います。
回答有難うございました。
Re: C言語のオセロプログラムについて
Posted: 2014年7月31日(木) 22:08
by わらわら
enumのエラー?が出ますが一応形は出来ました。
挟むと色を変える機能はまだ入れてませんが、実装できたらこのトピックにまたソースを貼りに来ます。
コード:
#include "DxLib.h"
#define NONE 0
#define BLACK 1
#define WHITE 2
//碁盤生成
int board[3][3] = {
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 }
};
//ステータス
enum{
PLAYING,
WIN,
LOSE
} status = PLAYING;
//プレイヤー
int px = 0, py = 0;
//ターン(黒が先手)
int turn = BLACK;
//選択チップ,駒チップ
int sentaku,chip[3];
//キーコード格納変数
int Key[256];
//色変数
int White = GetColor(255, 255, 255);
// キーの入力状態を更新する
int gpUpdateKey(){
char tmpKey[256]; // 現在のキーの入力状態を格納する
GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
for (int i = 0; i<256; i++){
if (tmpKey[i] != 0){ // i番のキーコードに対応するキーが押されていたら
Key[i]++; // 加算
}
else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
void Putchip(void){
int cx = px;
int cy = py;
if (Key[KEY_INPUT_Z] == 1){
if (board[cx][cy] == 0){
board[cx][cy] = turn;
turn = 2 / turn;
}
}
}
void Choose(void){
int kx = px;
int ky = py;
if (Key[KEY_INPUT_LEFT] == 1) { kx--; }
if (Key[KEY_INPUT_RIGHT] == 1) { kx++; }
if (Key[KEY_INPUT_UP] == 1) { ky--; }
if (Key[KEY_INPUT_DOWN] == 1) { ky++; }
if ((0 <= kx && kx <= 2) && (0 <= ky && ky <= 2)){
px = kx;
py = ky;
}
}
void Load(void){
LoadDivGraph("chip.bmp", 3, 3, 1, 32, 32, chip);
sentaku = LoadGraph("sentaku.png", TRUE);
}
void Display(void){
int i, j;
for (i = 0; i < 3; i++){
for (j = 0; j < 3; j++){
switch (board[i][j]){
case NONE:
DrawGraph(32 * i, 32 * j, chip[0], TRUE);
break;
case BLACK:
DrawGraph(32 * i, 32 * j, chip[1], TRUE);
break;
case WHITE:
DrawGraph(32 * i, 32 * j, chip[2], TRUE);
break;
}
}
}
DrawGraph(px * 32, py * 32, sentaku, TRUE);
DrawFormatString(270, 32,White, "座標X:%d 座標Y:%d", px + 1, py + 1);
DrawFormatString(270, 72,White, "%dのターン",turn);
DrawString(270, 112, "Zキーで選択", White);
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){
ChangeWindowMode(TRUE);
SetDrawScreen(DX_SCREEN_BACK);
DxLib_Init();
while (!ProcessMessage()){
ClearDrawScreen();
gpUpdateKey();
Choose();
Putchip();
Load();
Display();
ScreenFlip();
}
DxLib_End(); // DXライブラリ終了処理
return 0;
}
Re: C言語のオセロプログラムについて
Posted: 2014年7月31日(木) 23:18
by わらわら
一応完成しましたが、条件をやみくもに乗せただけで効率的なので書き換えるつもりです。
ソースでなにか変更した方がいいところがあったら教えてくれると助かります。
コード:
#include "DxLib.h"
#define NONE 0
#define BLACK 1
#define WHITE 2
//碁盤生成
int board[3][3] = {
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 }
};
//ステータス
enum Status{
PLAYING,
WIN,
LOSE,
EVEN
} status = PLAYING;
//プレイヤー
int px = 0, py = 0;
//ターン(黒が先手)
int turn = BLACK;
//選択チップ,駒チップ
int sentaku,chip[3];
//キーコード格納変数
int Key[256];
//色変数
int White = GetColor(255, 255, 255);
//ゲームカウント
int Gamecount = 0;
// キーの入力状態を更新する
int gpUpdateKey(){
char tmpKey[256]; // 現在のキーの入力状態を格納する
GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
for (int i = 0; i<256; i++){
if (tmpKey[i] != 0){ // i番のキーコードに対応するキーが押されていたら
Key[i]++; // 加算
}
else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
void Check(void){
if (Gamecount == 9) status = EVEN;
if (board[0][0] == BLACK && board[0][1] == BLACK && board[0][2] == BLACK){
status = WIN;
}
if (board[0][0] == BLACK && board[1][0] == BLACK && board[2][0] == BLACK){
status = WIN;
}
if (board[0][0] == BLACK && board[1][1] == BLACK && board[2][2] == BLACK){
status = WIN;
}
if (board[1][0] == BLACK && board[1][1] == BLACK && board[1][2] == BLACK){
status = WIN;
}
if (board[2][0] == BLACK && board[2][1] == BLACK && board[2][2] == BLACK){
status = WIN;
}
if (board[0][1] == BLACK && board[1][1] == BLACK && board[2][1] == BLACK){
status = WIN;
}
if (board[0][2] == BLACK && board[1][2] == BLACK && board[2][2] == BLACK){
status = WIN;
}
if (board[2][0] == BLACK && board[1][1] == BLACK && board[0][2] == BLACK){
status = WIN;
}
if (board[0][0] == WHITE && board[0][1] == WHITE && board[0][2] == WHITE){
status = LOSE;
}
if (board[0][0] == WHITE && board[1][0] == WHITE && board[2][0] == WHITE){
status = LOSE;
}
if (board[0][0] == WHITE && board[1][1] == WHITE && board[2][2] == WHITE){
status = LOSE;
}
if (board[1][0] == WHITE && board[1][1] == WHITE && board[1][2] == WHITE){
status = LOSE;
}
if (board[2][0] == WHITE && board[2][1] == WHITE && board[2][2] == WHITE){
status = LOSE;
}
if (board[0][1] == WHITE && board[1][1] == WHITE && board[2][1] == WHITE){
status = LOSE;
}
if (board[0][2] == WHITE && board[1][2] == WHITE && board[2][2] == WHITE){
status = LOSE;
}
if (board[2][0] == WHITE && board[1][1] == WHITE && board[0][2] == WHITE){
status = LOSE;
}
}
void Putchip(void){
int cx = px;
int cy = py;
if (Key[KEY_INPUT_Z] == 1){
if (board[cx][cy] == 0){
board[cx][cy] = turn;
turn = 2 / turn;
Gamecount++;
}
}
}
void Choose(void){
int kx = px;
int ky = py;
if (Key[KEY_INPUT_LEFT] == 1) { kx--; }
if (Key[KEY_INPUT_RIGHT] == 1) { kx++; }
if (Key[KEY_INPUT_UP] == 1) { ky--; }
if (Key[KEY_INPUT_DOWN] == 1) { ky++; }
if ((0 <= kx && kx <= 2) && (0 <= ky && ky <= 2)){
px = kx;
py = ky;
}
}
void Load(void){
LoadDivGraph("chip.bmp", 3, 3, 1, 32, 32, chip);
sentaku = LoadGraph("sentaku.png", TRUE);
}
void Display(void){
int i, j;
for (i = 0; i < 3; i++){
for (j = 0; j < 3; j++){
switch (board[i][j]){
case NONE:
DrawGraph(32 * i, 32 * j, chip[0], TRUE);
break;
case BLACK:
DrawGraph(32 * i, 32 * j, chip[1], TRUE);
break;
case WHITE:
DrawGraph(32 * i, 32 * j, chip[2], TRUE);
break;
}
}
}
DrawGraph(px * 32, py * 32, sentaku, TRUE);
DrawFormatString(270, 32,White, "座標X:%d 座標Y:%d", px + 1, py + 1);
DrawFormatString(270, 72,White, "%dのターン",turn);
DrawString(270, 112, "Zキーで選択", White);
DrawFormatString(270,200, White, "%dターン目", Gamecount);
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){
ChangeWindowMode(TRUE);
SetDrawScreen(DX_SCREEN_BACK);
DxLib_Init();
while (!ProcessMessage()){
ClearDrawScreen();
gpUpdateKey();
Choose();
Putchip();
Check();
Load();
Display();
switch (status){
case EVEN:
DrawString(270, 300, "引き分けです", White);
WaitTimer(1000);
DxLib_End();
break;
case WIN:
DrawString(270, 300, "あなたの勝ちです", White);
WaitTimer(1000);
DxLib_End();
break;
case LOSE:
DrawString(270, 300, "あなたの負けです", White);
WaitTimer(1000);
DxLib_End();
break;
}
ScreenFlip();
}
DxLib_End(); // DXライブラリ終了処理
return 0;
}
Re: C言語のオセロプログラムについて
Posted: 2014年8月01日(金) 10:36
by わらわら
勝敗判定のループ化、CPU(GetRandで適当にコマを置くので弱い)を追加、わかりやすいようにコメントを追加しました。一応完成です。
もし良かったらこの三目並べのコードの改善点(非効率的、見にくいなど)があれば教えてくれると助かります!
オセロすら作れない自分がRTS作れるようになる自信がないのですが、とにかくオセロを完成させたいと思います!
コード:
#include "DxLib.h"
#define NONE 0
#define BLACK 1
#define WHITE 2
//ステータス
enum Status{ PLAYING, WIN, LOSE, EVEN } status = PLAYING;
//碁盤生成
int board[3][3] = {
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 }
};
//プレイヤー(選択カーソル)
int px = 0, py = 0;
//ターン(黒が先手)
int turn = BLACK;
//選択チップ,駒チップ
int sentaku,chip[3];
//キーコード格納変数(新ゲームプログラミングの館より)
int Key[256];
//色変数
int White = GetColor(255, 255, 255);
//ゲームカウント
int Gamecount = 0;
// キーの入力状態を更新する(新ゲームプログラミングの館より)
int gpUpdateKey(){
char tmpKey[256]; // 現在のキーの入力状態を格納する
GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
for (int i = 0; i<256; i++){
if (tmpKey[i] != 0){ // i番のキーコードに対応するキーが押されていたら
Key[i]++; // 加算
}
else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
//勝利判定関数
void Check(void){
if (Gamecount == 9) status = EVEN;
int i,color;
for (color = 1; color < WHITE + 1; color++){
for (i = 0; i < 3; i++){
//横
if (board[0][i] == color && board[1][i] == color && board[2][i] == color){
if (color == 1)status = WIN;
if (color == 2)status = LOSE;
}
//縦
if (board[i][0] == color && board[i][1] == color && board[i][2] == color){
if (color == 1)status = WIN;
if (color == 2)status = LOSE;
}
//斜め
if (board[0][0] == color && board[1][1] == color && board[2][2] == color){
if (color == 1)status = WIN;
if (color == 2)status = LOSE;
}
if (board[2][0] == color && board[1][1] == color && board[0][2] == color){
if (color == 1)status = WIN;
if (color == 2)status = LOSE;
}
}
}
}
//コマを置く関数
void Putchip(int px, int py){
int cx = px;
int cy = py;
if (Key[KEY_INPUT_Z] == 1){
if (board[cx][cy] == 0){
board[cx][cy] = turn;
turn = 2 / turn;
Gamecount++;
}
}
}
//選択カーソルの関数
void Choose(void){
int kx = px;
int ky = py;
if (Key[KEY_INPUT_LEFT] == 1) { kx--; }
if (Key[KEY_INPUT_RIGHT] == 1) { kx++; }
if (Key[KEY_INPUT_UP] == 1) { ky--; }
if (Key[KEY_INPUT_DOWN] == 1) { ky++; }
if ((0 <= kx && kx <= 2) && (0 <= ky && ky <= 2)){
px = kx;
py = ky;
}
}
//画像読み込み
void Load(void){
LoadDivGraph("chip.jpg", 3, 3, 1, 32, 32, chip);
sentaku = LoadGraph("sentaku.png", TRUE);
}
//表示関数
void Display(void){
int i, j;
for (i = 0; i < 3; i++){
for (j = 0; j < 3; j++){
switch (board[i][j]){
case NONE:
DrawGraph(32 * i, 32 * j, chip[0], TRUE);
break;
case BLACK:
DrawGraph(32 * i, 32 * j, chip[1], TRUE);
break;
case WHITE:
DrawGraph(32 * i, 32 * j, chip[2], TRUE);
break;
}
}
}
DrawGraph(px * 32, py * 32, sentaku, TRUE);
DrawFormatString(270, 32,White, "座標X:%d 座標Y:%d", px + 1, py + 1);
if (turn == 1){
DrawString(270, 72,"先手のターン",White);
}
else if (turn == 2){
DrawString(270, 72,"後手のターン",White);
}
DrawString(270, 112, "Zキーで駒を置きます", White);
DrawFormatString(270,200, White, "総ターン数:%d", Gamecount);
}
void Statuscheck(int status){
switch (status){
case EVEN:
DrawString(270, 300, "引き分けです", White);
WaitTimer(1000);
DxLib_End();
break;
case WIN:
DrawString(270, 300, "あなたの勝ちです", White);
WaitTimer(1000);
DxLib_End();
break;
case LOSE:
DrawString(270, 300, "あなたの負けです", White);
WaitTimer(1000);
DxLib_End();
break;
}
}
void CPU(){
int CPUrand, CPUx, CPUy;
if (turn == 2){
CPUrand = GetRand(8);
CPUx = CPUrand % 3;
CPUy = CPUrand / 3;
Putchip(CPUx, CPUy);
if (turn == WHITE && !(Gamecount == 9) && status == PLAYING){
CPU();
}
}
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){
ChangeWindowMode(TRUE);
SetDrawScreen(DX_SCREEN_BACK);
DxLib_Init();
while (!ProcessMessage()){
ClearDrawScreen();
gpUpdateKey();
Choose();
Putchip(px,py);
Check();
Load();
CPU();
Display();
Statuscheck(status);
ScreenFlip();
}
DxLib_End(); // DXライブラリ終了処理
return 0;
}
Re: C言語のオセロプログラムについて
Posted: 2014年8月01日(金) 11:07
by usao
>コードの改善点
細かい点はいろいろとあるでしょうが,
真っ先に目につくのが 外部変数バリバリ なところです.
このスタイルのままで より規模の大きいプログラムにトライするのは厳しいように思います.
このコードを 【外部変数を1つも使わない形】 に修正できますか?
Re: C言語のオセロプログラムについて
Posted: 2014年8月01日(金) 13:06
by coco
懐かしいスレッドだなぁ・・・。
もう三年前になるんですね。
ちょっと気になった所は折角色を定数(#define BLACKなど)で定義しているので
勝敗判定のCheckでcolor == BLACKなどにして定数を使ってあげた方が良いかもしれません。
DrawGraphのとことかも本当はMAPCHIP_SIZE 32 みたいな形にしてあげたら良いかなと思いますが、細かい部分なのであまり気にしないで下さい。
取り敢えずプログラムしてて、「あ、またこの部分さっきと同じ意味で同じ数字つかっちゃってるな・・・」と思う場面に結構遭遇するので、そうしたら定数化を心掛けるみたいな感じで良いと思います。多分。
あとはオセロに向けてファイル分割を勉強されるのが良いかなと。
usaoさんがおっしゃられてます通り、グローバル変数が多いと今後大変な思いをされますのでファイル分割とグローバル変数を無くす事は大切です。
あと自分もオセロすら作れませんでしたが、多分今ならRTS作れます。
ただ画像の素材を揃えるのが大変なので作っておりませんが・・・。
(ああいう似たようなユニットが大量出現する系を個人で制作する場合は3Dモデルを使った方が雛形を使いまわせる分、まだ現実的なんじゃないかなぁと最近思います・・・。兵士同士ならモーションも共有出来ますから。)
何はともあれ途中で挫折しなければ必ず作れる様になりますし、仮に挫折したとしてもそれまでに学んだ知識はいずれ必ず役に立ちますので、制作活動頑張って下さい~!
めっちゃ応援してます!
Re: C言語のオセロプログラムについて
Posted: 2014年8月01日(金) 16:06
by わらわら
usaoさんへ
返信有難うございます!
>このコードを 【外部変数を1つも使わない形】 に修正できますか?
仰る通りにグローバル変数をなくすようにします!
明日から少し予定があるので返信が遅れてしまいますが、必ず完成させてまたソースを載せますので、ぜひ改善点がまたありましたらよろしくお願いします!
cocoさんへ
返信ありがとうございます!
>ちょっと気になった所は折角色を定数(#define BLACKなど)で定義しているので
>勝敗判定のCheckでcolor == BLACKなどにして定数を使ってあげた方が良いかもしれません。
>DrawGraphのとことかも本当はMAPCHIP_SIZE 32 みたいな形にしてあげたら良いかなと思いますが、細かい部分なのであまり気にしないで下さい。
>取り敢えずプログラムしてて、「あ、またこの部分さっきと同じ意味で同じ数字つかっちゃってるな・・・」と思う場面に結構遭遇するので、そうしたら定数化を心掛けるみたいな感じで良いと思います。多分。
極力わかりやすい文(マジックナンバーをなくす)に直した方がいいということでしょうか!
>あとはオセロに向けてファイル分割を勉強されるのが良いかなと。
>usaoさんがおっしゃられてます通り、グローバル変数が多いと今後大変な思いをされますのでファイル分割とグローバル変数を無くす事は大切です。
やはりグローバル変数を極力なくすことは大事なんですね。ファイル分割もせっかくなので挑戦したいと思います!
>あと自分もオセロすら作れませんでしたが、多分今ならRTS作れます。
>ただ画像の素材を揃えるのが大変なので作っておりませんが・・・。
>(ああいう似たようなユニットが大量出現する系を個人で制作する場合は3Dモデルを使った方が雛形を使いまわせる分、まだ現実的なんじゃないかなぁと最近思います・・・。兵士同士ならモーションも共有出来ますから。)
やはりオセロは簡単そうに見えて最初はつまずくものなのでしょうかw
僕は作りたいのはAOE2のような2DRTSなのですが、道は険しそうですね・・・
>何はともあれ途中で挫折しなければ必ず作れる様になりますし、仮に挫折したとしてもそれまでに学んだ知識はいずれ必ず役に立ちますので、制作活動頑張って下さい~!
>めっちゃ応援してます!
応援ありがとうございます!
その一言でモチベーションがかなり上がりますw
usaoさんcocoさん返信ありがとうございます!
Re: C言語のオセロプログラムについて
Posted: 2014年8月01日(金) 16:17
by わらわら
お二人の意見をまとめて
改善点
・グローバル変数をなくす。 →ローカル関数にする。
・ファイル分割をする。 →関数をhファイルとcppファイルに分ける。
・プログラムの変数などをわかりやすくする。 →読めるソースコードを目指します。
Re: C言語のオセロプログラムについて
Posted: 2014年8月01日(金) 17:23
by usao
>僕は作りたいのはAOE2のような2DRTSなのですが、道は険しそうですね・・・
私はゲーム作成者ではないので想像でしかないですが
RTSはとても難易度が高い部類のものなのではないかな?と思います.
オフトピック
私はオセロはなんとか作れそうな気が致しますが,RTSはどうか?と問われたら
AOE2みたいなのを考えると… 経路探索の問題 だけでも投げ出したくなりそうです.
マップランダム生成且つ動的変化があって実時間で動くとかとても難しそう.
初代Command&Conquerみたいなマップ固定のものならまだなんとか…?
Re: C言語のオセロプログラムについて
Posted: 2014年8月01日(金) 22:35
by わらわら
http://fast-uploader.com/file/6962455050389/
ご指摘していただいた細かいところの修正は終わりました。
今ソースの分割、グローバル変数を減らしている作業中なのですが、
仮デバッグしたところよくわからないエラーが出てきて困ってます
エラー 1 error LNK2005: "enum Status status" (?status@@3W4Status@@A) は既に board.obj で定義されています。 C:\src\三目並べ\cpu.obj 三目並べ
エラー 2 error LNK2005: "enum Status status" (?status@@3W4Status@@A) は既に board.obj で定義されています。 C:\src\三目並べ\init.obj 三目並べ
エラー 3 error LNK2005: "enum Status status" (?status@@3W4Status@@A) は既に board.obj で定義されています。 C:\src\三目並べ\main.obj 三目並べ
エラー 4 error LNK2005: "enum Status status" (?status@@3W4Status@@A) は既に board.obj で定義されています。 C:\src\三目並べ\putchip.obj 三目並べ
エラー 5 error LNK2001: 外部シンボル ""struct GameData_t MainData" (?MainData@@3UGameData_t@@A)" は未解決です。 C:\src\三目並べ\putchip.obj 三目並べ
エラー 6 error LNK2001: 外部シンボル ""struct GameData_t MainData" (?MainData@@3UGameData_t@@A)" は未解決です。 C:\src\三目並べ\board.obj 三目並べ
エラー 7 error LNK2001: 外部シンボル ""struct GameData_t MainData" (?MainData@@3UGameData_t@@A)" は未解決です。 C:\src\三目並べ\cpu.obj 三目並べ
エラー 8 error LNK2001: 外部シンボル ""struct GameData_t MainData" (?MainData@@3UGameData_t@@A)" は未解決です。 C:\src\三目並べ\init.obj 三目並べ
エラー 9 error LNK2001: 外部シンボル ""struct GameData_t MainData" (?MainData@@3UGameData_t@@A)" は未解決です。 C:\src\三目並べ\main.obj 三目並べ
エラー 10 error LNK1120: 1 件の未解決の外部参照 C:\src\三目並べ\Debug\三目並べ.exe 1 1 三目並べ
debugフォルダの中のobjファイルを消しても再発していて困ってます。
LNK2005のエラーは多重定義されていると調べたら出てきたのですが、何が多重定義?されているのかわかりません
LNK2001はMaindataは何が未解決なのか意味がわかりません。
LNK1120もさっぱりです。
どなたか解決法をご教授お願いします
Re: C言語のオセロプログラムについて
Posted: 2014年8月01日(金) 22:41
by わらわら
usaoさんへ
>初代Command&Conquerみたいなマップ固定のものならまだなんとか…?
C&Cというゲームを知らないのでなんとも言えないんですが、AOE2のマップがいつもおなじものと考えていいのでしょうか
>RTSはとても難易度が高い部類のものなのではないかな?と思います.
http://d.hatena.ne.jp/eiki_okuma/20110224/1298526867
こちらのページを見たんですが、やはり難易度が相当高いようですw
ランダムマップはさておきAIどころかRPGも作れなさそうなので、しばらくは修行です笑
Re: C言語のオセロプログラムについて
Posted: 2014年8月02日(土) 20:18
by わらわら
今前回書いたプログラムを参考にしながら骨組みを位置から作り替えたプログラムを作り終えました。
一応完成したのですが、細かいところを変えています。
前回のエラーログについては一応理解?したので自己解決しました。
完成したプログラムは遂行したあと圧縮してアップローダーに上げたいと思います。
Re: C言語のオセロプログラムについて
Posted: 2014年8月02日(土) 20:40
by わらわら
Re: C言語のオセロプログラムについて
Posted: 2014年8月02日(土) 21:41
by softya(ソフト屋)
ユーザー登録されていますので、ソースコードだけなら容量は少ないのでzipファイルの添付機能の利用をお願いします。
アップローダは消えるので掲示板の記録を残す方針にふさわしくないためです。
投稿時の画面の下の方に添付タブがあると思います。
Re: C言語のオセロプログラムについて
Posted: 2014年8月02日(土) 21:46
by わらわら
slnファイルがやたらサイズが大きかったので他のアップローダーにあげていました><
ソースコードと画像をまとめたものを再度上げ直しました
Re: C言語のオセロプログラムについて
Posted: 2014年8月02日(土) 21:50
by softya(ソフト屋)
実はバイナリ系の大きな構成ファイルはビルド時に再構築されるので添付の必要がありません。
ここのゲームプログラミングの館などの配布ファイルも取り除いてあるはずです。
最低限にすればプロジェクト全部でも小容量ですよ。
Re: C言語のオセロプログラムについて
Posted: 2014年8月02日(土) 21:56
by わらわら
softyaさんへ
ソリューションファイルやその他もろもろがないとうまく開けないと勝手に思ってましたw
30MB近く毎回上げるのはさすがに馬鹿らしいので今度からはソースコードだけあげます><
あとソースコードの分割の方法にsoftyaさんのRPG講座からファイルを少し参考にさせてもらいました。
Re: C言語のオセロプログラムについて
Posted: 2014年8月02日(土) 21:58
by softya(ソフト屋)
グローバル変数が無くならない件ですが例えば、Cursor_x, Cursor_yってmainがずっと知る必要が有るのでしょうか?
Cursor_Update();とBoard_Putchip(MainData.Cursor_x, MainData.Cursor_y);の間で情報がやりとりできればmainは知らなくて済むと思いませんか?
こういう風に全体のデータの受け渡しを見なおせば、グローバルは無くなることが多いのです。
Re: C言語のオセロプログラムについて
Posted: 2014年8月03日(日) 11:10
by わらわら
なるほど、、、
言われてみればそうですね、、、
ちょっと改良してきます
Re: C言語のオセロプログラムについて
Posted: 2014年8月04日(月) 18:37
by わらわら
カーソル座標を入れるための構造体と変数をcursor.hで宣言しCursor_Get関数で座標を取ってこれるようにしました。
この簡単な発想が出てくるまでにも結構時間がかかってしまいましたが、関数の戻り値を複数返してそれをどうやって使うかに地味に苦戦してしまいました。笑
一応参考にした方法貼っときますね
関数の戻り値を複数得る方法
http://dixq.net/forum/viewtopic.php?f=3&t=10275
Keboard_Get関数からヒントを得ました
http://dixq.net/g/d_04.html
structを使った関数の例
http://oshiete.goo.ne.jp/qa/3955714.html
Re: C言語のオセロプログラムについて
Posted: 2014年8月04日(月) 19:32
by usao
オフトピック
要らない話な気がしないでもないですが,
C&CについてAOEと違った(プログラミングの難易度に関係しそうな)箇所を書いときます.
(1)マップが固定
ステージクリアタイプのゲームで各ステージのマップが固定.
MAP上に敵の初期配置とかがしっかりと成された状態から開始.(AOEのキャンペーンみたいな)
(敵もユニット生産するけど,単に失った分を補充してるだけ? ほっといても大軍にならない)
・建造物の建造&破壊 以外でマップに変動がない.
・敵が建物を建てる場所すらマップ毎に完全固定.(破壊しても延々と同じ場所に建てる)
(2)経路探索は貧弱
ちょっと複雑だと「右手を壁につけてればいつかゴールに…」みたいな動きになる.
ユーザの建造物に阻まれると沈黙することも.
(3)隊列を組まない
AOEのような隊列という概念自体がない.
ドラッグで一度に複数ユニットを選択できるが,移動指示を出すと単に各ユニットが全く独立に移動する.
(4)資源が一種類
(5)視界は 一度でもそこを見たことがあれば以降ずっと見える (AOEはさらに 今見えているか? がある)
(6)同一のユニット生産建造物を複数建てても,一度に利用できるのは一個のみ
Re: C言語のオセロプログラムについて
Posted: 2014年8月04日(月) 20:44
by わらわら
usaoさんへ
解説有難うございます!
AOEを作る前にはC&Cを作れないととてもできなそうですね。資源はまだしも経路探索はやはり一番難しそうですね;;
僕は現役のAOC(AOE2)プレーヤーなんですけど、たまに経路探索AIが変な動きをすると感じる時がありますね。
それでもあれほどのAIを作るには相当なアルゴリズム技術がないと作れないようですね・・・
情報提供有難うございます><
Re: C言語のオセロプログラムについて
Posted: 2014年8月04日(月) 21:48
by わらわら
ちょっとグラフィックが気に入らなかったので、画像を差し替えました。
サイズも2倍にしました
ver1.2 三目並べ
Re: C言語のオセロプログラムについて
Posted: 2014年8月04日(月) 21:55
by softya(ソフト屋)
ちょっと見るタイミングがないのであとで見させていただきます。
Re: C言語のオセロプログラムについて
Posted: 2014年8月04日(月) 23:01
by わらわら
softyaさんへ
わざわざ忙しい中すみません;;
ゆっくり見れる時で大丈夫です><
Re: C言語のオセロプログラムについて
Posted: 2014年8月04日(月) 23:48
by coco
今プロジェクト見ているのですが、これってグローバル構造体になってませんか?
例えばCPU.cppがMain.turnを欲しがっていたら
int GetTurn(){ return turn;}
みたいな関数をMain.turnが扱えるファイルで用意しておいてCPU.cppの中で
int turn = GetTurn();
してあげたら使えますよ。
(この手法は欲しがる変数が増えると肥大化していくので、もし欲しがる変数があまりに多い様でしたら、ファイル分割の内容を見直してみて下さい)
turn == 2 も turn == CPU_TURNみたいどうぞー。他はちゃんとなってたので。
あとMain.hは普通作らないような気がします(違ったら本当ごめんなさい
MainはそこからGame管理を呼んだりOption管理を呼んだりさせたいかなと。
今のプロジェクトだとMain_Drawをなくして、Game.cppを作成し、MainループからはDrawGame()のみを呼び出し、DrawGame()内でBoard_Draw();などを呼び出してみたら更にすっきりするかもしれません。
MainはもうほんとUpdataGame()とDrawGame()しか呼び出さない!みたいな状態にしても大丈夫じゃないかな。
(熟練した方々がどうなさっているか分からないため、その方々からご回答ありましたらそちらを優先して下さいね)
あとはCPUのAIですね。
ボードに評価値を設定してあげて、値の高い順から調べ、コマが置かれてたら次に高い場所へ置くという感じです。
初期で設定した評価値に加え、リーチしてたら三つ並ぶ場所が一番評価高くならないといけないので、そこを気を付けて下さい。
(今思いついたAIなので、調べてもっとしっかりしたAIを参考にして下さいっ!
Re: C言語のオセロプログラムについて
Posted: 2014年8月05日(火) 16:30
by わらわら
cocoさんへ
turnは作りなおした時に最初からつくったのですっかり修正するの忘れてました;;
関数の流れはちょっと考えてみたんですけど画像の通りみたいにした方がいいということでいいんでしょうか?
すべてGet関数にすることも考えたんですけどGetGetくどいかな(見やすくするため)と思ってCursorメインの座標のみGet関数で得られるようにしたんですけどやはり訂正した方がいいですか><
AIに関しては多分スクリプトを書く能力がないと見ぬかれて、softyaさんの指示通り自分がイチから作ったAIなんでやはりダメダメですね(´・ω・`)
あと個人的にはオセロに取り掛かりたいんですけど、もうオセロを作り始めても問題無いですか?
人に聞くようなことではないと思うんですけど、三目並べのAIや関数を作りなおしてから次に行ったほうがいいのかもしくはきょうくうんをいかして次のステップに言ったほうがいいのか正直悩んでいます。
どちらのほうがいいですか?_?
Re: C言語のオセロプログラムについて
Posted: 2014年8月05日(火) 18:17
by coco
出先なので画像は後で見ますね
Getについてはどうなのかなぁ…
グローバルな構造体や変数を使うよりはGet増やす方が好きですが、人によっては効果的にグローバルが使えてればokという考え方もあるかもしれません。
Aiですがそもそも三目とオセロはAiの考え方がかなり近いです
三目の方が簡単な分オセロよりハマらないと思うのでお勧めしました
ただモチベ次第かなぁ
自分は昔オセロのAiを途中で投げてSLG作り始めたので、三目のAiを無理強いする資格無いです
Re: C言語のオセロプログラムについて
Posted: 2014年8月05日(火) 18:30
by わらわら
cocoさんへ
なるほど、モチベ次第ですか・・・
モチベーションは(今のところ)高いつもりなので同時並行でやっていこうと思います!
三目並べのAIは改善できるかわかりませんが今改善中です>_<
オセロについてはパスの概念も追加されたりするので関数とか位置から作りなおさないとダメそうなので地味に時間が作りそうですねw
オセロAIまではまだまだ時間がかかりそうです。
Re: C言語のオセロプログラムについて
Posted: 2014年8月05日(火) 19:30
by softya(ソフト屋)
まだ、ソースコードを見ていないですが(汗)
やる気が一番大事なので、オセロに行っても良いと思います。
ただ、最初の最初のつまづき点からすると、独自コードで人間vs人間を先に作った方が良かもしれません。
これで、コマのおけるチェック・白黒のひっくり返しと勝敗判定までは完成します。
その後にAIとなりますね。
ちなみにAIの難易度としては
三目並べ→連珠→オセロ→チェス→将棋かなとは個人的に思っています。
それとチェスの前にはさみ将棋が来るかもしれません。
軍人将棋はチェスより簡単かな?極めたこと無いので不明です。
Re: C言語のオセロプログラムについて
Posted: 2014年8月05日(火) 21:05
by わらわら
softyaさん
オセロの作り方を見ていると大体が人間どうしで戦えるようにしてから、CPU追加という形が多いようですね(個人的にはそのほうが作りやすい)
softyaさんも仰る通り、そうします!
軍人将棋なんですけど、コマごとに攻撃力の数値を降るという単純なAI(スパイや公平などの例外処理は追加する)になりそうなのでオセロと同じかオセロより難しいくらいになりそうな気がします。
ただ、オセロよりもコマ数や盤面が大きいのでRTSの前段階のSTGの前段階としてはいい課題になりそうですね>_<
Re: C言語のオセロプログラムについて
Posted: 2014年8月06日(水) 10:11
by usao
オフトピック
>ちなみにAIの難易度としては
>三目並べ→連珠→オセロ→チェス→将棋かなとは個人的に思っています。
どうぶつ将棋 をどこかに挟みたいですね.
(この手のプログラムの勉強に割と適した題材である気がします)
Re: C言語のオセロプログラムについて
Posted: 2014年8月08日(金) 08:10
by わらわら
現在作成中のオセロプログラムなんですけど、うまくコマを置くことができません。
駒を置けるか判定をしてるんですけど、どこかが間違っているのかうまく動いてくれません
1.オセロの現在カーソルから8方向のうち一方向にforのループ文で進む
2.空白か壁の外ならループ終了
3.敵の駒ならフラグをオンにして進む
4.進んだ先で自分の駒を見つけたら関数の戻り値0を返しコマを置く
Re: C言語のオセロプログラムについて
Posted: 2014年8月08日(金) 08:14
by わらわら
usaoさんへ
返信遅れてすみませんm(__)m
動物将棋はオセロと同じかオセロの次くらいですかね
軍人将棋よりもパターンが少なく駒も版の大きさも小さいのですごい簡単そうに見えますが実際のところどうなんでしょうか笑(・∀・)
Re: C言語のオセロプログラムについて
Posted: 2014年8月08日(金) 08:34
by わらわら
追記
問題部分と思われる部分のコードを貼ります。
ゲームメイン関数からputchip関数を呼び出してcheck関数で8方向文のループ、checkflip関数で一方向に関しての処理を行っているつもりです。すごい読みにくいかもしれませんがよろしくお願いします
コード:
//現在のカーソルの座標から8方向
#define VECTOR_ALL 8
//チップ画像変数
static int m_Chip[3];
//ボード状態を格納する変数
static int m_Board[BOARD_WIDTH][BOARD_HEIGHT];
//ベクトル変数
static int vector_x[VECTOR_ALL] = { 0, 1, 1, 1, 0, -1, -1, -1 };
static int vector_y[VECTOR_ALL] = { -1, -1, 0, 1, 1, 1, 0, -1 };
//ボード初期化
void Board_Init(){
int i, j;
for (i = 0; i < BOARD_WIDTH; i++){
for (j = 0; j < BOARD_HEIGHT; j++){
m_Board[i][j] = 0;
}
}
LoadDivGraph("img/chip.png", 3, 3, 1, BOARDCHIP_SIZE, BOARDCHIP_SIZE, m_Chip);
m_Board[3][3] = 2;
m_Board[4][3] = 1;
m_Board[3][4] = 1;
m_Board[4][4] = 2;
}
int Board_Check(){
int vec;
//どれか一方向でもひっくり返るか確認
for (vec = 0; vec < VECTOR_ALL; ++vec){
if (Board_Checkflip(vec) == 0) { return 0; }
}
return 1;
}
int Board_Checkflip(int i){
int flag = 0,j;
static Cursor_t tmp_CursorData = Cursor_Get();
int kx = tmp_CursorData.cursor_x;
int ky = tmp_CursorData.cursor_y;
for (j = 0; j < BOARDCHIP_SIZE; j++){
kx = kx + (j+1) * vector_x[i];
ky = ky + (j+1) * vector_y[i];
//盤面の外に出ていたら終了
if (kx < 0 || ky < 0 || kx > BOARDCHIP_SIZE - 1 || ky > BOARDCHIP_SIZE - 1) return 1;
//空きマスだったら終了
if (m_Board[kx][ky] == NONE) return 1;
//相手のコマがあったらフラグを立てる
if (m_Board[kx][ky] == (2 / MainData.turn)){ flag = 1; continue; }
if (m_Board[kx][ky] == MainData.turn && flag == 1){ return 0; }
}
return 1;
}
int Board_Flip(int i){
return 0;
}
//ボードにコマを置く関数
int Board_Putchip(int Board_x, int Board_y){
if (Board_Check() == 0 && m_Board[Board_x][Board_y] == 0){
m_Board[Board_x][Board_y] = MainData.turn;
MainData.turn = 2 / MainData.turn;
MainData.Gamecount++;
return 0;
}
return 1;
}
Re: C言語のオセロプログラムについて
Posted: 2014年8月08日(金) 09:52
by usao
ぱっと見ですが,39行目
>static Cursor_t tmp_CursorData = Cursor_Get();
の static が悪さしてるんじゃないか?と予想.
Re: C言語のオセロプログラムについて
Posted: 2014年8月08日(金) 15:57
by わらわら
すみません自己解決しました。
いろいろなオセロ作成サイトを回ってアルゴリズムはわかったのですが、真似してもなんかうまくできなかったので、自己流のきったないプログラムで解決しました。
check機能とflip機能をひとつにまとめて3つ目の引数でcheckのみか、flipするかの切り替え機能をつけて統合しました(なんかこっちのほうが作りやすかった)。
バグは結構出てそのたびにデバッグ→改善→作りなおしとそれなりに時間はかかってしまいましたが、一応裏返し機能まで自己流で出来ました。
コードが汚い部分はとりあえず置いといて、先に進みます。
コード:
int Board_CheckandFlip(int Board_x, int Board_y,int mode){
int vec;
int i,j,k;
int flag = 0;
int kx, ky;
int funcmode = mode;
//どれか一方向でもひっくり返るか確認
for (vec = 0; vec < VECTOR_ALL; ++vec){
//ベクトル代入
kx = Board_x + vector_x[vec];
ky = Board_y + vector_y[vec];
//盤面の外に出ていたら終了
if (kx < 0 || ky < 0 || kx > BOARDCHIP_SIZE - 1 || ky > BOARDCHIP_SIZE - 1) { continue; }
//空白もしくは自分の駒だったら終了
if (m_Board[kx][ky] == NONE || m_Board[kx][ky] == MainData.turn){ continue; }
//相手の駒を見つけたら
if (m_Board[kx][ky] == (2 / MainData.turn)){
for (i = 0; i < BOARDCHIP_SIZE; i++){
kx += vector_x[vec];
ky += vector_y[vec];
if (m_Board[kx][ky] == NONE){
if (i > 0){
j = i;
for (k = 0; k < j + 1; k++){
kx -= vector_x[vec];
ky -= vector_y[vec];
m_Board[kx][ky] = 2 / MainData.turn;
}
}
break;
}
if (funcmode == MODE_FLIP){
m_Board[kx - vector_x[vec]][ky - vector_y[vec]] =MainData.turn;
}
if (m_Board[kx][ky] == MainData.turn){
flag = 1;
break;
}
}
}
}
if (flag == 1){ return 0; }
return 1;
}
//ボードにコマを置く関数
int Board_Putchip(int Board_x, int Board_y){
if (Board_CheckandFlip(Board_x,Board_y,MODE_CHECK) == 0 && m_Board[Board_x][Board_y] == 0){
Board_CheckandFlip(Board_x, Board_y, MODE_FLIP);
m_Board[Board_x][Board_y] = MainData.turn;
MainData.turn = 2 / MainData.turn;
MainData.Gamecount++;
return 0;
}
return 1;
}
オフトピック
usaoさん返信ありがとうございます。
実はあのプログラムを検証しててusaoさんの指摘通りstaticが悪さしてたみたい?なんですが他にも直さなければいけないところがたくさんあったので新しく関数を一から作り直しました。ご指摘を生かせず申し訳ないです。
今図書館で借りている「C/C++によるプログラミングスタイルブック」という本を読んでプログラムのきれいな書き方?を少しずつ学んでるのですが、皆さんの言うことが何となく理解できてきたような気がします。以前cocoさんが指摘してくださった、わかりやすい文を書くということを自分なりには意識はしているのですが、そのせいでくどいもしくはわかりにくい変数があるかもしれません。もしあったら細かいことでもいいので教えてくださると助かります。
このトピックを1000人近く見てくださっているのですけど、ぜひ気になることがあったらどんどん書き込んでくれると嬉しいです!offtopicでもなんでもいいので幅広い意見大募集中です><
Re: C言語のオセロプログラムについて
Posted: 2014年8月08日(金) 16:46
by usao
一応これ
>static Cursor_t tmp_CursorData = Cursor_Get();
の何が悪かったか書いておきますと,
この文は 変数 tmp_CursorData の初期化 になっているのですが,
関数内のstatic変数の初期化は1回しかなされないということです.
(関数 int Board_Checkflip(int i) 内で,Cursor_Get() が呼ばれるのは
初回の呼び出し時だけで,2回目以降は呼ばれないという状態になっていた ということですね.)
Re: C言語のオセロプログラムについて
Posted: 2014年8月08日(金) 17:37
by わらわら
usaoさんへ
そうだったんですか・・・staticについては結構苦戦して調べたのですが初期化が一回しかされないのは知りませんでした・・・
一度カーソル地点から輪っかのような形に駒がでるバグが有りました。そのバグは8方向の各方向ごとに進む前にカーソルに戻らなくてはいけなかった(初期化)のにされてなかったのが原因だと考えていたのですが、その原因はこれみたいですね><
Re: C言語のオセロプログラムについて
Posted: 2014年8月08日(金) 19:23
by softya(ソフト屋)
ぱっと見、気になる点。
1.2 / MainData.turnは分かりづらいですかね。配列を使うか関数を推奨します。
2.この辺は構造体推奨ですね。
kx = Board_x + vector_x[vec];
ky = Board_y + vector_y[vec];
3.再帰を使うとシンプルになりますが、速度は低下します。先読み処理で使う場合は問題にになるかもしれません。
4.このままでもフリップだけでも関数を分けると分かりやすくなります。
5.int kx, ky;やflagなどが何の変数だか分かりづらいです。
ざっとですが。
Re: C言語のオセロプログラムについて
Posted: 2014年8月17日(日) 23:47
by わらわら
すみません返信遅れました>_<
ソースを半分くらい書き直しました。
理由は
・自分で読みにくい、理解し難いコードだった。
・バグが有った。
・そもそもオセロプログラムの原理を完全に理解できてないような気がした
です。
改善点は
・マウス操作にした
・バグを改善した
・ソースを読みやすくした(つもり)
・追加部分を作りやすくした
です。
softya(ソフト屋) さんが書きました:ぱっと見、気になる点。
1.2 / MainData.turnは分かりづらいですかね。配列を使うか関数を推奨します。
2.この辺は構造体推奨ですね。
kx = Board_x + vector_x[vec];
ky = Board_y + vector_y[vec];
3.再帰を使うとシンプルになりますが、速度は低下します。先読み処理で使う場合は問題にになるかもしれません。
4.このままでもフリップだけでも関数を分けると分かりやすくなります。
5.int kx, ky;やflagなどが何の変数だか分かりづらいです。
ざっとですが。
1の関数や配列を使う方法?というのがよくわかりません
25は解決しました。
4は分けてはみたのですが、無駄な関数が増えて見難くなってしまった気がしたので結果ひとつにしました。
3の問題は完成してから考えたいと思います。
一番苦戦したソース部分(コマを置く関数)だけ貼っておきます。
put_flagは実際に駒をおくかチェックのみかのフラグです。
戻り値がコマの数なのは後々AIで使う(反転できる数が多いところに置くAI)つもりです
コード:
/////////////////////////////////////////////////////////
//ボードに駒を置く関数(戻り値は反転したコマの数)
/////////////////////////////////////////////////////////
int Board_Putchip(int x, int y, bool put_flag){
//変数宣言
int flipsum = 0, check_x, check_y, direction_x, direction_y,
tmp_flip_x[BOARD_WIDTH], tmp_flip_y[BOARD_HEIGHT], count,flipcount;
//反転終了フラグ
bool flipend;
//ステータスがプレイ中 かつ マウスで指している座標が黒駒か白駒
if (MainData.status == PLAYING && (m_Board[x][y] == BLACK || m_Board[x][y] == WHITE)){
return 0;
}
//方向走査
for (direction_x = -1; direction_x <= 1; direction_x++){
for (direction_y = -1; direction_y <= 1; direction_y++){
//反転終了フラグ初期化
flipend = false;
//無限ループ(一つの方向にひたすら進む)
for (count = 0;; count++){
//まずチェックする座標を代入
check_x = x + direction_x * (count + 1);
check_y = y + direction_y * (count + 1);
//盤の外か駒が置いていない場合はループを抜ける
if (check_x < 0 || check_x >= (BOARD_WIDTH ) ||
check_y < 0 || check_y >= (BOARD_HEIGHT) ||
m_Board[check_x][check_y] == NONE) break;
//自分の駒がおいてある場合
if (m_Board[check_x][check_y] == MainData.turn && put_flag){
for (flipcount = 0; flipcount < count; flipcount++){
m_Board[tmp_flip_x[flipcount]][tmp_flip_y[flipcount]] = MainData.turn;
flipsum += count;
}
//反転終了後フラグを立てる
flipend = true;
}
//一時変数に格納
tmp_flip_x[count] = check_x;
tmp_flip_y[count] = check_y;
//反転終了した場合、次の方向を走査する
if (flipend) break;
}
}
}
//反転後コマを置く、ターンを変える、ターン数を増やす
if (flipsum > 0 && put_flag){
m_Board[x][y] = MainData.turn;
MainData.turn = 2 / MainData.turn;
MainData.Gamecount++;
return flipsum;
}
//何も反転できなかった場合
return 0;
}
Re: C言語のオセロプログラムについて
Posted: 2014年8月17日(日) 23:57
by softya(ソフト屋)
自分のコマの手と相手のコマの手の関係が分かりづらいのです。
MainData.turnの反対の手が2 / MainData.turn;と言うのは直感的ではありません。
関数化してやれば分かりやすくなると思ったわけです。
MainData.nowTurn = invertTurn( MainData.nowTurn ); //ターンの切り替え
とかすれば分かりやすいですよね。
他はまた後ほど。
Re: C言語のオセロプログラムについて
Posted: 2014年8月19日(火) 00:03
by softya(ソフト屋)
ざっと見ましたが、Game_Update();からMain_Player();呼び出されていないのが違和感ですね。
あとgamedata.cppにGame_Update();とかがあるので、gamedataを扱っているわけでもないのでgame.cppの方がふさわしそうです。
Board_Putchip()は、ひっくり返せる数の探索と実際のひっくり返しを分けたほうがすっきりする気がしますが、AIを作るまでこのままでも良いですよ。
Re: C言語のオセロプログラムについて
Posted: 2014年8月19日(火) 01:09
by わらわら
softyaさんへ
返信、アドバイスありがとうございます。一応オセロプログラム(対人)が完成したのでご報告させていただきます!!
デバッグ中かつソースグチャグチャなので整理して落ち着いてからソースをはらせていただきたいと思います。
上記の関数についてはすっきり理解出来ました!ありがとうございます!
2つ目の返信についてなんですけど、関数とかは上記の通りグチャグチャなので整理してから提出させていただきます。
一応スクリーンショットだけ貼っておきます。
Re: C言語のオセロプログラムについて
Posted: 2014年8月19日(火) 09:23
by usao
Re: C言語のオセロプログラムについて
Posted: 2014年8月19日(火) 14:43
by わらわら
オフトピック
usaoさんへ
ありがとうございます!
Re: C言語のオセロプログラムについて
Posted: 2014年8月19日(火) 16:25
by わらわら
一応他人様が読める?くらいにはなったのでアップします。
ただ、自分がこのプログラムを見て気になっているところ(突っ込まれそうなところ)は、
1:メインデータに黒駒、白駒を置く必要があるのか
2:せっかくステータス分岐しているのに勝敗表示部分、スキップ表示部分(Game_Draw)に判定コードを書いている。(Draw関数で判定をしている)
1:はboard.cppに構造体で白駒、黒駒をまとめたものを作って、関数の戻り値を使ってコマ数を呼び戻せるようにするように書き換えてみました。ですが他のファイルに構造体を知らせる必要出てきてグローバル変数が増えてしまって、本末転倒なので結局この形で落ち着きました。。
この問題を解決する方法として、黒駒の数だけ関数の戻り値で得られるようにして白駒の数を全体のコマの数から黒駒の数を引いて求めるという方法も思いついたのですが、わかりにくいと思って採用していません。
2:はGame_Updateのステータス分岐に勝敗を判定し何かの形で出力してからそれに応じて描画したほうがいいのはわかっているのですが、きれいな書き方ができなかったのでこの形になっています。治したいんですけどスマートにかけないので手直し中です。
softyaさんの改善案はputchip関数以外改善してみました。
Re: C言語のオセロプログラムについて
Posted: 2014年8月20日(水) 20:29
by わらわら
CPU(対戦相手)を2種類追加しました!
ひとつは、駒が置くとれる場所にコマを置くもの、もうひとつは盤面評価の値からコマを置くものとなっています。
これで一応ゲームとして遊べるようになりました。
ソースコードのごちゃごちゃはともかく一応完成しましたので解決したということにさせていただきます。
アドバイスを下さったsoftyaさん、usaoさん、cocoさん本当にありがとうございました!
完成したソースコードはアップしておきます。
なんか言いたいことがありましたら、書いてくださると嬉しいです(*^^*)
Re: C言語のオセロプログラムについて
Posted: 2014年8月20日(水) 22:27
by さこさこ
Re: C言語のオセロプログラムについて
Posted: 2014年8月21日(木) 00:03
by わらわら
さこさこさんへ
自分でもついさっき確認しました。
20回位はデバッグしたのですが、バウがなくてすっかり安心していましたが、ありましたね(;_;)
ちょっとコード見てきて訂正します