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とか使ってみた。本当のポーカー勝敗判定として使う場合にはさらにスートの考慮が必要。