

オセロのプログラムを作っていて、毎回取得できる最大の枚数を出力しようとしていますが、上手くいきません。  
実際に取得できた枚数はすぐ出せたのですが、どこがおかしいか全く分からないのでよろしくお願いします。

[code]

    #include<iomanip.h>
    #include<iostream.h>
    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string.h>
    
    const int Max_tate=8;       //縦の最大座標
    const int Max_yoko=8;       //横の最大座標
    
    //状態を定義
    const int None=0;
    const int kuro=1;
    const int shiro=2;
    
    int human, cpu;     //ターンプレイヤーの定義
    int difficulty;     //難易度
    
    int field[Max_tate][Max_yoko];
    int turn;
    int turnplayer; //コンピューターか人間かの判断
    
    int flipno;
    int vec;
    int flip_max;
    int flip_plus;
    int realflip;
    
    void disp(void){
        cout<<"　";
        for(int i = 0 ; i < Max_yoko;i++){
             cout<<" "<<i + 1;
        }
        cout<<endl;
        for(int i=0;i<Max_tate;i++){
            cout<<i+1<<"0";
        for(int l=0;l<Max_yoko;l++){
            if(field[i][l]==0){
                    cout<<"＋";
                }else if(field[i][l]==1){
                    cout<<"●";
                }else if(field[i][l]==2){
                    cout<<"○";
                }
            }
        cout<<endl;
        }
    }
    
    //向きごとの移動量
    vec_tate[] = {-1,-1,0,1,1,1,0,-1};
    vec_yoko[] = {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_tate[vec];
            x += vec_yoko[vec];
    
            //盤面の外に出ていたら終了
            if( x < 0 || y < 0 || x > Max_tate-1 || y > Max_yoko-1) return 0;
    
            //空きマスだったら終了
            if(field[y][x] == None) return 0;
    
            //相手のコマがあったらフラグを立てる
            if(field[y][x] == (turn ? kuro : shiro)){
                flag = 1;
                continue;
            }
    
            //もしフラグがたっていればループ脱出。いなければ終了
            if(flag == 1) break;
            return 0;
        }
        return 1;
    }
    
    //その場所に置くことができるかを確認する関数
    int check(int y,int x,int turn){
        //どれか一方向でもひっくり返るか確認
        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_tate[vec];
            x += vec_yoko[vec];
    
            //自分のコマがあったら終了
            if(field[y][x] == (turn ? shiro : kuro)){
                break;
            }
            //それ以外なら自分のコマで塗りつぶす
            field[y][x] = (turn ? shiro : kuro);
            realflip++;　　//実際に裏返した枚数
    
        }
    }
    
    //入力を受けて裏返せるか確かめる関数
    int put(int y,int x,int turn){
        int vec,flag=0;
    
        //空白でなければ終了
        if(field[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){
            //この場所にコマを置く
            field[y][x] = (turn ? shiro : kuro);
            return 1;
        }
    
        return 0;
    }
    
    //裏返せる枚数を計算する
    void number(int y,int x,int turn,int vec){
        flipno=0;
        while(1){
            y += vec_tate[vec];
            x += vec_yoko[vec];
    
            //自分のコマがあったら終了
            if(field[y][x] == (turn ? shiro : kuro)){
                break;
    
            }else{
            //それ以外なら自分のコマで塗りつぶす
            flipno++;
            }
        }if(flipno>flip_max){
        flip_max=flipno;
        }
    }
    //上の準備段階
    int reverse(int y,int x,int turn){
        for(int i=0;i<Max_tate;i++){
            for(int l=0;l<Max_yoko;l++){
            //空白でなければ終了
                if(field[i][l] != None){
                    return 0;
                }
                //全方向について確認
                for(vec=0 ; vec < 8 ; vec++){
                    if(checkFlip(y,x,turn,vec) == 1){
                    //裏返す個数
                    number(y,x,turn,vec);
                    }
                }
            }
        }
        return 0;
    }
    //入力関数
    void input(int turn){
        realflip =0;
        flip_max=0;
        for(int i=0;i<Max_tate;i++){
            for(int l=0;l<Max_yoko;l++){
        if(reverse(i-1,l-1,turn) == 1){
                 break;
        }}}
        cout<<flip_max<<"個取れます"<<endl;
        int place = 0,y,x;
        while(1){
            //入力する
            if(turnplayer==human){
            cout<<"どこに置きますか？";
            cin>>place;
            }else{
            place=rand()%89;
            }
            if(place == 0){
                //数値がとれなかったらバッファをクリアする
                if(turnplayer==human){
                cout<<"その入力は行えません";
                }continue;
            }
    
            //数値が範囲内か確認
            if(place < 11 || place > 88){
                if(turnplayer==human){
                cout<<"そこには置けません"<<endl;
                continue;
                }place = 0;
    
            }
            y = place / 10;
            x = place % 10;
    
            //もう少し詳しく確認
            if( x < 1 || y < 1 || x > 8 || y > 8){
                if(turnplayer==human){
                cout<<"input"<<place<<"error"<<endl;
                place = 0;
                continue;
                }}
            if(put(y-1,x-1,turn) == 1){
                 break;
            }else{
            if(turnplayer==human){
            cout<<"裏返す駒がありません"<<place<<endl;
            }place = 0;
            }
        }
    
    }
    
    int checkEnd(int turn){
        //置ける場所があるか確認
        for(int i = 0 ; i <Max_tate ; i++){
            for(int j = 0 ; j < Max_yoko ; j++){
                //あれば普通に続行
                if(field[i][j] == None && check(i,j,turn) == 1) return 0;
            }
        }
        //場所が無かったので交替して探す
        turn = (turn + 1) % 2;
        for(int i = 0 ; i < Max_tate; i++){
            for(int j = 0 ; j < Max_yoko; j++){
                //あればpassして続行
                if(field[i][j] == None && check(i,j,turn) == 1) return 1;
            }
        }
    
        //なかったのでゲーム終了
        return 2;
    }
    
    //勝者判定
    void checkWinner(){
        int b=0,w=0;
        //コマを数え上げる
        for(int i = 0 ; i < Max_tate ; i++){
            for(int j = 0 ; j <Max_yoko ; j++){
                if(field[i][j]==kuro){
                    b++;
                }else if(field[i][j]==shiro){
                    w++;
                }
            }
        }
        //最後に盤面表示
        disp();
    
        //勝者を表示
        cout<<"黒"<<b<<":白"<<w<<endl;
        if(b > w){
            cout<<"●の勝利です!\n";
        }else if(b < w) {
            cout<<"○の勝利です";
        }else{
            cout<<"引き分けです";
        }
    }
    
    int main(){
    //ゲームの準備
    cout<<"オセロ"<<endl;
    if(rand()%2==0){
    cout<<"あなたは先行（●）です"<<endl;
    human=0,cpu=1;
    }else{
    cout<<"あなたは後攻（○)です"<<endl;
    cpu=0,human=1;
    }
    
    for(int i=0;i<Max_tate;i++){
        for(int l=0;l<Max_yoko;l++){
            field[i][l]=None;
        }
    }
    field[Max_tate/2-1][Max_yoko/2-1]=kuro;
    field[Max_tate/2-1][Max_yoko/2]=shiro;
    field[Max_tate/2][Max_yoko/2-1]=shiro;
    field[Max_tate/2][Max_yoko/2]=kuro;
    
    //ゲームのメインループ
    while(turn < 2){
            //どちらの手番か表示
            if(turn == 0){
            cout<<"turn:●\n";
            }else{
            cout<<"turn:○\n";
            }
            //盤面表示
            disp();
    
            //fliper(Max_tate,Max_yoko,turn,Max_tate);
            //入力
            input(turn);
    
            //手番交替
            turn = (turn + 1) % 2;
            //CPUか人かを変える
            if(turnplayer==human){
            turnplayer=cpu;
            }else{
            turnplayer = human;
            }
            cout<<realflip<<"枚めくられました";
            //終了判定
            switch(checkEnd(turn)){
                case 1:
                    cout<<"取れる駒がありません"<<endl;
                    cout<<"手番をパスします"<<endl;
                    turn = (turn + 1) % 2;
                    break;
                case 2:
                    cout<<"ゲームを終了します"<<endl;
                    turn = 2;
                    checkWinner();
                    break;
                default:
                    break;
            }
    
        }
        return 0;
    }
    
[/code]

実行例：

[code]

    オセロ
    あなたは先行（●）です
    turn:●
    　 1 2 3 4 5 6 7 8
    10＋＋＋＋＋＋＋＋
    20＋＋＋＋＋＋＋＋
    30＋＋＋＋＋＋＋＋
    40＋＋＋●○＋＋＋
    50＋＋＋○●＋＋＋
    60＋＋＋＋＋＋＋＋
    70＋＋＋＋＋＋＋＋
    80＋＋＋＋＋＋＋＋
    1個取れます
    どこに置きますか？64
    1枚めくられましたturn:○
    　 1 2 3 4 5 6 7 8
    10＋＋＋＋＋＋＋＋
    20＋＋＋＋＋＋＋＋
    30＋＋＋＋＋＋＋＋
    40＋＋＋●○＋＋＋
    50＋＋＋●●＋＋＋
    60＋＋＋●＋＋＋＋
    70＋＋＋＋＋＋＋＋
    80＋＋＋＋＋＋＋＋
    1個取れます
    1枚めくられましたturn:●
    　 1 2 3 4 5 6 7 8
    10＋＋＋＋＋＋＋＋
    20＋＋＋＋＋＋＋＋
    30＋＋＋＋＋＋＋＋
    40＋＋＋●○＋＋＋
    50＋＋＋●○＋＋＋
    60＋＋＋●○＋＋＋
    70＋＋＋＋＋＋＋＋
    80＋＋＋＋＋＋＋＋
    1個取れます
    どこに置きますか？66
    2枚めくられましたturn:○(1個とれますと書いてあるが、みての通り66に置くと2個取得できる)
    　 1 2 3 4 5 6 7 8
    10＋＋＋＋＋＋＋＋
    20＋＋＋＋＋＋＋＋
    30＋＋＋＋＋＋＋＋
    40＋＋＋●○＋＋＋
    50＋＋＋●●＋＋＋
    60＋＋＋●●●＋＋
    70＋＋＋＋＋＋＋＋
    80＋＋＋＋＋＋＋＋
    2個取れます
    1枚めくられましたturn:●
    　 1 2 3 4 5 6 7 8
    10＋＋＋＋＋＋＋＋
    20＋＋＋＋＋＋＋＋
    30＋＋＋＋＋＋＋＋
    40＋＋＋●○＋＋＋
    50＋＋＋○●＋＋＋
    60＋＋○●●●＋＋
    70＋＋＋＋＋＋＋＋
    80＋＋＋＋＋＋＋＋
    1個取れます
    どこに置きますか？
    
[/code]


CC BY-SA 3.0
2017-08-02T13:28:58.733
36887
2017-08-03T08:52:57.877
2017-08-03T08:23:01.437
49
24209
question
c++
オセロで取得できる最大の枚数を出力する。


### 取得できる最大の枚数が異常な理由

このプログラムでは、`reverse`
関数において全てのマスを調べつつ、各マスについて8方向それぞれに対して裏返せるかチェックしています。その際、`number`
関数において裏返せる枚数をチェックすると共に、変数 `flip_max` にその枚数の最大値を記録しています。  
注意すべきは、`number`
関数は各方向ごとに呼び出されているため、ある一方向のみに裏返せる最大値が計算されているところです。実際に取得したいのは、8方向全てについての裏返せる枚数の和であるはずです。このため、関数の設計を変えて、8方向すべての
`flipno` を足したものの最大値を計算させるようにする必要があります。

### 補足

その他、蛇足かもしれませんが気になったことを少し補足しておきます。本題のバグが直ったら読んでください。

  * コンパイル時に出てくるエラーや警告をきちんと読む癖をつけた方が良さそうです。今のプログラムでも環境によっては動くでしょうが、以下のように微妙な点があります。 
    * 配列 `vec_tate` と `vec_yoko` を定義しているところで、型が書かれていません。
    * （これはコードをここへ投稿する際に起こったことかもしれませんが）コードの行末尾とコメントの間に全角スペース ` ` が紛れ込んでいます。これは半角スペース `` と見分けがつきにくく、しかもエラーを生むので、注意してください。エディタによっては設定を変えることで全角スペースを他と見分けやすくすることができるので、そうしておくと良いです。
    * 現在の C++ では、`cout` や `endl` を、`std::cout` や `std::endl` と書く方が良いです。これは名前空間と言って、`std` という名前空間の中にある `cout` や `endl` を意味しています。次の項目とも関係するため、合わせてお読みください。 （`using namespace std;` を使って名前空間を省略できるようにする書き方もあるのですが、慣れるまでは `std::` をつけて書いた方が良いと思います。）
  * `#include <iostream.h>` のように .h をつけて `#include` するのは C++ では[古い方法です](https://stackoverflow.com/q/214230/5989200)。C++ のヘッダファイルを読み込む際は `#include <iostream>` とします。しかも今回の場合、`#include` しているヘッダに C++ のものと C のものが混ざっており、更に分かりにくいことになっています。新しく書き直すと、以下のようになります。
[code]     #include <iomanip>

    #include <iostream>
    #include <cstdio>
    #include <ctime>
    #include <cstdlib>
    #include <string>
    
[/code]

c から始まっているファイルは C 言語のヘッダファイルとほぼ同じものです。たとえば `cstdio` は `stdio.h` とほぼ同じものです
([ほんの少し違いがある](https://stackoverflow.com/q/10460250/5989200)のですが、とりあえずは問題ないでしょう)。

    * ところで `cstdio` は `scanf` 関数や `printf` 関数を C++ で使うために必要ですが、今回のプログラムでは `iostream` で定義されている `std::cin` と `std::cout` しか使っていないため、`#include <cstdio>` という行自体を削除しても大丈夫です。
    * C++ の教科書ではたまに、std 名前空間の説明を省略するために `iostream.h` を使うことがあります。しかし最近の環境だとそもそも `iostream.h` 自体が存在しなかったりするため、これを使うのはやめたほうが良いでしょう。ただしこのことはオセロプログラムの本質とは関係ないため、とりあえず今はこのまま作り、後でもう一回ここに戻ってくるというのでも大丈夫だと思います。
  * 不必要にグローバルで定義されている変数があります。たとえば `flipno` は `number` 関数の中でしか使われていないため、`number` 関数のローカル変数にするのが良いでしょう。こうすることでプログラムの流れが読みやすくなり、また、考えてもいなかった場所で勝手にその変数が書き換わることを予防できます。
  * コードが適切にインデントできていない場所がところどころあります。コードをより読みやすくするためにも、インデントを正しくつけることを徹底した方が良いでしょう。
  * `input` 関数の中で入力がちゃんとした座標値であるか2度チェックしていますが、2回目だけあれば充分です。`place` を10で割った数が1以上8以下で、`place` の1桁目が1以上8以下ならば、`place` は11以上88以下であるからです。

これらについて調べてみても何か分からない点があれば、是非新しく質問してください。


CC BY-SA 3.0
2017-08-02T23:26:52.600
36893
2017-08-03T08:52:57.877
2017-08-03T08:52:57.877
19110
19110
36887
answer