ビンゴカード作成問題を解いてみた

問題はこちら

最初に書いたコード

class Bingo
    SIZE = 5

    def generate_card
        format_a_row = Proc.new do |array|
            array.map {|f| f.to_s.rjust(3)}.join(' |')
        end

        header = format_a_row("BINGO".chars)
        rows = (1..SIZE).map do |i|
            (1..SIZE).map {rand((i-1)*15+1..i*15)}
        end.transpose
        
        rows[(SIZE-1)/2][(SIZE-1)/2] = ""       
        rows.map! {|r| format_a_row(r)}

        puts [header, *rows].join("\n")
    end
end

Bingo.new.generate_card

出力結果は以下。

  B |  I |  N |  G |  O
  9 | 21 | 40 | 46 | 74
  9 | 29 | 45 | 59 | 67
  6 | 22 |    | 56 | 61
 12 | 18 | 34 | 51 | 66
  7 | 28 | 36 | 47 | 62

transposeすぐ発想できたのはよかった。

ん…?数字重複してるじゃん!これじゃだめだ。

修正したコード

解答例もみる。

あと考えたらheaderとbodyの整形まとめた方がいいな。

class Bingo
    SIZE = 5
    FORMAT = Array.new(5, '%3s').join(' |')

    def generate_card
        rows = (1..SIZE)
        .map {|i| [*((i-1)*15-1..i*15)].sample(5)}
        .transpose.tap {|t| t[(SIZE-1)/2][(SIZE-1)/2] = ""}
        
        puts rows.unshift("BINGO".chars).map {|r| FORMAT % r}.join("\n")
    end
end

Bingo.new.generate_card

tapってこうやってつかうんやな…すっきりやな…。

学んだこと

String#%(args)

sprintf的なフォーマットができる。リファレンス

> "--%s--" % "hoge"  # => "--hoge--"

# 引数に配列もわたせる
> "--%s--%s--" % ["hoge", "fuga"]  # => "--hoge--fuga--"

# 幅も指定できる(右寄せになる)
> "--%10s--" % "hoge"  # => "--      hoge--"

ランダムな値

をとるときには、重複を許容する/しないを意識すべし。