What is it, naokirin?

テスト駆動開発の練習(15) -C++&GoogleTestでポーカーの役判定-

久々にC++&GoogleTestをやりました。

最近、写経や試しに使ってみるとかしかしてなかったのと、他の言語がある程度身についてしまっていて書き間違いがやり始めたときに多少目立ちましたが、後半で少し勘を取り戻せた感じです。

今回はスートは考えずに数字のみでポーカーの役を判定するコードを作りました。なので、フラッシュやストレートフラッシュ、ロイヤルストレートフラッシュなどは判定できません。機会があればスートも入れて判定できるようにして、さらに二つ以上の役から強い役の判定をできるようにもしたいところです。

そして今回のコード。リファクタリングはほぼかかってないのですが、今回の場合は私はメソッドへの分割くらいしか思いつかないですね・・・。
PokerTest.cpp

#include <gtest/gtest.h>
#include <boost/scoped_ptr.hpp>
#include "Poker.h"

class PokerTest : public ::testing::Test {
public:
  Poker *poker;
  
protected:
  virtual void SetUp() {
    poker = new Poker();
  }
  
  virtual void TearDown() {
    delete poker;
  }
};

TEST_F(PokerTest, pokerHandsTest) {
  int onePair[5] = {1, 1, 2, 4, 3};
  EXPECT_EQ("one pair", poker->pokerHands(onePair));

  int twoPair[5] = {1, 2, 3, 1, 2};
  EXPECT_EQ("two pair", poker->pokerHands(twoPair));
  
  int fullHouse[5] = {1, 2, 1, 1, 2};
  EXPECT_EQ("full house", poker->pokerHands(fullHouse));
  
  int trips[5] = {1, 2, 2, 3, 2};
  EXPECT_EQ("three of a kind", poker->pokerHands(trips));
  
  int quads[5] = {13, 1, 1, 1, 1};
  EXPECT_EQ("four of a kind", poker->pokerHands(quads));
  
  int straight[5] = {1, 2, 3, 4, 5};
  EXPECT_EQ("straight", poker->pokerHands(straight));
  
  int boo[5] = {1, 3, 5, 4, 7};
  EXPECT_EQ("boo", poker->pokerHands(boo));
  
  int foul[5] = {1, 1, 1, 1, 1};
  EXPECT_EQ("foul!", poker->pokerHands(foul));
}


Poker.h & Poker.cpp

/* Poker.h */
#include <string>

class Poker {
public:
  Poker();
  ~Poker();
  
  std::string pokerHands(int array[5]);
};

/* Poker.cpp  */
#include "Poker.h"
#include <iostream>
#include <algorithm>

Poker::Poker() {}

Poker::~Poker() {}

std::string Poker::pokerHands( int array[5] ) {
  
  int sameCard = -5;
    
  for ( int i = 0; i < 5; ++i ) {
    for ( int j = i; j < 5; ++j ) {
      if ( array[i] == array[j] ) {
	    ++sameCard;
      }
    }
  }
  
  if ( sameCard == 0 ) {
    std::sort(array, array + 5);
	bool straightFlag = true;
	for(int i = 0; i < 4; ++i)
	  straightFlag = straightFlag && ( array[i] == array[i+1] - 1 );
	if ( straightFlag )
	  return "straight";
	else
	  return "boo";
  }
  else if ( sameCard == 1 )
    return "one pair";
  else if ( sameCard == 2 )
    return "two pair";
  else if ( sameCard == 3 )
    return "three of a kind";
  else if( sameCard == 4 )
    return "full house";
  else if ( sameCard == 6 )
    return "four of a kind";
  else
    return "foul!";
}

スートもちゃんと判定に入れる場合はカード用の構造体とスート用の列挙型を作るのが早いかなと思います。まあ、C++0x以前では整数があてられるだけの残念な列挙型ですが・・・。
ストレートの判定をどうするか迷ったのですが、ソートをかけてから判定した方が楽だろうということでソート→判定をしています。

ちなみに一致する数を数えている部分は配列でなければstd::count_ifなどを用いてもよかったかなと思います。ただ、今のままだとfor文が一つcount_ifに代わるだけですが・・・。

あと、少しホワイトボードなどに書きだして情報整理をしたりしてみていました。たまにちょっとした勘違いをしたりするのも書きだしてみると意外と簡単なことだったりします。一致する枚数の判定などもちょっとずつ落とし穴があって書きだしたときに「そうか」と気がつく時も結構ありました。やっぱりアナログも重要ですね。

うーん、実はこんなことをしているより物理をやらないとマズいんですが、そっちの方はさっぱり進んでません。どうにかしなければ・・・。