void COsero2Dlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 描画用のデバイス コンテキスト SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // クライアントの矩形領域内の中央 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // アイコンを描画します。 dc.DrawIcon(x, y, m_hIcon); } else { if(InitFrag){ CDC *pDC=GetDC(); int x,y; CBrush gray_brush, black_brush, white_brush; white_brush.CreateStockObject(WHITE_BRUSH);//白色 gray_brush.CreateSolidBrush(RGB(200,200,200));//灰色 black_brush.CreateSolidBrush(RGB(0,0,0));//黒色 pDC->SelectObject(&gray_brush); for(x=0;x<MASU_NUM;x++){ for(y=0;y<MASU_NUM;y++){ pDC->Rectangle(x*MASU_SIZE,y*MASU_SIZE,(x+1)*MASU_SIZE,(y+1)*MASU_SIZE); if(m_Board[x][y]==0)continue; else if(m_Board[x][y]==BLACK_STONE)pDC->SelectObject(&black_brush); else if(m_Board[x][y]==WHITE_STONE)pDC->SelectObject(&white_brush); pDC->Ellipse(x*MASU_SIZE+3, y*MASU_SIZE+3,(x+1)*MASU_SIZE-3,(y+1)*MASU_SIZE-3); pDC->SelectObject(&gray_brush); } } ReleaseDC(pDC); } CDialog::OnPaint(); } } void COsero2Dlg::OnButton1() { // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください MessageBox("ゲームを開始します"); InitFrag=TRUE; CurSor=TRUE; CDC *pDC=GetDC(); pDC->TextOut(450,100,"オセロゲーム"); CStatic *fr; fr=(CStatic *)GetDlgItem(IDC_STATIC1); fr->SetWindowText("黒が先手です"); shokika();//初期化関数 OnPaint();//描画関数 ReleaseDC(pDC); } void COsero2Dlg::shokika()//盤の初期化 { int x,y; for(x=0;x<MASU_NUM;x++){ for(y=0;y<MASU_NUM;y++){ m_Board[x][y]=0; m_Board[3][3]=BLACK_STONE; m_Board[4][4]=BLACK_STONE; m_Board[3][4]=WHITE_STONE; m_Board[4][3]=WHITE_STONE; } } Frag=TRUE;//先手は黒 CButton *B; B=(CButton *)GetDlgItem(IDC_BUTTON1); B->EnableWindow(FALSE); countALL=0;//手数の初期化 } void COsero2Dlg::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください int x,y; x=point.x/MASU_SIZE;//(例) 0-50のどこをクリックしても0と認識されるようにする y=point.y/MASU_SIZE; CDC *pDC=GetDC(); if(Rule(x,y)){ Reverce( x,y ); PutStone(x,y);//交互に石を置く関数 countALL++; OnPaint(); CStatic *p; p=(CStatic *)GetDlgItem(IDC_STATIC1); p->SetWindowText("黒の番です"); ReleaseDC(pDC); End(); MessageBox("CPUが打ちます"); AI(); nothing(); } BOOL COsero2Dlg::Rule(int x, int y) { if(x>=MASU_NUM && y>=MASU_NUM )return(FALSE);//マス数を超えるような位置には置けない。 if(m_Board[x][y]==WHITE_STONE ||m_Board[x][y]==BLACK_STONE)return(FALSE);//既に石がおいてあるところには置けない。 if(Rule(x,y,1,0))return TRUE;//右 if(Rule(x,y,0,1))return TRUE;//下 if(Rule(x,y,1,1))return TRUE;//右下 if(Rule(x,y,-1,-1))return TRUE;//左下 if(Rule(x,y,0,-1))return TRUE;//上 if(Rule(x,y,-1,0))return TRUE;//下 if(Rule(x,y,1,-1))return TRUE;//右上 if(Rule(x,y,-1,1))return TRUE;//左上 return FALSE; } void COsero2Dlg::PutStone(int x, int y)//交互に打つようにする { if(Frag==TRUE) m_Board[x][y]=BLACK_STONE; else m_Board[x][y]=WHITE_STONE; Frag=!Frag; } BOOL COsero2Dlg::Rule(int x, int y, int v_x, int v_y) { int put_stone; if(Frag==FALSE) put_stone=WHITE_STONE; else put_stone=BLACK_STONE; x+=v_x;// y+=v_y; if(m_Board[x][y]==0)return(FALSE);// if(m_Board[x][y]==put_stone)return(FALSE); x+=v_x; y+=v_y; while(x>=0 &&x<MASU_NUM && y>=0&& y<MASU_NUM){ if(m_Board[x][y]==0)return(FALSE); if(m_Board[x][y]==put_stone)return(TRUE); x+=v_x; y+=v_y; } return FALSE; } void COsero2Dlg::Reverce(int x, int y) { if(Rule(x,y,1,1)){ Reverce(x,y,1,1); } if(Rule(x,y,-1,-1)){ Reverce(x,y,-1,-1); } if(Rule(x,y,1,0)){ Reverce(x,y,1,0); } if(Rule(x,y,0,1)){ Reverce(x,y,0,1); } if(Rule(x,y,-1,0)){ Reverce(x,y,-1,0); } if(Rule(x,y,0,-1)){ Reverce(x,y,0,-1); } if(Rule(x,y,1,-1)){ Reverce(x,y,1,-1); } if(Rule(x,y,-1,1)){ Reverce(x,y,-1,1); } } void COsero2Dlg::Reverce(int x, int y, int v_x, int v_y) { int put_stone; if(Frag==FALSE) put_stone=WHITE_STONE; else put_stone=BLACK_STONE; while(m_Board[x+=v_x][y+=v_y]!=put_stone) m_Board[x][y]=put_stone; }
オセロ
オセロ
今度はAIを組み入れてみました。ちょっと長いですが・・
Re:オセロ前編
続きです。
稚拙な所は初心者の為ご容赦下さい。MFCのダイアログベースで作成しました。ダイアログ上のボタンをクリック
すると盤が描画されます。思考ルーチンなんですが今回のはそういう大層なものではなく、置ける場所を
探し出して置く、という単純なものだと思います。OnSetCurSorでは石が置ける場所に限りカーソルを変化
させています。ご質問したいのは打つ所がない場合CPUに手番をチェンジしてるのですが途中でエラーが出て
止まってしまいます。他にも色々危ない感じのがたくさんあるような気もします・・
よろしければ何かアドバイスください。
int COsero2Dlg::count(int stone) { int x,y; int count=0; for(x=0;x<MASU_NUM;x++) for(y=0;y<MASU_NUM;y++) if(m_Board[x][y]==stone) count++; return(count); } //判定する関数です。 void COsero2Dlg::End() { num1=0,num2=0; CString str1,str2; num1=count(BLACK_STONE);//黒だった場合num1に黒石の数を入れる num2=count(WHITE_STONE);//白だった場合num2に白石の数を入れる str1.Format("%d",num1); str2.Format("%d",num2); if(countALL==60){ if(num1>num2){ MessageBox(str1,"黒石の数"); MessageBox(str2,"白石の数"); MessageBox("黒の勝ちです","勝敗判定"); AfxGetApp()->m_pMainWnd->DestroyWindow(); } else if(num1<num2){ MessageBox(str1,"黒石の数"); MessageBox(str2,"白石の数"); MessageBox("白の勝ちです","勝敗判定"); AfxGetApp()->m_pMainWnd->DestroyWindow(); } else{ MessageBox(str1,"黒石の数"); MessageBox(str2,"白石の数"); MessageBox("引き分けです","勝敗判定"); AfxGetApp()->m_pMainWnd->DestroyWindow(); } } } void COsero2Dlg::nothing() { int x,y,no=0,bl=0,wh=0; for(x=0;x<MASU_NUM;x++){ for(y=0;y<MASU_NUM;y++){ if(Rule(x,y)==FALSE) no++; if(m_Board[x][y]==BLACK_STONE) bl++; if(m_Board[x][y]==WHITE_STONE) wh++; } } if((no==64)&&(countALL!=60)&&((bl!=0)&&(wh!=0))){ MessageBox("打つ所がありません"); MessageBox("手番を変更します"); AI(); } if(bl==0){ MessageBox("黒石が無くなったので白の勝ちです"); AfxGetApp()->m_pMainWnd->DestroyWindow(); } if(wh==0){ MessageBox("白石が無くなったので黒の勝ちです"); AfxGetApp()->m_pMainWnd->DestroyWindow(); } } BOOL COsero2Dlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { if(CurSor==TRUE){ HCURSOR myCSR; if(myCSR){ ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEALL)); return TRUE; } } return CDialog::OnSetCursor(pWnd, nHitTest, message); } void COsero2Dlg::OnMouseMove(UINT nFlags, CPoint point) { // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください int x,y; x=point.x/MASU_SIZE; y=point.y/MASU_SIZE; if(Rule(x,y,1,1)){ CurSor=TRUE; } else if(Rule(x,y,-1,-1)){ CurSor=TRUE; } else if(Rule(x,y,1,0)){ CurSor=TRUE; } else if(Rule(x,y,0,1)){ CurSor=TRUE; } else if(Rule(x,y,-1,0)){ CurSor=TRUE; } else if(Rule(x,y,0,-1)){ CurSor=TRUE; } else if(Rule(x,y,1,-1)){ CurSor=TRUE; } else if(Rule(x,y,-1,1)){ CurSor=TRUE; } else{ CurSor=FALSE; } CDialog::OnMouseMove(nFlags, point); } void COsero2Dlg::AI() { int x,y,X,Y; for(x=0;x<MASU_NUM;x++){ for(y=0;y<MASU_NUM;y++){ if(Rule(x,y)){ X=x; Y=y; } } } m_Board[X][Y]=WHITE_STONE; Reverce( X,Y ); Frag=!Frag; countALL++; OnPaint(); End(); nothing(); } ヘッダファイルはこちらになります class COsero2Dlg : public CDialog { // 構築 public: BOOL CurSor; BOOL Frag;//手番の設定 BOOL InitFrag;//ボタンがクリックされるまで描写されないようにするBOOL変数 int num2;//白石の数 int num1;//黒石の数 int countALL;//手数を数える。盤がいっぱいになったら勝利判定 int m_Board[MASU_NUM][MASU_NUM]; void End();//勝利判定をする関数 int count(int stone);//石を数える void nothing();//打つところがない場合の設定 void Reverce(int x, int y,int v_x, int v_y); void Reverce(int x, int y);//裏返しにする関数 BOOL Rule(int x,int y, int v_x, int v_y); void PutStone(int x, int y);//交互に打つように設定をする BOOL Rule(int x, int y);//裏返しにできる所のみ置けるようにする設定をする。 void AI();//コンピューターの設定をする関数 void shokika();//初期化関数 }
稚拙な所は初心者の為ご容赦下さい。MFCのダイアログベースで作成しました。ダイアログ上のボタンをクリック
すると盤が描画されます。思考ルーチンなんですが今回のはそういう大層なものではなく、置ける場所を
探し出して置く、という単純なものだと思います。OnSetCurSorでは石が置ける場所に限りカーソルを変化
させています。ご質問したいのは打つ所がない場合CPUに手番をチェンジしてるのですが途中でエラーが出て
止まってしまいます。他にも色々危ない感じのがたくさんあるような気もします・・
よろしければ何かアドバイスください。
Re:オセロ前編
ひとつ質問。
AIを使用しない(人が操作する)場合は正しく動作するんですよね?
その場合、Rule(int x, int y)実行時のxとyの値の範囲を教えてもらえませんか?
Rule(x, y, v_x, v_y)のソースを見る限り、xとyの値によってはm_Boardの範囲外にアクセスします。
AI()はxとyを0~(MASU_NUM-1)の範囲で実行しますが、例えばx=0,y=0の状態で
AIを使用しない(人が操作する)場合は正しく動作するんですよね?
その場合、Rule(int x, int y)実行時のxとyの値の範囲を教えてもらえませんか?
Rule(x, y, v_x, v_y)のソースを見る限り、xとyの値によってはm_Boardの範囲外にアクセスします。
AI()はxとyを0~(MASU_NUM-1)の範囲で実行しますが、例えばx=0,y=0の状態で
if(Rule(x,y,-1,-1))return TRUE;//左下 if(Rule(x,y,0,-1))return TRUE;//上 if(Rule(x,y,-1,0))return TRUE;//下 if(Rule(x,y,1,-1))return TRUE;//右上 if(Rule(x,y,-1,1))return TRUE;//左上を実行するとRule(引数4個)の
x+=v_x; y+=v_y;のところでx,yのいずれか、もしくは両方が-1になります。
Re:オセロ前編
> x+=v_x;//
> y+=v_y;
>
> if(m_Board[x][y]==0)return(FALSE);//
> if(m_Board[x][y]==put_stone)return(FALSE);
いや、そのwhileよりも前の時点ですでに変更した値でのxとyを使ってm_Boardを参照しているでしょ。
範囲外にリードアクセスするだけであれば停止してしまう確率は低いですが、期待した結果は得られないでしょう。(運良く動く場合もあるでしょうが)
そもそも、Rule(引数2個のほう、むぅC++はこういうときにめんどくさいな)で問答無用で周囲8方向をチェックしているのはまずくないですか?
例えば左上(座標0,0)のマスには右、右下、下方向にしかマスは存在しませんよね。
現状はこの辺の考慮が抜けているように(私には)見えます。
考慮されているということであればご説明していただけますか。
その辺が知りたかったので以下の質問をしたわけですが。
>> AIを使用しない(人が操作する)場合は正しく動作するんですよね?
>> その場合、Rule(int x, int y)実行時のxとyの値の範囲を教えてもらえませんか?
> y+=v_y;
>
> if(m_Board[x][y]==0)return(FALSE);//
> if(m_Board[x][y]==put_stone)return(FALSE);
いや、そのwhileよりも前の時点ですでに変更した値でのxとyを使ってm_Boardを参照しているでしょ。
範囲外にリードアクセスするだけであれば停止してしまう確率は低いですが、期待した結果は得られないでしょう。(運良く動く場合もあるでしょうが)
そもそも、Rule(引数2個のほう、むぅC++はこういうときにめんどくさいな)で問答無用で周囲8方向をチェックしているのはまずくないですか?
例えば左上(座標0,0)のマスには右、右下、下方向にしかマスは存在しませんよね。
現状はこの辺の考慮が抜けているように(私には)見えます。
考慮されているということであればご説明していただけますか。
その辺が知りたかったので以下の質問をしたわけですが。
>> AIを使用しない(人が操作する)場合は正しく動作するんですよね?
>> その場合、Rule(int x, int y)実行時のxとyの値の範囲を教えてもらえませんか?
Re:オセロ前編
AIを使用しないときはAI()使いません。Ruleのxとyの範囲ですけど番の外や石が置いてある所をクリックしても
変化なしですのでともに0~7だと思います。
AIを使用してないときは動きます。こちらのHP参考にしました
http://www.geocities.co.jp/SiliconValle ... index.html
関数名は違ってますが大体似たり寄ったりです。こちらにはミニマックス法がかかれていますが
よくわからなかったので自分でAI作ってみようってことになりました。
変化なしですのでともに0~7だと思います。
AIを使用してないときは動きます。こちらのHP参考にしました
http://www.geocities.co.jp/SiliconValle ... index.html
関数名は違ってますが大体似たり寄ったりです。こちらにはミニマックス法がかかれていますが
よくわからなかったので自分でAI作ってみようってことになりました。
Re:オセロ前編
m_Board[x][y]が0とはm_Board[0][0]のことでしょうか?それともm_Board[x][y]=0のことかな?
m_BoardはBOOL変数でTRUEのときに黒石(ここではBLACK_STONE)です,FALSEのときは逆なので
後者なら、m_Board[x][y]=の値はBLACK_STONEかWHITE_STONEしかないと思います。
前者の場合m_Board[0][0]の値はフラグ(手番)によって変わります。
>>あと、MASU_NUMとかの値がわからないので定数定義も載せてもらえますか?
すいません載せてませんでした。
#define BLACK_STONE 1
#define WHITE_STONE -1
#define MASU_NUM 8 //マスの数のことです。8x8なので8
#define MASU_SIZE 50 //マスのサイズのことです。
m_BoardはBOOL変数でTRUEのときに黒石(ここではBLACK_STONE)です,FALSEのときは逆なので
後者なら、m_Board[x][y]=の値はBLACK_STONEかWHITE_STONEしかないと思います。
前者の場合m_Board[0][0]の値はフラグ(手番)によって変わります。
>>あと、MASU_NUMとかの値がわからないので定数定義も載せてもらえますか?
すいません載せてませんでした。
#define BLACK_STONE 1
#define WHITE_STONE -1
#define MASU_NUM 8 //マスの数のことです。8x8なので8
#define MASU_SIZE 50 //マスのサイズのことです。
Re:オセロ前編
回答ありがとうございます。質問の意図は後者です。
> m_BoardはBOOL変数でTRUEのときに黒石(ここではBLACK_STONE)です,FALSEのときは逆なので
ますますわからなくなりました。
m_Boardはintで宣言されています。
BLACK_STONEは1、WHITE_STONEは-1で定義されています。
そして、Rule(引数4)には以下のコードがあり
> if(m_Board[x][y]==0)return(FALSE);
m_Board[x][y]==0には、石を置けないというルールが定められているように思います。
ざこさんの理解とソースコードは既に一致していないように思います。
まずはAIをどうするかよりも、もう一度原点に立ち返ってプログラムの理解を深められたほうがAIが動かない理由が見えてくるのではないでしょうか。
> m_BoardはBOOL変数でTRUEのときに黒石(ここではBLACK_STONE)です,FALSEのときは逆なので
ますますわからなくなりました。
m_Boardはintで宣言されています。
BLACK_STONEは1、WHITE_STONEは-1で定義されています。
そして、Rule(引数4)には以下のコードがあり
> if(m_Board[x][y]==0)return(FALSE);
m_Board[x][y]==0には、石を置けないというルールが定められているように思います。
ざこさんの理解とソースコードは既に一致していないように思います。
まずはAIをどうするかよりも、もう一度原点に立ち返ってプログラムの理解を深められたほうがAIが動かない理由が見えてくるのではないでしょうか。
Re:オセロ前編
う~ん、ちょっと期待していた回答と違うかな。
> つまりOnLButtonのif(Rule(x,y)から下の文は実行されない状態を意味する。(描写、リバース、手番変更、判定など)
> TRUEのときのみそのx,y使ってOnLButtonのRuleから下の文を実行する状態を意味する。
それは、ソースそのままなので見ればわかります。
実行できる(できない)ということはそのマスがどういう状態にあるからなのかを知りたいのですが。
余り質問ばかりしていても長くなるだけなのでRuleとAI関数を範囲外アクセスしないように改造しました。
ざこさんが期待する動作をするかどうかわかりませんが(特にAI)、ソフトが落ちることは無いと思います。
> つまりOnLButtonのif(Rule(x,y)から下の文は実行されない状態を意味する。(描写、リバース、手番変更、判定など)
> TRUEのときのみそのx,y使ってOnLButtonのRuleから下の文を実行する状態を意味する。
それは、ソースそのままなので見ればわかります。
実行できる(できない)ということはそのマスがどういう状態にあるからなのかを知りたいのですが。
余り質問ばかりしていても長くなるだけなのでRuleとAI関数を範囲外アクセスしないように改造しました。
ざこさんが期待する動作をするかどうかわかりませんが(特にAI)、ソフトが落ちることは無いと思います。
BOOL COsero2Dlg::Rule(int x, int y, int v_x, int v_y) { int put_stone; if(Frag==FALSE) put_stone=WHITE_STONE; else put_stone=BLACK_STONE; x+=v_x; y+=v_y; // ★範囲内チェック if (x>=0 && x<MASU_NUM) return(FALSE); // ★ if (y>=0 && y<MASU_NUM) return(FALSE); // ★ if(m_Board[x][y]==0)return(FALSE); if(m_Board[x][y]==put_stone)return(FALSE); x+=v_x; y+=v_y; while(x>=0 &&x<MASU_NUM && y>=0&& y<MASU_NUM){ if(m_Board[x][y]==0)return(FALSE); if(m_Board[x][y]==put_stone)return(TRUE); x+=v_x; y+=v_y; } return FALSE; } void COsero2Dlg::AI() { int x,y,X,Y; for(x=0;x<MASU_NUM;x++){ for(y=0;y<MASU_NUM;y++){ if(Rule(x,y)){ X=x; Y=y; break; // ★石を置く所が見つかったのだからループ終了 } } } // ★置ける所があったかどうかのチェック if (x >= MASU_NUM) { // ★置ける場所がなかった場合に必要な処理があれば追加してください return; } m_Board[X][Y]=WHITE_STONE; Reverce( X,Y ); Frag=!Frag; countALL++; OnPaint(); End(); nothing(); }