こんにちは!じゃいごテックのあつしです。
今回はpaizaレベルアップ問題集から野球の審判という問題を解説していきたいと思います。こちらは過去にスキルチェックで実際に出題されていた問題らしいです!
難易度は 1276±10 で、C級としては簡単な問題だと思います。
野球の審判 (paizaランク C 相当) を解いてみる
※ paiza レベルアップ問題集 スキルチェック過去問題セットより
問題
野球の各打者はストライクが 3 つたまるとアウトとなり、ボールが 4 つたまるとフォアボールとなります。
アウトあるいはフォアボールになると、この打者の番は終了します。
あなたはストライクとボールを判定してコールする審判です。
その場の状況に合わせて適切なコールを出しましょう。
【コール一覧】
- ストライクが 1 〜 2 つたまったとき → "strike!"
- ストライクが 3 つたまったとき → "out!"
- ボールが 1 〜 3 つたまったとき → "ball!"
- ボールが 4 つたまったとき → "fourball!"
コールの例
ある打者の番における投球の結果 (ストライクまたはボール) が与えられるので、各投球に対してどのようなコールをすればよいかを出力してください。
なお、実際の野球にあっても上記にないルール (ヒット、ファウルなど) については考慮する必要はありません。
1 球目: ボール → "ball!"
2 球目: ストライク → "strike!"
3 球目: ボール → "ball!"
4 球目: ストライク → "strike!"
5 球目: ストライク → "out!"
5 球目では、ボールが 2 つ、ストライクが 3 つたまったのでこの打者はアウトとなります。
1 球目: ボール → "ball!"
2 球目: ストライク → "strike!"
3 球目: ボール → "ball!"
4 球目: ボール → "ball!"
5 球目: ストライク → "strike!"
6 球目: ボール → "fourball!"
6 球目では、ストライクが 2 つ、ボールが 4 つたまったのでこの打者はフォアボールとなります。
入力される値
入力は、以下のフォーマットで与えられます。
N
s_1
s_2
...
s_n
1 行目に合計の投球数を表す整数 N が与えられます。
続く N 行のうち i 行目 (1 ≦ i ≦ N) に i 番目の投球の結果を表す文字列 s_i が与えられます。i 番目の投球がストライクであれば s_i は "strike"、ボールであれば "ball" となります。
入力は合計で N + 1 行からなり、入力値最終行の末尾に改行が1つ入ります。
入力値最終行の末尾に改行が1つ入ります。
期待する出力
各投球に対する適切なコールを以下の形で出力してください。
c_1
c_2
...
c_N
期待する出力は N 行からなります。
N 行のうちの i 行目 (1 ≦ i ≦ N) に問題文の条件に即して打者へのコールを表す文字列 c_i を "strike!", "ball!", "out!", "fourball!" のいずれかで出力してください。
出力の N 行目の末尾に改行を 1 つ入れ、余計な文字、空行を含んではいけません。
条件
すべてのテストケースにおいて、以下の条件をみたします。
- 3 ≦ N ≦ 6
- s_i (1 ≦ i ≦ N) は英字小文字で構成される文字列で、"strike", "ball" のいずれか。
- 投球はこの打者の番がちょうど終了するまで続きます。
すなわち、最終的にこの打者にはアウトかフォアボールのいずれかが必ず与えられ、それ以降の投球はおこなわれません。
入力例1
5
ball
strike
ball
strike
strike
出力例1
ball!
strike!
ball!
strike!
out!
入力例2
6
ball
strike
ball
ball
strike
ball
出力例2
ball!
strike!
ball!
ball!
strike!
fourball!
攻略ポイント
ポイント
- 標準入力から複数行のデータを取得する
- 配列、繰り返し、条件分岐を使う
参考記事
問題を解く流れ
入出力例をコピペしてヒアドキュメントで変数に代入し、データを確認します。INPUT1 が与えられた時に OUTPUT1 を出力すれば正解と言うことになります。
きちんと受け取れていれば確認用コードは削除します。
INPUT1 = <<~"EOS" 5 ball strike ball strike strike EOS OUTPUT1 = <<~"EOS" ball! strike! ball! strike! out! EOS INPUT1 = <<~"EOS" 6 ball strike ball ball strike ball EOS OUTPUT1 = <<~"EOS" ball! strike! ball! ball! strike! fourball! EOS # 確認用コード p INPUT1 p OUTPUT1 p INPUT2 p OUTPUT2 # > "5\nball\nstrike\nball\nstrike\nstrike\n" # > "ball!\nstrike!\nball!\nstrike!\nout!\n" # > "6\nball\nstrike\nball\nball\nstrike\nball\n" # > "ball!\nstrike!\nball!\nball!\nstrike!\nfourball!\n"
続いて問題を解くメソッド( solve
メソッドとします)を変数の下に定義します。
まず、入力データを受け取る処理を書きます。
入力された文字列を改行で区切り、1行目は使わないので _
に代入、2行目以降の文字列を配列 calls
に代入します。
def solve(input_lines) # 改行区切りで分割 # 1行目を _ に代入(使わないので捨てる) # 2行目以降を配列 calls に代入 _, *calls = input_lines.split("\n") # 動作確認用コード calls end p solve(INPUT1) # > ["ball", "strike", "ball", "strike", "strike"]
配列 calls
にデータが正しく受け取れていれば、solve
メソッドにストライク・ボールの判定処理を追加します。
strike_count, ball_count
の変数を 0
で初期化した後、calls
の先頭から次々と判定して結果を配列で返す処理を追記していきます。
def solve(input_lines) # 改行区切りで分割 # 1行目を _ に代入(使わないので捨てる) # 2行目以降を配列 calls に代入 _, *calls = input_lines.split("\n") # カウント初期化 strike_count, ball_count = 0, 0 # calls の先頭から次々判定して結果を配列で返す calls.each.map do |call| if call == "strike" # strike の場合 strike_count += 1 if strike_count == 3 # strike_count が 3 なら out! "out!" else # strike_count が 3 未満なら strike! "strike!" end else # ball の場合 ball_count += 1 if ball_count == 4 # ball_count が 4 なら fourball! "fourball!" else # ball_count が 4 未満なら ball! "ball!" end end end end p solve(INPUT1)
この時点で p solve(INPUT1)
を puts solve(STDIN.read)
に変更すれば正解になりますが、p solve(INPUT1) == OUTPUT1 -> true
を確認するために、ひと手間加えてみます。
当たり判定結果を一旦 result
へ代入し、result
の各要素を改行で連結して、末尾に改行を追加したものを返すように変更します。
def solve(input_lines) # 改行区切りで分割 # 1行目を _ に代入(使わないので捨てる) # 2行目以降を配列 calls に代入 _, *calls = input_lines.split("\n") # カウント初期化 strike_count, ball_count = 0, 0 # calls の先頭から次々判定して結果の配列を result に代入する result = calls.each.map do |call| if call == "strike" # strike の場合 strike_count += 1 if strike_count == 3 # strike_count が 3 なら out! "out!" else # strike_count が 3 未満なら strike! "strike!" end else # ball の場合 ball_count += 1 if ball_count == 4 # ball_count が 4 なら fourball! "fourball!" else # ball_count が 4 未満なら ball! "ball!" end end end result.join("\n") << "\n" end p solve(INPUT1) p solve(INPUT1) == OUTPUT1 puts solve(INPUT1) # > "ball!\nstrike!\nball!\nstrike!\nout!\n" # > true # > ball! # > strike! # > ball! # > strike! # > out!
確認用コードを削除して、標準入力からのデータ受け取りに変更と動作確認をしたら提出します。
(STDIN.read
の 入力終了は Ctrl+D 又は Ctrl+Z)
解答コード
def solve(input_lines) # 改行区切りで分割 # 1行目を _ に代入(使わないので捨てる) # 2行目以降を配列 calls に代入 _, *calls = input_lines.split("\n") # カウント初期化 strike_count, ball_count = 0, 0 # calls の先頭から次々判定して結果の配列を result に代入する result = calls.each.map do |call| if call == "strike" # strike の場合 strike_count += 1 if strike_count == 3 # strike_count が 3 なら out! "out!" else # strike_count が 3 未満なら strike! "strike!" end else # ball の場合 ball_count += 1 if ball_count == 4 # ball_count が 4 なら fourball! "fourball!" else # ball_count が 4 未満なら ball! "ball!" end end end result.join("\n") << "\n" end puts solve(STDIN.read)
他の解答例
"out!" でなければ "strike!" 、"fourball!" でなければ "ball!" のような単純な分岐は三項演算子を使うとスッキリ書けます。
また、集計をハッシュにすればベースボールカウントとして strike
と ball
をまとめて管理できます。
def solve(input_lines) # 改行区切りで分割 # 1行目を _ に代入(使わないので捨てる) # 2行目以降を配列 calls に代入 _, *calls = input_lines.split("\n") # カウント初期化 count = { "strike" => 0, "ball" => 0 } # calls の先頭から次々判定して結果の配列を result に代入する result = calls.each.map do |call| if call == "strike" # strike の場合 count["strike"] += 1 # count["strike"] が 3 なら out! 3 以外(0~2) なら strike! count["strike"] == 3 ? "out!" : "strike!" else # ball の場合 count["ball"] += 1 # count["ball"] が 4 なら fourball! 4 以外(0~3) なら ball! count["ball"] == 4 ? "fourball!" : "ball!" end end result.join("\n") << "\n" end puts solve(INPUT1)
今回のまとめ
今回は分岐パターンが4種類でしたが、分岐条件をうまく整理出来ればあとは基本操作の組み合わせなので難しくないと思います。
分岐条件や処理が複雑だと感じたら、手書きで整理してみるのも良いと思います!