コンテンツへスキップ

【Ruby基礎】AtCoder Beginner Contest 031 A – ゲーム

  • by

■はじめに

Rubyの基礎的な問題をたくさん解くことで基本的な考え方やメソッドの使い方を定着させたい。
基本的にはAtCoderというプログラミングコンテスト(競技プログラミング)の過去問を使う。(AtCoderは難易度が分かれており、難易度の低いA問題かB問題を解いていく)

(5/23時点の方針)
メソッドの切り分け方や値の受け渡しを練習するために、コード長の短さについては気にせずに書くことにする。

■問題

●出典

AtCoder Beginner Contest 031のA問題
https://atcoder.jp/contests/abc031/tasks/abc031_a

●問題文

高橋君は新しくゲームを買った。

高橋君のゲームキャラクターには攻撃力と防御力が定まっており、レベルが上がると攻撃力と防御力のうち好きなひとつを 1 だけ上げることができる。

高橋君はゲームキャラクターの攻撃力と防御力の積が最大となるように攻撃力か防御力のうちひとつを選んで上げる。もしもどちらを選んでもレベルアップ後の攻撃力と防御力の積が等しくなる場合、攻撃力を選んで上昇させる。

高橋君がゲームキャラクターをレベルアップさせた場合の、レベルアップ後の攻撃力と防御力の積を求めよ。

●入力

入力は以下の形式で標準入力から与えられる。

A D
  • 1 行目には、2 個の整数 A(1≦A≦200), D(1≦D≦200) が空白区切りで与えられる。これは、ゲームキャラクターの攻撃力が A で、防御力が D であることを表す。

●出力

高橋君がゲームキャラクターをレベルアップさせた後の、ゲームキャラクターの攻撃力と防御力の積を 1 行に出力せよ。
出力の末尾にも改行を入れること。

■回答

●愚直に書く

積が最大になるようにということは、小さい方の数字を1つ大きくする、ということ。
また、「どちらを選んでもレベルアップ後の攻撃力と防御力の積が等しくなる場合」というのは攻撃力と防御力が同じ数字の場合ということ。

a, d = gets.split.map(&:to_i)

if a <= d
  puts (a + 1) * d
else
  puts a * (d + 1)
end

通った!

●メソッド化して書く

メソッドを作る練習のために、あえてそういう書き方をする。
今回はメインメソッドとif分岐を処理するメソッドと標準入力の取得をするメソッドの3つに分けてみた。

def main
  num = read_num
  a = num[0]
  d = num[1]
  display_result(a, d)
end

def display_result(a, d)
  if a <= d
    puts (a + 1) * d
  else
    puts a * (d + 1)
  end
end

def read_num
  gets.split.map(&:to_i)
end

main

通った!引数を渡す練習もできた。

●他の方の回答例

下記の回答が面白かった。

a,d=gets.split.map &:to_i
p [(a+1)*d,a*(d+1)].max

どっちも計算しちゃって配列にしてmaxで大きい方を出すという作戦。なるほど〜!
AtCoderの面白いところは、Rubyの勉強もさることながら、こういう計算の工夫を見られるところ。

●出てきたメソッド等

公式リファレンスを見る訓練。

■振り返りなど

メソッド化の練習は、勉強にはなるけど、他の回答例ではそういうアプローチが無いので(あったとしても探せないので)自分の書き方に何か問題や改善の余地があるのかどうかを自分で判断できないのがちょっとツラいな…。色々やっていって自分で気づいていくしかないか。

■6/10追記

june29 さんに色々とアドバイスをいただきました、感謝…!🙏

  • 標準入力からは配列を取ってきているので、単数形のread_numよりもread_numbersのような名前の方が良い
  • read_numbersを2つの変数に分けるのはa, d = read_numbersでできる
  • display_resultに、ジャッジと表示の2つの役割があるので、メソッドでの役割はジャッジだけにしてmainメソッドの方でputsしてあげれば良さそう
def main
  a, d = read_numbers
  puts judge(a, d)
end

def judge(a, d)
  if a <= d
    (a + 1) * d
  else
    a * (d + 1)
  end
end

def read_numbers
  gets.split.map(&:to_i)
end

main

ついでにjudgeメソッドの中は三項演算子にしようかな。

def main
  a, d = read_numbers
  puts judge(a, d)
end

def judge(a, d)
  a <= d ? (a + 1) * d : a * (d + 1)
end

def read_numbers
  gets.split.map(&:to_i)
end

main

修正前に比べてスッキリして見通しも良くなったように思います。ありがとうございます!!