rustedの日記 このページをアンテナに追加 RSSフィード

ruby勉強するよ。

2006-12-20

[][]Amazing Mazes (#31) 08:16 Amazing Mazes (#31) - rustedの日記 を含むブックマーク はてなブックマーク - Amazing Mazes (#31) - rustedの日記

今度は迷路の自動生成(とその解き方)(http://www.rubyquiz.com/quiz31.html)。

All nodes of the maze must be reachable from any point.

まず全てのノードに到達できるようにすること。これは生成後に移動可能な場所をチェックして、いっていないところがあったら穴をあけることで解決した。

Furthermore, let us enforce that only 1 viable solution for the maze exists for any given start/stop (you cannot reach the same destination from 2 different routes).

もう1つ注意点。ただ1つの解になるようにすること、ということなので環ができていたら埋めてみました。

生成した迷路を解くほうは再帰でサクっと書いてみた。

ソースstart/stopランダムで決定されるよ。

#!/usr/bin/ruby
# Amazing Mazes (#31)

class Maze
  LEFT = 1
  TOP = 2
  RIGHT = 4
  BOTTOM = 8
  def initialize(w, h)
    if h <= 0 || w <= 0
      raise 'error : invalid size'
    end
    @width = w
    @height = h
    @start = rand(@width * @height)
    @stop = rand(@width * @height)
    while @start == @stop
      @stop = rand(@width * @height)
    end
    @data = Array.new(@width * @height) { |i| 0 }
    @solve = Array.new(@width * @height) { |i| 0 }
  end
  
  def dig(i, d)
    if d == 0
      if i % @width != 0
        @data[i] |= LEFT
        @data[i - 1] |= RIGHT
      end
    elsif d == 1
      if i >= @width
        @data[i] |= TOP
        @data[i - @width] |= BOTTOM
      end
    elsif d == 2
      if i % @width != @width - 1
        @data[i] |= RIGHT
        @data[i + 1] |= LEFT
      end
    elsif d == 3
      if i < @width * @height - @width
        @data[i] |= BOTTOM
        @data[i + @width] |= TOP
      end
    end
  end
  
  def fill(i, d)
    if d == 0
      if i % @width != 0
        @data[i] &= ~LEFT
        @data[i - 1] &= ~RIGHT
      end
    elsif d == 1
      if i >= @width
        @data[i] &= ~TOP
        @data[i - @width] &= ~BOTTOM
      end
    elsif d == 2
      if i % @width != @width - 1
        @data[i] &= ~RIGHT
        @data[i + 1] &= ~LEFT
      end
    elsif d == 3
      if i < @width * @height - @width
        @data[i] &= ~BOTTOM
        @data[i + @width] &= ~TOP
      end
    end
  end
  
  def generate
    n = @width * @height
    list = Array.new(n) { |i| i }
    
    cnt = 0
    while cnt < 10000
      i = rand(n)
      j = rand(n)
      tmp = list[i]
      list[i] = list[j]
      list[j] = tmp
      cnt += 1
    end
    
    list.each_index { |i| 
      while !dig(i, rand(4))
      end
    }
  end
  
  def search(list, x, y, d)
    if(list[x + y * @width] == 1)
      fill(x + y * @width, d)
      return
    end
    list[x + y * @width] = 1
    
    if (d != 0) && (x > 0) && (@data[x + y * @width] & LEFT != 0)
      search(list, x - 1, y, 2)
    end
    if  (d != 1) && (y > 0) && (@data[x + y * @width] & TOP != 0)
      search(list, x, y - 1, 3)
    end
    if  (d != 2) && (x < @width - 1) && (@data[x + y * @width] & RIGHT != 0)
      search(list, x + 1, y, 0)
    end
    if  (d != 3) && (y < @height - 1) && (@data[x + y * @width] & BOTTOM != 0)
      search(list, x, y + 1, 1)
    end
  end
  
  def check
    list = Array.new(@width * @height) { |i| 0 }
    search(list, rand(@width), rand(@height), -1)
    
    dig_list = Array.new
    list.each_index { |i|
      if list[i] == 0
        dig_list.push(i)
      end
    }
    
    if dig_list.size > 0
      while !dig(dig_list[rand(dig_list.size)], rand(4))
      end
      return false
    end
    return true
  end
  
  def solve(x, y, d)
    if @stop == x + y * @width
      return true
    end
    
    if (d != 0) && (x > 0) && (@data[x + y * @width] & LEFT != 0) && (solve(x - 1, y, 2))
      @solve[x + y * @width] = 1
      return true
    end
    if  (d != 1) && (y > 0) && (@data[x + y * @width] & TOP != 0) && (solve(x, y - 1, 3))
      @solve[x + y * @width] = 1
      return true
    end
    if  (d != 2) && (x < @width - 1) && (@data[x + y * @width] & RIGHT != 0) && (solve(x + 1, y, 0))
      @solve[x + y * @width] = 1
      return true
    end
    if  (d != 3) && (y < @height - 1) && (@data[x + y * @width] & BOTTOM != 0) && (solve(x, y + 1, 1))
      @solve[x + y * @width] = 1
      return true
    end
    return false
  end
  
  def solving_maze
    solve(@start % @width, @start / @width, -1)
  end
  
  def print_maze
    y = 0
    while y < @height
      if y == 0
        x = 0
        while x < @width
          if x == 0
            print "+"
          end
          if @data[x + y * @width] & TOP != 0
            print " "
          else
            print "+"
          end
          print "+"
          x += 1
        end
        print "\n"
      end
      x = 0
      while x < @width
        if x == 0
          if @data[x + y * @width] & LEFT != 0
            print " "
          else
            print "+"
          end
        end
        if @start == x + y * @width
          print "S"
        elsif @stop == x + y * @width
          print "P"
        elsif @solve[x + y * @width] == 1
          print "."
        else
          print " "
        end
        if @data[x + y * @width] & RIGHT != 0
          print " "
        else
          print "+"
        end
        x += 1
      end
      print "\n"
      x = 0
      while x < @width
        if x == 0
          print "+"
        end
        if @data[x + y * @width] & BOTTOM != 0
          print " "
        else
          print "+"
        end
        print "+"
        x += 1
      end
      print "\n"
      y += 1
    end
  end
end

m = Maze.new((ARGV[0] || 8).to_i, (ARGV[1] || 8).to_i)
m.generate
while !m.check
end
m.print_maze
m.solving_maze
m.print_maze

実行!

$ ruby maze.rb
+++++++++++++++++
+ +P  + +       +
+ +++ + +++ +++ +
+     +  S    + +
+ +++++++++++++ +
+ +   + +   +   +
+ + +++ +++ + + +
+     +       + +
+ +++ +++ + +++++
+ +     + +     +
+ +++ + +++ + +++
+   + +     + + +
+++ + +++++++++ +
+   +   +     + +
+++ + +++ +++ + +
+   +     +     +
+++++++++++++++++
+++++++++++++++++
+ +P .+ +  . . .+
+ +++ + +++ +++ +
+. . .+  S .  +.+
+ +++++++++++++ +
+.+   + +   +. .+
+ + +++ +++ + + +
+. . .+    . .+ +
+ +++ +++ + +++++
+ +  . .+ +.    +
+ +++ + +++ + +++
+   + +. . .+ + +
+++ + +++++++++ +
+   +   +     + +
+++ + +++ +++ + +
+   +     +     +
+++++++++++++++++

できた!

AnibalAnibal2012/05/24 02:12I thuoght finding this would be so arduous but it's a breeze!

qpdovwpikcoqpdovwpikco2012/05/24 11:311Y3tEJ <a href="http://htkvrnwrcwuo.com/">htkvrnwrcwuo</a>

vunjvpvunjvp2012/05/25 15:067ERx6q , [url=http://ixlmsvaujdex.com/]ixlmsvaujdex[/url], [link=http://savglgtqnctc.com/]savglgtqnctc[/link], http://yqfetetzdsph.com/

pnuzxeegeppnuzxeegep2012/05/25 17:11AbH2wG , [url=http://kjlildmopokz.com/]kjlildmopokz[/url], [link=http://wbgnuqliopsu.com/]wbgnuqliopsu[/link], http://cibtscycnbwg.com/

zropusqzropusq2012/05/25 19:295x90up , [url=http://lwlqwbzgxyor.com/]lwlqwbzgxyor[/url], [link=http://sihwpywrrdrz.com/]sihwpywrrdrz[/link], http://zqefsciumrcj.com/

juucrfjxqjuucrfjxq2012/05/26 15:19TtvI89 <a href="http://kldbcjvczieo.com/">kldbcjvczieo</a>

2006-09-23

[][]Grid Folding (#63) 22:29 Grid Folding (#63) - rustedの日記 を含むブックマーク はてなブックマーク - Grid Folding (#63) - rustedの日記

16×16サイズの紙を指定されたコマンドで半分ずつ折っていく問題(http://www.rubyquiz.com/quiz63.html)。最終的に1×1サイズになったところで、グリッドについていた番号を上から並べて出力する。

この例がわかりやすい。

12

34

fold("RB") => [3, 4, 2, 1]

fold("TL") => [3, 1, 2, 4]

fold("LR") => raise exception for invalid input

コマンドは"T"(上辺を下辺に合わせるように折る。折った後、上辺側が上になる)、"B"(下辺を上辺に~)、"L"(左辺を右辺に~)、"R"(右辺を左辺に~)の4つ。折ったときに上になるほうは並びが反転するけど、下になるほうはそのままなことに注意する必要があるかな。

Extra credit: Make your fold function take an additional input parameter(s), the dimensions of the paper; dimensions should be power-of-2 in order to fold down to one cell.

サイズを指定できるようにするといいらしい。あと最終的に1つのマスにたたみこむために、1辺の長さを2の累乗にするべきとのこと。

今回のソース

#!/usr/bin/ruby
# Grid Folding (#63)

class Paper
  def initialize(n)
    i = 2
    while i != n
      if i > n
        raise "invalid dimension"
      end
      i *= 2
    end
    @width = n
    @height = n
    @grid = Array.new(n * n)
    i = 0
    while i < @grid.size
      @grid[i] = [ i + 1 ]
      i += 1
    end
  end

  def pos(x, y)
    return x + y * @width
  end

  def to_bottom
    if @height == 1
      raise "invalid input"
    end
    size = @grid[0].size * 2
    x = 0
    y = @height / 2
    while y < @height
      while x < @width
        i = pos(x, @height - 1 - y) 
        @grid[i] = @grid[i].reverse
        @grid[pos(x, y)] = @grid[i] + @grid[pos(x, y)]
        x += 1
      end
      x = 0
      y += 1
    end    
    @grid.delete_if { |g| g.size != size }
    @height = @height / 2
  end
  
  def to_top
    if @height == 1
      raise "invalid input"
    end
    size = @grid[0].size * 2
    x = 0
    y = 0
    while y < @height / 2
      while x < @width
        i = pos(x, @height - 1 - y)
        @grid[i] = @grid[i].reverse
        @grid[pos(x, y)] = @grid[i] + @grid[pos(x, y)]
        x += 1
      end
      x = 0
      y += 1
    end
    @grid.delete_if { |g| g.size != size }
    @height = @height / 2
  end

  def to_right
    if @width == 1
      raise "invalid input"
    end
    size = @grid[0].size * 2
    x = @width / 2
    y = 0
    while y < @height
      while x < @width
        i = pos(@width - 1 - x, y)
        @grid[i] = @grid[i].reverse
        @grid[pos(x, y)] = @grid[i] + @grid[pos(x, y)]
        x += 1
      end
      x = @width / 2
      y += 1
    end
    @grid.delete_if { |g| g.size != size }
    @width = @width / 2
  end
  
  def to_left
    if @width == 1
      raise "invalid input"
    end
    size = @grid[0].size * 2
    x = 0
    y = 0
    while y < @height
      while x < @width / 2
        i = pos(@width - 1 - x, y)
        @grid[i] = @grid[i].reverse
        @grid[pos(x, y)] = @grid[i] + @grid[pos(x, y)]
        x += 1
      end
      x = 0
      y += 1
    end
    @grid.delete_if { |g| g.size != size }
    @width = @width / 2
  end

  def fold(str)
    while str.size > 0
      case str
      when /\AT/o
        to_bottom
      when /\AB/o
        to_top
      when /\AL/o
        to_right
      when /\AR/o
        to_left
      when /\A./o
        raise "invalid input"
      end
      str = $'
    end
    return @grid.first
  end
end

paper = Paper.new((ARGV[1] || 16).to_i)
p paper.fold(ARGV[0] || "")

実行結果。

$ ./grid_folding.rb "RBLT" 4
[2, 3, 15, 14, 13, 16, 4, 1, 5, 8, 12, 9, 10, 11, 7, 6]
$ ./grid_folding.rb "RBLTRT" 8
[19, 22, 46, 43, 42, 47, 23, 18, 10, 15, 55, 50, 51, 54, 14, 11, 12, 13, 53, 52, 
49, 56, 16, 9, 17, 24, 48, 41, 44, 45, 21, 20, 28, 29, 37, 36, 33, 40, 32, 25, 
1, 8, 64, 57, 60, 61, 5, 4, 3, 6, 62, 59, 58, 63, 7, 2, 26, 31, 39, 34, 35, 38, 
30, 27]
$ ./grid_folding.rb "LLLLBBBB" 16
[32, 17, 24, 25, 28, 21, 20, 29, 30, 19, 22, 27, 26, 23, 18, 31, 239, 226, 231, 
234, 235, 230, 227, 238, 237, 228, 229, 236, 233, 232, 225, 240, 160, 145, 152, 
153, 156, 149, 148, 157, 158, 147, 150, 155, 154, 151, 146, 159, 111, 98, 103, 
106, 107, 102, 99, 110, 109, 100, 101, 108, 105, 104, 97, 112, 96, 81, 88, 89, 92, 
85, 84, 93, 94, 83, 86, 91, 90, 87, 82, 95, 175, 162, 167, 170, 171, 166, 163, 
174, 173, 164, 165, 172, 169, 168, 161, 176, 224, 209, 216, 217, 220, 213, 212, 
221, 222, 211, 214, 219, 218, 215, 210, 223, 47, 34, 39, 42, 43, 38, 35, 46, 45, 
36, 37, 44, 41, 40, 33, 48, 64, 49, 56, 57, 60, 53, 52, 61, 62, 51, 54, 59, 58, 
55, 50, 63, 207, 194, 199, 202, 203, 198, 195, 206, 205, 196, 197, 204, 201, 
200, 193, 208, 192, 177, 184, 185, 188, 181, 180, 189, 190, 179, 182, 187, 186, 
183, 178, 191, 79, 66, 71, 74, 75, 70, 67, 78, 77, 68, 69, 76, 73, 72, 65, 80, 
128, 113, 120, 121, 124, 117, 116, 125, 126, 115, 118, 123, 122, 119, 114, 127, 
143, 130, 135, 138, 139, 134, 131, 142, 141, 132, 133, 140, 137, 136, 129, 144, 
256, 241, 248, 249, 252, 245, 244, 253, 254, 243, 246, 251, 250, 247, 242, 255, 
15, 2, 7, 10, 11, 6, 3, 14, 13, 4, 5, 12, 9, 8, 1, 16]

できた??サイズ4×4ではできているはず。チェック関数も実装する必要があるかな。

2006-09-20

[][]Dice Roller (#61) 23:29 Dice Roller (#61) - rustedの日記 を含むブックマーク はてなブックマーク - Dice Roller (#61) - rustedの日記

Time to release your inner nerd.

ダイス表現から値を生成する問題(http://www.rubyquiz.com/quiz61.html)。2d6(6面ダイスを2回振る)とかだね。

今回、メイン部分はすでに用意されているのでした。

d = Dice.new(ARGV[0])
(ARGV[1] || 1).to_i.times { print "#{d.roll} " }

今回の補足。

A few more things... Feel free to either craft this by hand or an available lexing/parsing library. Handling whitespace between integers and operators is nice. Some game systems use d100 quite often, and may abbreviate it as "d%" (but note that '%' is only allowed immediately after a 'd').

相変わらず英語に自信がないけど、

  • パーサの使用は自由
  • 演算子回りに空白を入れることができるとナイス
  • いくつかのゲームではd100をd%と略すことがあるよ

こんな感じかな。最後のやつは初めて聞いたよ。これまでパーサを使ったことがないので、パーサを使ってみることにする。racc(http://www.loveruby.net/ja/projects/racc/)をチョイス。

ソース

class DiceParser
prechigh
  left 'd'
  left '*' '/'
  left '+' '-'
preclow

rule
expr : '(' expr ')' { result = val[1] }
     |  expr '+' expr { result += val[2] }
     |  expr '-' expr { result -= val[2] }
     |  expr '*' expr { result *= val[2] }
     |  expr '/' expr { result /= val[2] }
     | expr 'd' expr { result = parse_d(val[0], val[2]) }
     | expr 'd' '%' { result = parse_d(val[0], 100) }
     | 'd' expr { result = parse_d(1, val[1]) }
     | 'd' '%' { result = parse_d(1, 100) }
     | NUMBER
     ;
end
---- header ----

def parse_d(x, y)
  r = 0
  i = 0
  while i < x
    r += (rand * y + 1).to_i
    i += 1
  end
  return r
end

---- inner ----

def parse(str)
  @q = []
  while str.size > 0
    case str
    when /\A\s+/o # 空白を読み飛ばす
    when /\A\d+/o
      @q.push [:NUMBER, $&.to_i]
    when /\A./o # 空白、数字以外の1文字
      s = $&
      @q.push [s, s]
    end
    str = $'
  end
  @q.push [false, $end]
  do_parse  
end

def next_token
  @q.shift
end

---- footer ----

class Dice
  def initialize(s)
    @str = s
    @parser = DiceParser.new
  end

  def roll
    begin
      val = @parser.parse(@str)
    rescue
      p $!
    end
  end
end  

d = Dice.new(ARGV[0])
(ARGV[1] || 1).to_i.times { print "#{d.roll} " }

raccに投入して実行!

$ racc -o dice.rb dice.y
$ ruby dice.rb "(((d(3 + d2)) * 10d3) - (10d2) / 2)" 10
33 11 27 36 7 28 10 12 27 31 
$ ruby dice.rb "(((2 * (3 + 2)) * 3) - (12 + 3) / 2)"
23 

できた!パーサ便利だなあ。

2006-09-19

[][]The Solitaire Cipher (#1) 22:05 The Solitaire Cipher (#1) - rustedの日記 を含むブックマーク はてなブックマーク - The Solitaire Cipher (#1) - rustedの日記

トランプを使ったSolitaire Cipherという方法を使って暗号化、復号化を行えという問題(http://www.rubyquiz.com/quiz1.html)。

この問題は手順が細かく書いてあるので特に迷うこともなくできた。

ソースはこんな感じ。

#!/usr/bin/ruby
# Ruby Quiz - The Solitaire Cipher (#1)

$letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"

# 前処理
def preprocess(s)
  s.upcase!
  s.gsub!(/[^A-Z]/, '')
  pad = (s.length % 5 != 0) ? 5 - s.length % 5 : 0
  i = 0
  while i < pad
    s.insert(-1, 'X')
    i += 1
  end
  return s.length
end

# 初期化
def initDeck(deck)
  i = 0
  while i < 54
    deck[i] = i
    i += 1
  end
end

# Joker A を下に 1 枚分動かす
def moveJokerA(deck)
  jokera = deck.index(52)
  deck.delete_at(jokera)
  jokera += 1
  if jokera >= 54
    jokera -= 53
  end
  deck.insert(jokera, 52)
end

# Joker B を下に 2 枚分動かす
def moveJokerB(deck)
  jokerb = deck.index(53)
  deck.delete_at(jokerb)
  jokerb += 2
  if jokerb >= 54
    jokerb -= 53
  end
  deck.insert(jokerb, 53)
end

# 上側の Joker より上にあるカード と 下側の Joker より下にあるカード を入れ換える
def triple_cut(deck)
  tmp = Array.new()
  while deck.first < 52
    tmp.push(deck.shift)
  end
  while deck.last < 52
    deck.insert(0, deck.pop)
  end
  while tmp.first != nil
    deck.push(tmp.shift)
  end
end

# 底のカードの値だけ一番上からカードを抜いて底のカードの上に入れる
def card_cut(deck)
  i = 0
  while i < deck.last + 1
    deck.insert(52, deck.shift)
    i += 1
  end
end

# 一番上のカードの値だけ数えたところにあるカードの値を出力する
# ジョーカーの場合はまたこんど
def getLetter(deck, key)
  i = deck.first + 1
  if i > 53
    i = 53
  end
  l = deck[i]
  if l < 52
    key.insert(-1, $letter[l].chr)
    return 1
  end
  return 0
end

# Key Stream Letter なるものを得る
def getKeyStreamLetter(n)
  key = String.new()
  deck = Array.new(54)

  initDeck(deck)

  i = 0
  while(i < n)
    moveJokerA(deck)
    moveJokerB(deck)
    triple_cut(deck)
    card_cut(deck)
    i += getLetter(deck, key)
  end
  return key
end

# 数値に変換する (A -> 1 B->2 ... ) 
def toNumbers(msg, num)
  for i in msg.split('')
    num.push($letter.index(i) + 1)
  end
end

# 文字列に変換する
def toString(num, msg)
  for i in num
    msg.insert(-1, $letter[i - 1].chr)
  end
end

# 配列と配列の足し算
def addArray(msg, key, sum)
  while msg.first != nil
    i = msg.shift + key.shift
    if i > 26
      i -= 26
    end
    sum.push(i)
  end
end

# 配列と配列の引き算
def subArray(msg, key, sum)
  while msg.first != nil
    i = msg.shift - key.shift
    if i < 1
      i += 26
    end
    sum.push(i)
  end
end

# 区切って出力
def print_out(s)
  while s.length > 0
    print s.slice!(0..4), ' '
  end
  puts
end

def encrypt(s)
  n = preprocess(s)
  key = getKeyStreamLetter(n)
  msg_ary = Array.new()
  toNumbers(s, msg_ary)
  key_ary = Array.new()
  toNumbers(key, key_ary)
  sum_ary = Array.new()
  addArray(msg_ary, key_ary, sum_ary)
  output = String.new()
  toString(sum_ary, output)
  print_out(output)
end

def decrypt(s)
  s.gsub!(/[^A-Z]/, '')
  key = getKeyStreamLetter(s.length)
  msg_ary = Array.new()
  toNumbers(s, msg_ary)
  key_ary = Array.new()
  toNumbers(key, key_ary)
  sum_ary = Array.new()
  subArray(msg_ary, key_ary, sum_ary)
  output = String.new()
  toString(sum_ary, output)
  print_out(output)
end

if $*[0] == "-d" and $*.length == 2
  decrypt($*[1].dup)
elsif $*[0] == "-e" and $*.length == 2
  encrypt($*[1].dup)
else
  puts "usage: " + $0 + " -d|-e string"
end

実行結果。

> ./quiz01.rb -d 'ABVAW LWZSY OORYK DUPVH'
WELCO METOR UBYQU IZXXX

できた!メッセージの1つは"Welcome to Ruby Quiz"。最初に解いておいたほうがよかったかなあ。

2006-09-16

[][]Crosswords (#10) 23:10 Crosswords (#10) - rustedの日記 を含むブックマーク はてなブックマーク - Crosswords (#10) - rustedの日記

クロスワードパズルレイアウトをする問題(http://www.rubyquiz.com/quiz10.html)。レイアウトファイルで与えられる。大きくするだけ?と思ったけど、

As a style point, many crosswords drop filled squares on the outer edges. We wouldn't want our Ruby generated crosswords to be unfashionable so we better do that too.

どこの流行か知らないけれど、流行に取り残されないために外側に埋められたマス(■)がある場合はその部分の描画をするなとの通達をいただいたよ。

The final step of laying out a crossword puzzle is to number the squares for word placement. A square is numbered if it is the first square in a word going left to right or top to bottom. Numbers start at 1 and count up left to right, row by row going down.

さらに答えを埋めるための番号(タテ「1」、ヨコ「5」とか)をつけてくれとの注文も。クロスワードパズルには欠かせない要素でした。

で、こんなんでました。

#!/usr/bin/ruby
# Crosswords (#10)

def print_board(board)
 for line in board
   for c in line
     print c
   end
   puts
 end
end

# 最初のマスかどうか
def first_square?(board, x, y)
 if board[y][x] != '_'
   return false
 end
 if (x == 0 || board[y][x - 1] != '_') &&
     (x < board[y].size - 1 && board[y][x + 1] == '_')
   return true
 end
 if (y == 0 || board[y - 1][x] != '_') &&
     (y < board.size - 1 && board[y + 1][x] == '_')
   return true
 end
 return false
end

# 番号を配置
def put_number(board, big)
 cnt = 1
 x = 0
 y = 0
 while y < board.size
   while x < board[y].size
     if first_square?(board, x, y)
       i = 0
       while i < cnt.to_s.size
         big[y * 3 + 1][x * 5+ 1 + i] = cnt.to_s[i].chr
         i += 1
       end
       cnt += 1
     end
     x += 1
   end
   x = 0
   y += 1
 end
end

# でっかくする
def toBig(board)
 big = Array.new
 # まずはそのまま
 for line in board
   l1 = Array.new()
   l2 = Array.new()
   l3 = Array.new()
   l4 = Array.new()
   for c in line
     if c == ' '
       l1.push(' ', ' ', ' ', ' ', ' ', ' ')
       l2.push(' ', ' ', ' ', ' ', ' ', ' ')
       l3.push(' ', ' ', ' ', ' ', ' ', ' ')
       l4.push(' ', ' ', ' ', ' ', ' ', ' ')
     elsif c == '_'
       l1.push('#', '#', '#', '#', '#', '#')
       l2.push('#', ' ', ' ', ' ', ' ', '#')
       l3.push('#', ' ', ' ', ' ', ' ', '#')
       l4.push('#', '#', '#', '#', '#', '#')
     else
       l1.push('#', '#', '#', '#', '#', '#')
       l2.push('#', '#', '#', '#', '#', '#')
       l3.push('#', '#', '#', '#', '#', '#')
       l4.push('#', '#', '#', '#', '#', '#')
     end
   end
   big.push(l1, l2, l3, l4)
 end

 # 重なりをあわせる (縦)
 i = 4
 while i < big.size
   j = 0
   while j < big[i].size
     if big[i-1][j] == '#' || big[i][j] == '#'
       big[i-1][j] = '#'
     else
       big[i-1][j] = ' '
     end
     j += 1
   end
   big.delete_at(i)
   i += 3 # delete で -1
 end

 # 重なりをあわせる (横)
 i = 6
 while i < big[0].size
   j = 0
   while j < big.size
     if big[j][i-1] == '#' || big[j][i] == '#'
       big[j][i-1] = '#'
     else
       big[j][i-1] = ' '
     end
     big[j].delete_at(i)
     j += 1
   end
   i += 5
 end
 return big
end

# 再帰的に外側を外していく
def search(board, x, y)
 if board[y][x] == 'X'
   board[y][x] = ' '
   if x < board[y].size - 1
     search(board, x + 1, y)
   end
   if x > 0
     search(board, x - 1, y)
   end
   if y < board.size - 1
     search(board, x, y + 1)
   end
   if y > 0
     search(board, x, y - 1)
   end
 end
end

# 外側の埋まっているマスをはずす
def remove(board)
 x = 0
 y = 0
 while x < board[y].size
   if board[y][x] == 'X'
     board[y][x] = ' '
     search(board, x, y + 1)
   end
   x += 1
 end
 x = board[y].size - 1
 while y < board.size
   if board[y][x] == 'X'
     board[y][x] = ' '
     search(board, x - 1, y)
   end
   y += 1
 end
 y = board.size - 1
 while x >= 0
   if board[y][x] == 'X'
     board[y][x] = ' '
     search(board, x, y - 1)
   end
   x -= 1
 end
 x = 0
 while y >= 0
   if board[y][x] == 'X'
     board[y][x] = ' '
     search(board, x + 1, y)
   end
   y -= 1
 end
end

# 問題をファイルから読む
board = Array.new()
while line = ARGF.gets
 ary = line.split(' ')
 board.push(ary)
end
remove(board)
big = toBig(board)
put_number(board, big)
print_board(big)

実行結果

crosswords.txt =>
X _ _ _ _ X X
_ _ X _ _ _ _
_ _ _ _ X _ _
_ X _ _ X X X
_ _ _ X _ _ _
X _ _ _ _ _ X
$ ./quiz10.rb crosswords.txt
     #####################
     #1   #    #2   #3   #
     #    #    #    #    #
####################################
#4   #    ######5   #    #6   #7   #
#    #    ######    #    #    #    #
####################################
#8   #    #9   #    #    #10  #    #
#    #    #    #    #    #    #    #
#####################    ###########
#    ######11  #    #
#    ######    #    #
####################################
#12  #13  #    ######14  #15  #    #
#    #    #    ######    #    #    #
####################################
     #16  #    #    #    #    #
     #    #    #    #    #    #
     ##########################

できた!

親しみやすい課題で面白かったよ。でもコードはwhile文とif文だらけでかなり汚い感じ。もっとシンプルに仕上げたいなあ。