ライフゲーム


life game
life game
前に社内のコーチの認証に参加して、第1ラウンドはこの経典の生命のゲームで、具体的な需要は羅列しないで、ネット上で1枚の大きい映画を探します.基本的なニーズに新たに追加されたニーズは、入力ファイル('0','1'で表される)に基づいて、最終反復結果ファイル('','$')を印刷し、c++で実現し、コード量は小さくなく、学習に重点を置くことです.すべてのコードにgitをアップロードし、ここで自分の収穫をまとめます.今回はifelseなどの条件で判断できないという特殊な要求もあり、今回のコードにも少なからぬ難易度を高めた.
1試験用例
1.1テストケース設計
1.ライフゲームの基本的なニーズに基づいて3*3の9つの基本的な使用例を設計し、
  • FtWorld_3_3.should_DEAD_board_when_1_neighbor
  • FtWorld_3_3.should_STAY_board_when_2_neighbor
  • FtWorld_3_3.should_ALIVE_board_when_3_neighbor
  • FtWorld_3_3.should_DEAD_board_when_4_neighbor
  • FtWorld_3_3.should_DEAD_board_when_5_neighbor
  • FtWorld_3_3.should_DEAD_board_when_6_neighbor
  • FtWorld_3_3.should_DEAD_board_when_7_neighbor
  • FtWorld_3_3.should_DEAD_board_when_8_neighbor

  •   2.デザインがやや複雑なシーン4*4の例に基づいて
  • 反復により次世代結果
  • が得られる.
  • 複数回反復して安定結果
  • を得た.
    3.需要を満たす用例20*20の用例
    2設計構想
    2.1インタフェースの設計:
  • worldの作成には、元の図とサイズ
  • が必要です.
  • worldは次世代
  • に進化する必要がある.
  • world出力
  • も必要
    DEFINE_ROLE(World)
    {
        ABSTRACT(void startWith(const char*
                , const WorldSize&));
        ABSTRACT(void nextGeneration());
        ABSTRACT(char* output());
    };

    2.2 world設計
    自己反復が必要であり,A,Bの2つの世界が出力を交換し,互いにソースとターゲットである必要がある.そこでworldgroupクラスが生成され,このクラスは内部実装に属するためworldFactoryに置かれる.
    2.3 cell設計
    状態遷移インタフェースのみが提供され、所与の隣接状況に応じて次の状態に遷移する.cellは現在の自身の状態のみを保存します.
    2.4特殊需要設計
    if-elseが使用できない条件に対して,主なボトルネックは境界判断にある.境界判断問題を解決するために、私はworldが初期世界より1周大きいworldに設計され、境界を0にし、どの位置でも有効な隣人情報を得ることができるようにしたと話しています.その他cell状態に関する判断は,テーブル駆動により解決する.
    3再構築
    3.1テスト再構築
    再構築されたテスト例は、より強力な表現能力を持ち、実行結果を一目瞭然にします.
    FtWorld_3_3.should_DEAD_board_when_8_neighbor
    ***world start with :***
    111
    111
    111
    ***after evolve 1 times:***
    ***world would be :***
    101
    000
    101

    3.2設計再構築
    3.2.1入出力再構築
    実装が増加するにつれて,テストの拡張により,コードの重複がますます目立つようになった.主に原始データの入力、テスト用例の入力、テスト用例の出力、最終印刷の出力に現れ、変化したのは細胞の生と死の表現形式であり、そこでインタフェースに対して以下の再設計を行った.
    DEFINE_ROLE(World)
    {
        ABSTRACT(void startWith(const char*
                , const CellStateChars&
                , const WorldSize&));
        ABSTRACT(void nextGeneration());
        ABSTRACT(void output(std::ostream&, const CellStateChars&));
    };

    CellStateCharsは、ユーザーが入力と出力を選択するライフステートインタフェースとして設計されています.
    template<char ALIVE_CHAR, char DEAD_CHAR>
    struct GenericCellStateChars : CellStateChars
    {
        OVERRIDE(char getAliveChar() const) {return ALIVE_CHAR;}
        OVERRIDE(char getDeadChar() const) {return DEAD_CHAR;};
        OVERRIDE(CellState getState(char state) const)
        {
            static CellState states[] = {DEAD, ALIVE};
            return states[ALIVE_CHAR == state];
        };
    };
    
    static GenericCellStateChars<'1', '0'> input_chars;
    static GenericCellStateChars<1, 0> ft_chars;
    static GenericCellStateChars<'$', ' '> output_chars;

    テストと正式なコードを兼ね備えており、テストに新しいインタフェースを追加する必要はありません.さらにostreamの出力方式を採用し,ofstreamにより最終結果を出力し,stringstreamによりテスト検証を行った.元のデータ入力コード:
    World& world = WorldFactory::create();
    world.startWith(init, input_chars, GenericWorldSize<20,20>());

    最終結果出力:coutがコンソールに出力
    ofstream of(str);
    world.output(of, output_chars);
    world.output(cout, output_chars);
    of.close();

    テスト結果の検証
    bool isEq(World& ret, const char* right)
    {
        std::ostringstream ss;
        ret.output(ss, ft_chars);
    
        const char*  left = ss.str().c_str();
    
        int offset = 0;
        for(int i = 0; i< ROW * COL ; i++)
        {
            if(left[(i+offset)] == '
    ') offset++;//filter the 
            if(left[(i+offset)] != right [i])             return false;     }     return true; }

    3.2.2 world再構築
    第1版のworldはほとんどすべての職責を担っているが、worldはcellにとって周辺のcell情報しか提供していないため、worldに対してneighborインタフェースを抽象化し、単一のWorldで実現し、隣人情報を計算する方法を提供している.
    DEFINE_ROLE(Neighbor)
    {
        ABSTRACT(int countNeighbors(const Position&));
        ABSTRACT(Cell& getSrcCell(const Position&));
    };

    3.2.3 cell再構築
    さらに職責を分離し,位置情報はcellオブジェクトに固定されるべきである.cellに対してpositionインタフェースを抽象化し、位置情報を提供し、worldが作成されると、可達情報も固定されるため、可達隣人がbitmapを打つことを抽象化し、毎回隣人情報を計算することを避ける.
    struct CellObject : Cell
    {
        CellObject ();
    
    private:
        OVERRIDE(void init(const Position&, CellState));
        OVERRIDE(bool isAlive() const);
        OVERRIDE(bool isEndl() const);
        OVERRIDE(void evolve(Neighbor&));
        OVERRIDE(CellState getState() const);
    
    private:
        CellState state;
        Position pos;
    };
    struct Position
    {
        Position();
        Position(int index, const WorldSize&);
    
        bool isEndl() const;
        int getIndex() const;
        int getCol() const;
        ArivalMap getArivalMap() const;
    
    private:
        int index;
        int col;
        ArivalMap arival;
    };

    4まとめ
    現在完成した結果は必ず最終結果とすることができず、まだ大きな改善空間があり、以下の痛み点を残している.
  • 隣人達関係に関する計算は複雑すぎて、ifelseで使用されるテーブル駆動を除去するために、マクロマルチステートによって表現力を向上させるが、依然として複雑すぎる.
  • 結果出力の統一のためにクラスとインタフェース設計に妥協し,過剰設計インタフェースが存在する.

  • 自己感覚の重複解消はよくできていますが、後で再構築し続ける時間があれば、他の言語で実現してみてください.
    Date: 2015-03-03T22:47+0800
    Author: zhangchao
    Org version 7.9.3f with Emacs version 24
    Validate XHTML 1.0