Project Euler 54

カードゲームのポーカーでは, 手札は5枚のカードからなりランク付けされている. 役を低い方から高い方へ順に並べると以下である.

  • 役無し: 一番値が大きいカード
  • ワン・ペア: 同じ値のカードが2枚
  • ツー・ペア: 2つの異なる値のペア
  • スリーカード: 同じ値のカードが3枚
  • ストレート: 5枚の連続する値のカード
  • フラッシュ: 全てのカードが同じスート (注: スートとはダイヤ・ハート・クラブ/スペードというカードの絵柄のこと)
  • フルハウス: スリーカードとペア
  • フォーカード: 同じ値のカードが4枚
  • ストレートフラッシュ: ストレートかつフラッシュ
  • ロイヤルフラッシュ: 同じスートの10, J, Q, K, A

ここでカードの値は小さい方から2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, Aである. (訳注:データ中で10は'T'と表される)

もし2人のプレイヤーが同じ役の場合には, 役を構成する中で値が最も大きいカードによってランクが決まる: 例えば, 8のペアは5のペアより強い (下の例1を見よ). それでも同じランクの場合には (例えば, 両者ともQのペアの場合), 一番値が大きいカードによってランクが決まる (下の例4を見よ). 一番値が大きいカードが同じ場合には, 次に値が大きいカードが比べれられ, 以下同様にランクを決定する.

例:

プレイヤー1 プレイヤー2 勝者
1 5H 5C 6S 7S KD (5のペア) 2C 3S 8S 8D TD (8のペア) プレイヤー2
2 5D 8C 9S JS AC (役無し, A) 2C 5C 7D 8S QH (役無し, Q) プレイヤー1
3 2D 9C AS AH AC (Aのスリーカード) 3D 6D 7D TD QD (ダイヤのフラッシュ) プレイヤー2
4 4D 6S 9H QH QC (Qのペア, 9) 3D 6D 7H QD QS (Qのペア, 7) プレイヤー1
5 2H 2D 4C 4D 4S (4-2のフルハウス) 3C 3D 3S 9S 9D (3-9のフルハウス) プレイヤー1

poker.txtには1000個のランダムな手札の組が含まれている. 各行は10枚のカードからなる (スペースで区切られている): 最初の5枚がプレイヤー1の手札であり, 残りの5枚がプレイヤー2の手札である. 以下のことを仮定してよい

  • 全ての手札は正しい (使われない文字が出現しない. 同じカードは繰り返されない)
  • 各プレイヤーの手札は特に決まった順に並んでいるわけではない
  • 各勝負で勝敗は必ず決まる

1000回中プレイヤー1が勝つのは何回か?

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>

using namespace std;

string p1[1000][5], p2[1000][5];


enum pokerHand{
	ROYAL_FLUSH = 9,
	STRAIGHT_FLUSH = 8,
	FOUR_OF_A_KIND = 7,
	FULL_HOUSE = 6,
	FLUSH = 5,
	STRAIGHT = 4,
	THREE_OF_A_KIND = 3,
	TWO_PAIRS = 2,
	ONE_PAIR = 1,
	HIGH_CARD = 0
};

struct handStrength{
	pokerHand p;
	vector<int> v;
};


int cardToInt(char c){
	if(c=='T') return 10;
	else if(c=='J') return 11;
	else if(c=='Q') return 12;
	else if(c=='K') return 13;
	else if(c=='A') return 14;
	else return c-48;
}

bool isFlush(string s){
	return s[0]==s[1] && s[1]==s[2] && s[2]==s[3] && s[3]==s[4];
}

bool isStraight(vector<int> v){
	if(v[0]!=14){
		return v[0]==v[1]+1 && v[1]==v[2]+1 && v[2]==v[3]+1 && v[3]==v[4]+1;
	}
	
	// A,5,4,3,2 or A,K,Q,J,10
	return (v[1]==5 && v[2]==4 && v[3]==3 && v[4]==2) || (v[1]==13 && v[2]==12 && v[3]==11 && v[4]==10);
}

handStrength judgeHand(vector<string> v, vector<int> vnum, string vsuit){

	handStrength ret;

	sort(vnum.begin(), vnum.end(), greater<int>());
	
	// ROYAL FLUSH
	if(vnum[0]==14 && vnum[1]==13 && vnum[2]==12 && vnum[3]==11 && vnum[4]==10 && isFlush(vsuit)){
		ret.p = ROYAL_FLUSH;
		ret.v = vnum;
		return ret;
	}

	// STRAIGHT FLUSH
	else if(isFlush(vsuit) && isStraight(vnum)){
		ret.p = STRAIGHT_FLUSH;
		ret.v = vnum;
		return ret;
	}
	// FOUR OF A KIND
	else if(vnum[1]==vnum[2] && vnum[2]==vnum[3] &&( vnum[0]==vnum[1] || vnum[3]==vnum[4] )){
		ret.p = FOUR_OF_A_KIND;
		ret.v.push_back(vnum[2]);
	}
	// FULL HOUSE
	// ソートしているので、FULL HOUSEならば前2枚と後ろ2枚は等しい。
	// あとは3枚目のカードが前、または後ろの2枚と等しければよい。

	else if(vnum[0]==vnum[1] && vnum[3]==vnum[4] && (vnum[2]==vnum[1] || vnum[2]==vnum[3] )){
		ret.p = FULL_HOUSE;
		if(vnum[2]==vnum[1]){
			ret.v.push_back(vnum[0]);
			ret.v.push_back(vnum[4]);
		}
		else{
			ret.v.push_back(vnum[4]);
			ret.v.push_back(vnum[0]);
		}
		return ret;
	}

	// FLUSH
	else if(isFlush(vsuit)){
		ret.p = FLUSH;
		ret.v = vnum;
		return ret;
	}

	// STRAIGHT
	else if(isStraight(vnum)){
		ret.p = STRAIGHT;
		ret.v = vnum;
		return ret;
	}
	
	// THREE OF A KIND
	int numnum[15];
	for(int i=0 ; i<=14 ; i++){
		numnum[i]=0;
	}

	// どの数字のカードが何枚あるかカウントする
	for(int i=0 ; i<5 ; i++){
		numnum[vnum[i]]++;
	}

	// THREE OF A KIND, TWO_PAIRS, ONE_PAIR 判定
	for(int i=1 ; i<=14 ; i++){
		if     (numnum[i]==3)                      ret.p = THREE_OF_A_KIND;
		else if(numnum[i]==2 && ret.p == ONE_PAIR) ret.p = TWO_PAIRS;
		else if(numnum[i]==2)                      ret.p = ONE_PAIR;
	}

	// THREE OF A KIND
	if(ret.p == THREE_OF_A_KIND){
		for(int i=1 ; i<=14; i++){
			if(numnum[i]==3) ret.v.push_back(i);
		}
		for(int i=1 ; i<=14 ; i++){
			if(numnum[i]==1) ret.v.push_back(i);
		}
		sort(ret.v.begin()+1, ret.v.end());

		return ret;
	}

	// TWO PAIRS or ONE PAIR
	bool isOnePair = false;

	int tmp1 = 0 , tmp2 = 0;
	for(int i=1 ; i<=14 ; i++){
		if(numnum[i]==2 && !isOnePair){
			isOnePair = true;
			tmp1 = i;
		}
		else if(numnum[i]==2 && isOnePair){
			tmp2 = i;
			ret.p = TWO_PAIRS;

			if(tmp1 > tmp2){
				ret.v.push_back(tmp1);
				ret.v.push_back(tmp2);
			}
			else{
				ret.v.push_back(tmp2);
				ret.v.push_back(tmp1);
			}
		}
	}

	if(ret.p == TWO_PAIRS){
		for(int i=1 ; i<=14 ; i++){
			if(numnum[i]==1){
				ret.v.push_back(i);
				return ret;
			}
		}
	}

	if(isOnePair){
		ret.p = ONE_PAIR;
		ret.v.push_back(tmp1);
		for(int i=1 ; i<=14 ; i++){
			if(numnum[i]==1) ret.v.push_back(i);
		}
		sort(ret.v.begin()+1, ret.v.end(), greater<int>());
		return ret;
	}
	
	// HIGH CARD
	ret.p = HIGH_CARD;
	ret.v = vnum;
	return ret;

}

// n番目の勝負の勝敗を決定する。
// player1が勝ちの場合trueを返す。
bool judge(int n){

	vector<string> v1,v2;
	string v1suit, v2suit;
	vector<int> v1num, v2num;

	for(int i=0 ; i<5 ; i++){
		v1.push_back(p1[n][i]);
		v2.push_back(p2[n][i]);
	}
	for(int i=0 ; i<5 ; i++){
		v1num.push_back(cardToInt(v1[i][0]));
		v2num.push_back(cardToInt(v2[i][0]));
		v1suit.push_back(v1[i][1]);
		v2suit.push_back(v2[i][1]);
	}

	handStrength j1, j2;
	j1 = judgeHand(v1, v1num, v1suit);
	j2 = judgeHand(v2, v2num, v2suit);

	//cout << j1.p << " " << j2.p << endl;

	if(j1.p>j2.p) return true;
	else if(j1.p==j2.p){
		for(int i=0 ; i<j1.v.size() ; i++){
			//cout << j1.v[i] << " " << j2.v[i] << endl;
			if     (j1.v[i] > j2.v[i]) return true;
			else if(j1.v[i] < j2.v[i]) return false;
		}
	}
	return false;
}


int main(){
	ifstream ifs("dat054.txt");
	string tmp;

	// ファイル読み込み
	for(int i=0 ; i<1000 ; i++){
		for(int j=0 ; j<5 ; j++){
			ifs >> tmp;
			p1[i][j] = tmp;
		}

		for(int j=0 ; j<5 ; j++){
			ifs >> tmp;
			p2[i][j] = tmp;
		}
	}

	int cnt = 0;
	
	for(int i=0 ; i<1000 ; i++){
		if(judge(i)){
			cnt++;
			//cout << "p1 win" << endl;
		}
		//else cout << "p2 win" << endl;
	}
	
	cout << cnt << endl;
}

めっちゃ長くなった。あとenumとか使ってみた。本当のポーカー勝敗判定として使う場合にはさらにスートの考慮が必要。