Euler : Problem 54

Posted by YpsilonTAKAI On 2011年7月3日日曜日 0 コメント
ポーカーの勝敗を判定する問題。

やたら長いけど身は少ない。

まず、ファイルを読み込んで、扱いやすくする。
  [[8 :club] [10 :spade] [13 :club] [8 :heart] [4 :spade]]

役を判定する関数(rank-hand)を使って、役で判定。
同じ役ならのところがちょっと考えた。
結局、同じ役なんだから、手札を枚数の多い順に並べて、
 2 3 1 3 1 3 3  ->  (3 3 3 3) (1 1) (2)
1つだけにして
 (3 3 3 3) (1 1) (2)  -> 3 1 2
頭から大小比較すればいいということに気づいた。


ところで、A,1,2.3.4 をストレートと判定していないことに、今、気づいたんだけど、
答えはあってたみたい。





;;
;; Problem 54 : 2011/6/20
;; "Elapsed time: 430.744131 msecs"

(use '[clojure.contrib.duck-streams :only (reader read-lines)])
(use '[clojure.contrib.str-utils :only (re-split)])

(defn group-same
"Split into same value group
ex. (2 3 1 3 1 3 3) -> [(1 1) (2) (3 3 3 3)]"
([col] (group-same (sort col) []))
([col res]
(if (empty? col)
res
(let [[head tail] (split-with #(= (first col) %) col)]
(recur tail (conj res head ))))))


(def card-value
{\2 2, \3 3, \4 4, \5 5, \6 6, \7 7, \8 8,
\9 9, \T 10, \J 11, \Q 12, \K 13, \A 14})

(def suite-value
{\C :club, \H :heart, \D :diamond, \S :spade})

(def hand-rank
{:high-card 0 :one-pair 1 :two-pairs 2 :three-ofa-kind 3 :straight 4 :flush 5
:full-house 6 :four-ofa-kind 7 :straight-flush 8 :royal-flush 9})

(defn expand-card [note]
"extract notation to data 8C -> [8 :club]"
(let [[n s] (seq note)]
[(card-value n) (suite-value s)]))


(defn how-many-pairs [hand]
(count (filter #(= % 2) (map count (group-same (map first hand))))))

(defn how-many-3ok [hand]
(count (filter #(= % 3) (map count (group-same (map first hand))))))

(defn how-many-4ok [hand]
(count (filter #(= % 4) (map count (group-same (map first hand))))))

(defn one-pair? [hand]
(= 1 (how-many-pairs hand)))

(defn two-pair? [hand]
(= 2 (how-many-pairs hand)))

(defn three-of-kind? [hand]
(= 1 (how-many-3ok hand)))

(defn straight? [hand]
(apply = (map #(- %1 %2) (sort (map first hand)) (range 5))))

(defn flush? [hand]
(apply = (map second hand)))

(defn full-house? [hand]
(and (= 1 (how-many-pairs hand))
(= 1 (how-many-3ok hand))))

(defn four-of-kind? [hand]
(= 1 (how-many-4ok hand)))

(defn straight-flush? [hand]
(and (straight? hand)
(flush? hand)))

(defn royal-flush? [hand]
(and (flush? hand)
(= (sort (map first hand)) '(10 11 12 13 14))))

(defn rank-hand [hand]
(cond (royal-flush? hand) :royal-flush
(straight-flush? hand) :straight-flush
(four-of-kind? hand) :four-ofa-kind
(full-house? hand) :full-house
(flush? hand) :flush
(straight? hand) :straight
(three-of-kind? hand) :three-ofa-kind
(two-pair? hand) :two-pairs
(one-pair? hand) :one-pair
:else :high-card))

(defn poker-sort [hand]
"Sort cand num. set letf according to card count.
ex. [2 2 3 7 7 8] -> (7 2 8 3) : 7 and 2 are two cards."
(map first
(sort (fn [a b]
(if (= (count a) (count b))
(> (first a) (first b))
(> (count a) (count b))))
(group-same (map first hand)))))

(defn compare-poker [p1 p2]
"compare porker-sorted cards."
(if (= (first p1) (first p2))
(recur (rest p1) (rest p2))
(if (> (first p1) (first p2))
:p1
:p2)))


(defn which-is-win [[p1-hand p2-hand]]
(let [p1-rank (hand-rank (rank-hand p1-hand))
p2-rank (hand-rank (rank-hand p2-hand))]
(cond (> p1-rank p2-rank) :p1
(< p1-rank p2-rank) :p2
:else
(let [p1-sort (poker-sort p1-hand)
p2-sort (poker-sort p2-hand)]
(compare-poker p1-sort p2-sort)))))

;; one hand data
;;[[8 :club] [10 :spade] [13 :club] [8 :heart] [4 :spade]]

(count (filter #(= :p1 %)
(let [file-data (read-lines "poker_test.txt")
input-datas (map #(split-at 5 (map expand-card (re-split #"\s+" %))) file-data)]
(map which-is-win input-datas))))
;;

0 コメント:

コメントを投稿