100点以下の残りスコアのときのダーツの上がり手順

オイラープロジェクト109問目は、ダーツの計算問題。むかし夜な夜な投げていたので懐かしい。100以下の残りスコアのとき、ダブル・アウトで上がれる得点の組み合わせはいくつあるか。Clojureで書いてみた。

(def all-shots
     (conj
      (for [r (range 1 4) t (range 1 21)]
	[r t]) [0 0] [1 25] [2 25]))

(defn calc-score [pair]
  (let [[t r] pair]
    (* t r)))

(defn calc-total [shots]
  (apply +
	 (map #(calc-score %) shots)))

(defn sort12 [shot]
  (let [[a b c] shot]
    (conj (sort [a b]) c)))

(defn double? [shot]
  (let [[r _] shot]
    (= r 2)))

(defn under-n [n]
  (for [a all-shots b all-shots c all-shots
	:when (double? c) :when (< (calc-total [a b c]) n)]
      [a b c]))

(defn numbers-of-chechout [n]
  (count (distinct (map sort12 (under-n n)))))

(println (time
	  (numbers-of-chechout 100)))

どのぐらいの組み合わせ数になるなのか、ずっと気にはなっていた。100以下だと4万ぐらいという感じで、想像していたよりずっと多い。プロのダーツプレイヤーともなると、瞬時に残りスコアから可能な上がりパターンを数個教えてくれたりしていたけど、やっぱりあれは結構すごいことなんじゃないかといまさら思ってみたり。

Project Euler掲示板で、ほかのユーザーがPythonJavaで書いた多重ループの解法を見ると、ループの境界や範囲が錯綜していて、かなり読みづらい。一番ストレートで美しいと思ったのは、やっぱりHaskellで書かれたものだった。けど、それは単にそのHaskellerが優秀という可能性もある。

そしてまたしてももっとも短いコードはAPLやJといったキテレツ系の言語による解法だった。

……、と思ったけど、Pythonでも非常に美しいコードを書いている人がいた。内包表記があってインデントで抽象ループを書けば、ClojurePythonも似たようなものなのかも。Clojureの本領は並行処理とか、マクロ、マルチメソッドあたりを使って初めて発揮されるってことかしら。しかし、マクロもマルチメソッドも強力だけど、実際にはあまり使いまくるという感じでもないというし、よく分からん。

世代が古めのプログラミング言語だとやっぱり読みづらいよ! と、思ったら、Perlでもすごいコードを書くヤツがいてビビった。

ていうか、C++で書かれたシンプルなコードを見て、なんだ、これがベストじゃん、最速っぽいしとか思ったりもして。いろいろあって面白いなー、Project Eulerは。