わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

ビンゴカード生成プログラム!

プログラムのお披露目です.

#!/usr/bin/env ruby

# bingo.rb

def choose(max, pcs, ofst = 1)
  # ofst〜(ofst+max-1)からpcs個を選んでランダムな順序で配列にして返す
  # ランダムな順序になっているかは要検証
  array = []

  while pcs > 0
    r = rand(max)
    if r < pcs
      array.insert(rand(array.size + 1), ofst)
      pcs -= 1
    end

    max -= 1
    ofst += 1
  end

  array
end

def choose_alternative(max, pcs, ofst = 1)
  # ofst〜(ofst+max-1)からpcs個を選んでランダムな順序で配列にして返す
  # もう一つの方法
  # 要Ruby 1.8.7以降または1.9以降,要srand呼び出し
  (ofst...(ofst + max)).to_a.shuffle[0, pcs]
end

class Bingo
  # ビンゴカード生成に関するクラス

  def initialize(option = {})
    # コンストラクタ
    @max = option.fetch(:max, 75)      # 数値の最大値
    @row = option.fetch(:row, 5)       # 列数
    @column = option.fetch(:column, 5) # 行数(列あたりの乱数生成個数)
    @rep = option.fetch(:rep, 1)       # 生成回数
    @free = option.fetch(:free, true)  # 中央をfreeにするなら真
    @zerofill = option.fetch(:zerofill, false) # 0詰めするなら真

    if @max == 0 or @row == 0 or @column == 0 or @rep == 0
      puts "abort (some parameter is zero)"
      exit
    elsif @row * @column > @max
      puts "abort (#{@row} * #{@column} > #{@max})"
      exit
    end
  end

  def setup_table
    # 2次元の表を作成
    @table = []
    step = @max / @column
    @column.times do |i|
      if @free and i == (@column - 1) / 2
        array = choose(step, @row - 1, step * i + 1)
        array.insert(array.size / 2, nil)
      else
        array = choose(step, @row, step * i + 1)
      end

      @table << array
    end
  end

  def setup_digit
    # 桁数の計算
    x = @max
    @digit = 1
    while x >= 10
      x /= 10
      @digit += 1
    end

    # @digit = @max.to_s.length でもいいことに気づいた
  end

  def rollout
    # ビンゴカードを生成する
    setup_digit
    free_s = "Free"[0, @digit]
    if @zerofill
      format_d = "%0*d "
    else
      format_d = "%*d "
    end
    format_s = "%*s "

    @rep.times do |k|
      setup_table
      @row.times do |i|
        @column.times do |j|
          v = @table[j][i]
          if Numeric === v
            print format_d % [@digit, v]
          else
            print format_s % [@digit, free_s]
          end
        end
        puts
      end
      puts if k < @rep - 1
    end
  end
end

if __FILE__ == $0
  srand

  require 'optparse'
  option = {}
  OptionParser.new {|opt|
    opt.on('-n VAL', '--number=VAL') {|v| option[:max] = v.to_i}
    opt.on('-c VAL', '--column=VAL') {|v| option[:column] = v.to_i}
    opt.on('-r VAL', '--row=VAL') {|v| option[:row] = v.to_i}
    opt.on('-s VAL', '--side=VAL') {|v| option[:row] = option[:column] = v.to_i}
    opt.on('-t VAL', '--times=VAL') {|v| option[:rep] = v.to_i}
    opt.on('-f') {option[:free] = true}
    opt.on('-F') {option[:free] = false}
    opt.on('--[no-]free') {|v| option[:free] = v}
    opt.on('-z') {option[:zerofill] = true}
    opt.on('-Z') {option[:zerofill] = false}
    opt.on('--[no-]zerofill') {|v| option[:zerofill] = v}
    opt.on('--bingo') {
      option[:max] = 75
      option[:column] = 5
      option[:row] = 5
      option[:free] = true
    }
    opt.on('--minibingo') {
      option[:max] = 30
      option[:column] = 3
      option[:row] = 3
      option[:free] = true
    }
    opt.on('--iromonea') {
      option[:max] = 100
      option[:column] = 5
      option[:row] = 1
      option[:free] = false
    }

    opt.parse!(ARGV)
  }

  Bingo.new(option).rollout
end

引数はいろいろありますが,シンボルから想像できると思います.option[:rep] は,生成個数で,デフォルトは1個です.option[:zerofill] は,桁数が少ないときに左に0を詰めたいときにtrueにします.デフォルトはfalseです.
「おすすめセット」を指定できるオプションを3つ,用意しました.通常のビンゴカードを求める --bingo (デフォルト),イロモネアの審査員番号5つを生成する --iromonea,そして,3×3の小さなサイズのビンゴカードを出す --minibingo です.
プログラムでは,optparseライブラリ,Array#insert,Hash#fetch,sprintfフォーマットでの*指定*1を取り入れてみました.
一つ二つ,動作させてみますか.

$ ruby bingo.rb
15 24 35 58 63 
14 22 32 48 72 
 1 18 Fr 55 75 
 4 23 40 60 67 
12 25 41 50 73 
$ ruby bingo.rb
11 21 45 58 71 
12 20 31 57 73 
 9 27 Fr 49 66 
 2 16 41 60 70 
14 17 35 53 65 
$ ruby bingo.rb --iromonea -t 5
  5  29  52  65  88 

 17  40  50  66  90 

 16  25  55  73  86 

 14  38  48  64  91 

  7  25  41  70 100 

ライバル:

*1:Rubyでは使いどころがほとんどありませんね.プログラム中の「format_d % [@digit, v]」は,「"%#{@digit}d " % v」と等価です.