yashigani?.days

週刊少年ジャンプについてだらだら書きます

ボウリングのスコア計算は難しい

前回紹介したフィッシュボウルをまた社内でやった. 前回はFizzBuzzがお題だったけど,今回は時間をとれることもあって難易度を上げて,ボウリングのスコア計算がお題になった. おもしろそうなので,同僚が挑戦しているのを横目に自分も Haskell でやってみた.

ちなみに挑戦したペアは2ペアあるけど,2ペアとも最後まで辿りつけなかった. さすがに40分じゃキツいと思う. 自分も割り込み入れつつダラダラやって,トータル3時間くらいかかった.

お題

ボウリングのスコアを計算するプログラムを作成する. ストライクはX,スペアは/,ガーターは-と表現し,ゲームの結果は以下のように入力される.

XXXXXXXXXXXX #パーフェクト
-------------------/5 # 9フレームまでガーターで10フレームでスペア 

書いたコード

反省

関数の入力にやたらタプルを使った

別にタプルじゃなくてよかったと思う.

foldl使ってて素人っぽい

これに関しては気に入らないならreverseすればいいだけなんだろうけど.

パターンマッチ使いすぎ?

whereでパターンマッチしてとってきたやつを返すってパターンがあんまり綺麗に思えない. どうやるのがよかったんだろう.

総評

今回はとりあえず Haskell でやってみたけど,がんばってやっただけでいい感じに書けてないと思う. もっといい感じに書けるようになりたい.

以下方針とかはまったことを書いておきます. やろうと思う人は読まないように!

方針

  1. ボーナスは(2, 1) = (その投擲, 次の投擲)みたいに持つ
  2. 投擲ごとに投擲の結果と前の投擲でのトータルスコア,投擲のスコア,ボーナスをとってトータルスコアと投擲のスコア,次のボーナスを計算
  3. 10フレームはボーナス加算が無いので最後に減算

はまったこと

ある投擲のスコアを計算するには前の投擲のスコアが必要

スペアをとった投擲ではその投擲でのスコアを計算するのに前回の投擲のスコアが必要なことに気づかなくて,計算する関数を書いてる途中に気づいて困った. 関数のインターフェースが変わってしまうし,地味に最初の壁だった.

ボーナスの計算

当初スペアのボーナスとストライクのボーナスを別に考えていてややこしくなってしまった. 特にストライクのボーナスが重複してるときのハンドリングがややこしい. そこで,方針2のように考えることでボーナスの処理が簡単になった. ある投擲のボーナスが(b1, b2)だとすると,次の投擲のボーナスは(b2, 0)にその投擲で発生したボーナスを加算すればいいだけ. まとめると下の表になる.

結果 次のボーナス
ストライク (b2 + 1, 1)
スペア (b2 + 1, 0)
それ以外 (b2, 0)

10フレーム目の投擲にはボーナスが加算されない

書き終わったーと思ってパーフェクトのパターンを見てみたら330ってなってなんぞってなった. 10フレームだけ別の処理にするってのは面倒でダサいから,10フレームの投擲をとってきて,足しすぎたボーナスを減算することにした.