C#オセロのコマの反転について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ぼのりん
記事: 18
登録日時: 11年前

C#オセロのコマの反転について

#1

投稿記事 by ぼのりん » 11年前

C#でコンソールアプリケーションのオセロを作っているのですがコマの反転に躓いてしまいわからないです。

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{    
    class Program
    {
        //盤面のサイズ
        const int BOARDSIZE = 8;

        //状態の定義
        const int NONE =  0;    //なし
        const int BLACK = 1;    //黒
        const int WHITE = 2;    //白
       
        //盤面の初期化
        static int[,] board = new int[BOARDSIZE,BOARDSIZE];

        //プレイヤー
        //static int player, cpu;

        //メイン関数
        static void Main(string[] args)
        {

            Init(); //盤面初期化
            disp(); //盤面表示           
            
        }   //Main END

        //盤面初期化
        static void Init()
        {
            board[BOARDSIZE / 2 - 1, BOARDSIZE / 2 - 1] = WHITE;
            board[BOARDSIZE / 2 - 1, BOARDSIZE / 2] = BLACK;
            board[BOARDSIZE / 2, BOARDSIZE / 2 - 1] = BLACK;
            board[BOARDSIZE / 2, BOARDSIZE / 2] = WHITE;

        }//Init END

        //盤面表示
        static void disp()
        {
            int i, j;

            for (i = 0; i < BOARDSIZE; i++)
            {
                for (j = 0; j < BOARDSIZE; j++)
                {
                    switch (board[i, j])
                    {
                        case NONE:
                            Console.Write("□");
                            break;
                        case BLACK:
                            Console.Write("●");
                            break;
                        case WHITE:
                            Console.Write("○");
                            break;
                        default:
                            Console.Write("er");
                            break;
                    }
                }
                Console.WriteLine("");
            }
        }//disp END
        
        

    }   //クラス Program END
}   //ネーム ConsoleApplication1 END
お願いします

YuO
記事: 947
登録日時: 14年前
住所: 東京都世田谷区

Re: C#オセロのコマの反転について

#2

投稿記事 by YuO » 11年前

投稿欄の上にある,「フォーラムルール」の,「質問は具体的にする」の項目に従って,
「自分は何が解らないのか、知りたいのか」
を提示して下さい。

コマの反転というだけでも,分解すれば複数の要素になるはずです。
e.g.) どの場所に置くのか,そこにその色のコマはおけるのか,反転するコマはどれか,実際に反転させるとどのようになるのかetc.
いきなり,全てをやろうしていませんか。
ちゃんとわかるところまで処理をかみ砕いて,それを実装していってみてはどうでしょうか。

ぼのりん
記事: 18
登録日時: 11年前

Re: C#オセロのコマの反転について

#3

投稿記事 by ぼのりん » 11年前

YuO さんが書きました:投稿欄の上にある,「フォーラムルール」の,「質問は具体的にする」の項目に従って,
「自分は何が解らないのか、知りたいのか」
を提示して下さい。

コマの反転というだけでも,分解すれば複数の要素になるはずです。
e.g.) どの場所に置くのか,そこにその色のコマはおけるのか,反転するコマはどれか,実際に反転させるとどのようになるのかetc.
いきなり,全てをやろうしていませんか。
ちゃんとわかるところまで処理をかみ砕いて,それを実装していってみてはどうでしょうか。
回答ありがとうございます。

ご助言いただいたように少しずつ実装してみたいと思います。

またよろしくお願いします。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: C#オセロのコマの反転について

#4

投稿記事 by softya(ソフト屋) » 11年前

YuOさんの言いたいのは、処理を噛み砕いて必要な要素を書き出す事が出来ないと始まらないので、まずそこから始めましょうって事だと思います。
すぐ実装しろってことでは無いはずですよ。

この掲示板に書き出して見たらどうでしょうか。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ぼのりん
記事: 18
登録日時: 11年前

Re: C#オセロのコマの反転について

#5

投稿記事 by ぼのりん » 11年前

返信遅れて申し訳ありませんでした。
YuOさん、softyaさん、ご指摘ありがとうございます。

自分なりにオセロに必要な処理を書いてみます。

人間VS人間で書きます。


1.盤面の初期化
2.盤面に配置するコマの初期化
3.盤面の表示
4.コマを配置するとこを入力する。
5.入力したとこが空白で置けるかどうか判定する。
  盤面の2次元配列のサイズが超えないようにする。
  1次元配列を使い入力したところからループで時計回りに
  盤面の配列を引き算して回りに相手のコマがないか判定する。
  相手のコマが無いなら 4 に戻る、有るなら次の処理へ
6.配置したとこから相手のコマの先に自分のコマがあるかループで判定する。
  無ければ 4に戻る、有るなら次の処理へ
7.配置したとこから、相手のコマの先にある自分のコマまでひっくり返す。
  盤面2次元配列に自分のコマの値をループで代入する。
8.3 に戻って盤面の表示をしたら プレイヤーの交代。
9.盤面にコマが置けなくなるまで 3 〜 8 を繰り返す。
10.置けなくなったら 勝ち負けの判定。
11.終了


よろしくお願いします

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: C#オセロのコマの反転について

#6

投稿記事 by softya(ソフト屋) » 11年前

そうですね。こんな感じで良いと思います。
さて気になるところですが、

>5.入力したとこが空白で置けるかどうか判定する。
自分のコマを置いた盤面の判定の説明不足です。まず、最初に判定すべきです。

>  1次元配列を使い入力したところからループで時計回りに
ここが説明不足です。何となくは想像できますが。 1次元配列が唐突ですかね。

>  盤面の配列を引き算して回りに相手のコマがないか判定する。
ここも曖昧ですね。

> 配置したとこから相手のコマの先に自分のコマがあるかループで判定する。
8方向ありえるので、その辺の処理が必要なことも説明して下さい。
【補足】探索している間に盤面外に飛び出す事も考えないといけません。それと6.から7.にどうやって置き換える方向の情報を引き渡すかも書き出すと良いですね。8方向のうちの該当方向はひっくり返すわけですが残りの方向はひっくり返さないわけです。 
7.も同様です。

>9.盤面にコマが置けなくなるまで 3 〜 8 を繰り返す。
自分がパスする状況もありえるので、そう言う処理も必要ですね。
もう一回オセロのルールを再確認してみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ぼのりん
記事: 18
登録日時: 11年前

Re: C#オセロのコマの反転について

#7

投稿記事 by ぼのりん » 11年前

返信遅れて申し訳ありませんでした。
オセロのソースがほぼ完成したとおもうのでのせます。
コメントの方に動作というか説明をかいてみたので
よろしくお願いします。

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        //盤面のサイズ
        const int BOARDSIZE = 10;

        //盤面の状態の定義
        const int NONE = 0;     //・
        const int BLACK = 1;    //●
        const int WHITE = 2;    //○
        const int FRAME = 3;    //枠

        //盤面の初期化
        static int[,] board = new int[BOARDSIZE, BOARDSIZE];

        //入力した盤面配列の中心の上から時計回りに回る(添字が0の場合)
        static int[] vec_y = { -1, -1, 0, 1, 1, 1, 0, -1 };
        static int[] vec_x = { 0, 1, 1, 1, 0, -1, -1, -1 };

        static int change_turn = 0;
        static int change = 0;

        //-----------------------------------------------------
        //メイン関数
        //-----------------------------------------------------
        static void Main(string[] args)
        {
            //メインループ脱出用
            bool flag = true;
            //プレイヤー判定
            bool turn = true;
            int i, j;
            Init(); //盤面初期化

            //----------------------------------------------
            //メインループ
            //----------------------------------------------
            while (flag)
            {
                Console.WriteLine("{0}", turn ? "  プレイヤー:●" : " プレイヤー:○");

                disp(); //盤面の表示

                change = 0;
                change_turn = 0;

                input(turn);//置くコマの数値を入力する               

                //プレイヤーの交代の確認
                for (i = 0; i < BOARDSIZE; i++)
                {
                    for (j = 0; j < BOARDSIZE; j++)
                    {
                        switch (board[i, j])
                        {
                            case BLACK:
                                change++;
                                change_turn++;
                                break;
                            case WHITE:
                                change++;
                                change_turn++;
                                break;
                            default:
                                break;
                        }
                    }
                }

                //前回記憶した空きマスが増えていたら、実際にプレイヤーを交代する
                if (change_turn < change)
                {
                    change_turn = change;   //空きマスの数を保存

                    switch (turn)
                    {
                        case true:
                            turn = false;
                            break;
                        case false:
                            turn = true;
                            break;
                        default:
                            break;
                    }
                }
 
                //パスの判定
                switch (passflag(turn))
                {
                    case 1:
                        Console.WriteLine("{0}", turn ? "●:パス" : "○:パス");
                        //パスならもう一度入力
                        switch (turn)
                        {
                            case true:
                                turn = false;
                                break;
                            case false:
                                turn = true;
                                break;
                            default:
                                break;
                        }
                        break;
                    case 2:
                        //どちらも置けなくなりゲーム終了のためメインループを抜けwinner関数へ移行
                        Console.WriteLine("どちらも置けなくなりましたので終了します。");//終了確認
                        flag = false;
                        break;
                    //返された値が0ならループの最初から
                    default:
                        break;
                }


            }//メインループ END

            winner();

        }//Main END------------------------------------------------------------


        //盤面初期化
        static void Init()
        {

                board[BOARDSIZE / 2 - 1, BOARDSIZE / 2 - 1] = WHITE;
                board[BOARDSIZE / 2 - 1, BOARDSIZE / 2] = BLACK;
                board[BOARDSIZE / 2, BOARDSIZE / 2 - 1] = BLACK;
                board[BOARDSIZE / 2, BOARDSIZE / 2] = WHITE;
          
                for (int i = 0; i < BOARDSIZE; i++)
                {
                    board[i, 0] = FRAME;
                    board[0, i] = FRAME;
                    board[BOARDSIZE - 1, i] = FRAME;
                    board[i, BOARDSIZE - 1] = FRAME;
                }

        }//Init END----------------------------------------------------------

        //盤面表示
        static void disp()
        {
            int i, j;

            //X座標の数値を表示
            Console.Write("  X");
            for (i = 0; i < BOARDSIZE; i++)
            {
                if (i < BOARDSIZE - 2)
                {
                    Console.Write(" {0}", i + 1);
                }
            }

            Console.Write("\nY ");

            //盤面表示
            for (i = 0; i < BOARDSIZE; i++)
            {
                //Y座標の数値を表示
                if (i + 1 == 1)
                {
                    //処理なし
                }
                else if (i < BOARDSIZE - 1)
                {
                    Console.Write("{0} ", i);
                }
                else Console.Write("  ");

                for (j = 0; j < BOARDSIZE; j++)
                {
                    switch (board[i, j])
                    {
                        case NONE:
                            Console.Write("・");
                            break;
                        case BLACK:
                            Console.Write("●");
                            break;
                        case WHITE:
                            Console.Write("○");
                            break;
                        case FRAME:
                            Console.Write("  ");
                            break;
                        default:
                            Console.Write("er");
                            break;
                    }
                }
                Console.WriteLine("");
            }

        }//disp END-------------------------------------------------------

        //置くコマの数値を入力
        static void input(bool turn)
        {
            int x, y;
            while (true)
            {
                //数字以外が入力されたらもう一回入力            
                try
                {
                    //マスの置く位置を入力する
                    Console.Write("x>> ");
                    x = int.Parse(Console.ReadLine());
                    Console.Write("y>> ");
                    y = int.Parse(Console.ReadLine());
                }
                catch (FormatException)
                {
                    continue;
                }

                //入力した値が1〜8の数値かどうか判定する。1〜8以外の数値だったらもう一度入力
                if (x >= 1 && x <= 8 && y >= 1 && y <= 8)
                {
                    piece(x, y, turn);

                }
                else
                {
                    continue;
                }
                break;
            }

        }//input END------------------------------------------------------------


        //入力した位置にコマを置けるか判定
        static int check(int x, int y, int vec, bool turn)
        {
            /*この関数ではpiece関数や、checkflag関数からvecを引数で受け取り、
              1次元配列のvec_y[]とvec_x[]の添字として使います。
               無限ループの初めは、変数yにvec_y[vec]+1 xにvec_x[vec]+1を代入する
                   
             -----***この関数の例***-----
              例として vecの値は0、コマをboard[5,3]の位置に入力したとします。
                配列の添字vecは、このcheck関数の呼び出し元で8回ループし、
                添字の値の数を指定します。vec_y[vec]に1足す理由は、
                 2次元配列board[5,3]の上、board[4,3]を調べたいときに
                  vec_y[0]の配列の値は-1,vec_x[0]の配列の値は0だから
                    board[5,3]の5に-1を足し、3に0を足すと3のままなので
                     board[4,3]になります。
                      vecの値はこの関数の呼び出し元で計算するので、
                       もしboard[4,3]に相手のコマがあるとwhileの最初に戻り、
                        またboard[4,3]に-1と0を足し、board[3,3]になり、入力した位置より
                         board[5,3]の2つ上を確認できます。board[3,3]の上に相手のコマがあれば
                          呼び出し元に 1 を返す。*/

            int flag = 0;
            while (true)
            {
                //時計回りに確認するためのもの
                y += vec_y[vec];//y=vec_y[vec]+1
                x += vec_x[vec];//x=vec_x[vec]+1

                //board[y,x]の回りに相手のコマがあればループを抜けるためのフラグを立てる
                if (board[y, x] == (turn ? WHITE : BLACK))
                {
                    flag = 1;
                    continue;
                }

                //空きマスか外枠だったら終了
                if (board[y, x] == NONE || board[y, x] == FRAME) return 0;

                //もしフラグがたっていればループを抜ける。たっていなければ終了
                if (flag == 1) break;

                return 0;

            }

            change++;   //プレイヤーの切り替え用
            return 1;

        }//check END------------------------------------------------------------

        //コマを裏返すか判定
        static int piece(int x, int y, bool turn)
        {
            int vec, flag = 0;

            //入力した位置にコマがあれば終了 メインループの最初へ
            if (board[y, x] != NONE) return 0;
            //入力した位置を中心に8方向の上から時計回りに確認
            for (vec = 0; vec < 8; vec++)
            {              
                //check関数を呼び出し、回りに相手のコマがあるか確認する
                if (check(x, y, vec, turn) == 1)
                {
                    //check関数から1が返されたらpieceturn関数を呼び出す
                    pieceturn(x, y, vec, turn);
                    flag = 1;
                }
                
            }

            //入力した位置にコマを配置する            
            if (flag == 1)
            {
                board[y, x] = turn ? BLACK : WHITE;
                flag = 0;
            }

            return 0;

        }//pieceturn END--------------------------------------------------------

        //実際にコマを裏返す
        static void pieceturn(int x, int y, int vec, bool turn)
        {
            while (true)
            {
                y += vec_y[vec];
                x += vec_x[vec];

                //もし相手のコマの先に自分のコマがあれば間のコマを裏返し終了
                if (board[y, x] == (turn ? BLACK : WHITE)) break;
                board[y, x] = (turn ? BLACK : WHITE);
            }
        }//pieceturn END---------------------------------------------------------


        //パスの判定
        static int passflag(bool turn)
        {
            int i, j;

            //すべての盤面を確認
            for (i = 1; i <= 8; i++)
            {
                for (j = 1; j <=8 ; j++)
                {
                        if (board[i, j] == NONE && checkflag(i, j, turn) == 1) return 0;
                }
            }

            
            switch (turn)
            {
                case true:
                    turn = false;
                    break;
                case false:
                    turn = true;
                    break;
                default:
                    break;
            }

            for (i = 1; i <= 8; i++)
            {
                for (j = 1; j <= 8; j++)
                {
                        if (board[i, j] == NONE && checkflag(i, j, turn) == 1) return 1;
                }
            }

            return 2;
        }//passflag END-------------------------------------------------------

        //passflagから呼び出されコマが置けるか判定
        static int checkflag(int x,int y,bool turn)
        {
            int vec;

            for (vec = 0; vec < 8; vec++)
            {
                if (check(x, y, vec, turn) == 1) return 1;
            }

                return 0;

        }//checkflag-----------------------------------------------------------


        //勝ち負けの判定
        static void winner()
        {
            int i, j, win = 0, lose = 0;

            for (i = 0; i < BOARDSIZE; i++)
            {
                for (j = 0; j < BOARDSIZE; j++)
                {
                    switch (board[i, j])
                    {
                        case BLACK:
                            win++;
                            break;
                        case WHITE:
                            lose++;
                            break;
                        default:
                            break;
                    }
                }
            }

            dispsort(win, lose);    //盤面のソートを呼び出す

            //勝者の表示
            if (win > lose) Console.WriteLine("●:勝ち:{0}個\n○:負け:{1}個", win,lose);
            else if (win < lose) Console.WriteLine("○:勝ち:{0}個\n●:負け:{1}個", lose,win);
            else Console.WriteLine("引き分け");

        }//winner END---------------------------------------------------------

        //盤面のソート
        static void dispsort(int win, int lose)
        {
            int i, j;
            int W = win;
            int L = lose;
            
            Console.WriteLine("置換前");
            disp(); //置換前の盤面

            //盤面のコマを全部消去
            for (i = 0; i < BOARDSIZE; i++)
            {
                for (j = 0; j < BOARDSIZE; j++)
                {
                    if (board[i, j] != FRAME)
                    {
                        board[i, j] = NONE;
                    }
                }
            }

            //盤面の一番左上から表示する
            i = 1;
            j = 1;

            //●の数だけ空きマスだったら●を置く
            for (; j <= W; )
            {
                //Xの盤面が端までいったら、Yの盤面を1つ下にずらしXを盤面1から繰り返す
                if (board[i, j] != NONE)
                {
                    j = j - 1;  /*開始地点が盤面の左上(board[1,1])でBOARDSIZEが10で盤面が1~8、
                                   外枠が0と9、盤面Xのjが左から右へ行くと盤面8の、次の外枠9まで
                                    ●が表示されないけど含まれるので、外枠9の分をjから-1引く*/

                    W = W - j;  //残りの●の数を記憶する
                    j = 1;      //Xを盤面の左端に戻す
                    i++;        //board[i,j]のiを1増やし、盤面Yが+1される
                    continue;
                }
                board[i, j] = BLACK;
                j++;
            }

            /*続けて正しく表示するために L に表示したとこまでのXの盤面のjの値を足し
               X盤面9の外枠を除外するために-1する*/
             L = L + j - 1;

            //●が置き終えれば続きから○の数だけ置く
            for (; j <= L; )
            {
                if (board[i, j] != NONE)
                {
                    j = j - 1;
                    L = L - j;
                    j = 1;
                    i++;
                    continue;
                }
                board[i, j] = WHITE;
                j++;
            }

            Console.WriteLine("置換後");
            disp(); //置換後の盤面

        }//dispsort END--------------------------------------------------------

    }   //クラス Program END
}   //ネーム ConsoleApplication1 END


閉鎖

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