ポーカーの役判定
久々にどう書く?orgに挑戦。既に半月前のですが、何とか自分でもヒントとか無しで解けそうだったのでやってみました。こういうのをLispかHaskellで、すらすら書けようになりたい。
本体はpoker_hand。それより下はテスト実行用。本体はごちゃっとしたのをすっきりさせたいところですが、時間切れです。
def poker_hand(card) # suits, numbers = card.split(//).partition do |c| c[/[SCHD]/] end suits = (0..4).collect do |i| card[i * 2, 1] end numbers = (0..4).collect do |i| " A23456789TJQK".index(card[i * 2 + 1, 1]) end.sort # 事前ソート # p suits, numbers straight = numbers == (numbers[0]..numbers[0]+4).to_a if suits.uniq.size == 1 # flush return "Royal flush" if numbers == [1, 10, 11, 12, 13] return "Straight flush" if straight return "Flush" elsif straight return "Straight" elsif numbers.uniq.size == 2 # 11112 12222 / 11122 11222 if numbers[1] == numbers[3] # 2番目と4番目が一緒 return "Four of a kind" else return "Full house" end elsif numbers.uniq.size == 3 # 11223 11233 12233 / 11123 12223 12333 if numbers.select do |n| n == numbers[2] end.size == 3 # 3番目と同じ要素が3個 return "Three of a kind" else return "Two pair" end elsif numbers.uniq.size == 4 return "One pair" end "No pair" end DATA.readlines.each do |line| card, expect = line.chomp.split(/ /, 4)[2..3] result = poker_hand(card) f = if result == expect then "OK" else "NG" end puts "[#{f}] card:#{card}, expect:#{expect}, result:#{result} " end __END__ % ./poker SQSJSASKST Royal flush % ./poker D9D7D6D5D8 Straight flush % ./poker C2D2S2H3H2 Four of a kind % ./poker C2D3S2H3H2 Full house % ./poker S9S4S8STSJ Flush % ./poker C4H7D5S6H3 Straight % ./poker S6H6C5DQC6 Three of a kind % ./poker S6HQC5DQC6 Two pair % ./poker S6H4C5DQC6 One pair % ./poker SJSQSKSAC2 No pair
追記:Haskellバージョン改良版を元に整理してみました。もしかしたら、正規表現を駆使すればもっと簡潔に書けるのかもしれませんが、文字列でなく数列として考えてみたかったので、配列による判定となっています。
def poker_hand(cards) indices = (0..9).partition do |i| i % 2 == 0 end suits = indices[0].collect do |i| cards[i].chr end ranks = indices[1].collect do |i| cards[i].chr end.sort numbers = ranks.collect do |rank| "A23456789TJQK".index(rank) + 1 end.sort flush = suits.uniq.size == 1 straight = numbers == (numbers[0]..numbers[0]+4).to_a return "Royal flush" if flush && ranks.join == "AJKQT" return "Straight flush" if flush && straight return "Flush" if flush return "Straight" if straight groups = ranks.uniq.collect do |item| ranks.select do |rank| rank == item end.size end.sort.reverse return "Four of a kind" if groups == [4, 1] return "Full house" if groups == [3, 2] return "Three of a kind" if groups == [3, 1, 1] return "Two pair" if groups == [2, 2, 1] return "One pair" if groups == [2, 1, 1, 1] return "No pair" end