Here is my solution to Ruby Quiz 10: Crosswords. Jump down to the solution or the test data.
Back to my Ruby Quiz solutions page.
#! /usr/bin/env ruby class Cell WIDTH = 6 HEIGHT = 4 attr_accessor :neighbors # Hash, key=:left, :right, :top, :bottom def Cell.from_char(c) return c == '_' ? WhiteCell.new : BlackCell.new end def initialize @neighbors = {} end def print_border_at(row, col, paper) x = col * (Cell::WIDTH - 1) y = row * (Cell::HEIGHT - 1) # corners paper[y][x] = paper[y][x+Cell::WIDTH-1] = paper[y+Cell::HEIGHT-1][x] = paper[y+Cell::HEIGHT-1][x+Cell::WIDTH-1] = '+' # top and bottom (Cell::WIDTH-2).times { | j | paper[y][x+1+j] = '-' } (Cell::WIDTH-2).times { | j | paper[y+Cell::HEIGHT-1][x+1+j] = '-' } # sides (Cell::HEIGHT - 2).times { | i | paper[y+1+i][x] = '|' paper[y+1+i][x+Cell::WIDTH-1] = '|' } end end class WhiteCell < Cell attr_accessor :clue_number def black? return false end def needs_clue_number? return ((@neighbors[:left].nil? || @neighbors[:left].black?) && !@neighbors[:right].nil? && !@neighbors[:right].black?) || ((@neighbors[:top].nil? || @neighbors[:top].black?) && !@neighbors[:bottom].nil? && !@neighbors[:bottom].black?) end def print_at(row, col, paper) print_border_at(row, col, paper) if @clue_number x = col * (Cell::WIDTH - 1) y = row * (Cell::HEIGHT - 1) s = @clue_number.to_s s.split(//).each_with_index { | char, i | paper[y+1][x+1+i] = char } end end end class BlackCell < Cell def initialize super @fill_char = '#' end def black? return true end def hidden? return @hidden end def needs_clue_number? return false end def hide return if hidden? @hidden = true @fill_char = ' ' @neighbors.each_value { | cell | cell.hide if cell && cell.black? } end def print_at(row, col, paper) return if hidden? print_border_at(row, col, paper) # fill center x = col * (Cell::WIDTH - 1) y = row * (Cell::HEIGHT - 1) (Cell::HEIGHT-2).times { | i | (Cell::WIDTH-2).times { | j | paper[y+1+i][x+1+j] = '#' } } end def print_scanline(i) print @fill_char * (Cell::WIDTH - 1) end def print_bottom print_scanline(0) end def print_right_wall print @fill_char end end class Puzzle def initialize(io) read_picture(io) introduce_neighbors() hide_outer_black() assign_clue_numbers() end def read_picture(io) @cells = [] io.each_with_index { | line, i | line = line.chomp.gsub(/[^x_]/i, '') @cells[i] = [] line.split(//).each { | c | @cells[i] << Cell.from_char(c) } } end def introduce_neighbors @cells.each_with_index { | row, i | row.each_with_index { | cell, j | cell.neighbors[:left] = @cells[i][j-1] unless j == 0 cell.neighbors[:right] = @cells[i][j+1] cell.neighbors[:top] = @cells[i-1][j] unless i == 0 cell.neighbors[:bottom] = @cells[i+1][j] if @cells[i+1] } } end def hide_outer_black @cells.first.each { | cell | cell.hide if cell.black? } @cells.last.each { | cell | cell.hide if cell.black? } @cells.each { | row | row.first.hide if row.first.black? row.last.hide if row.last.black? } end def assign_clue_numbers clue_number = 1 @cells.each_with_index { | row, i | row.each_with_index { | cell, j | if cell.needs_clue_number? cell.clue_number = clue_number clue_number += 1 end } } end def print paper = blank_paper @cells.each_with_index { | row, row_num | row.each_with_index { | cell, col_num | cell.print_at(row_num, col_num, paper) } } paper.each { | scanline | puts scanline.join } end def blank_paper width = @cells[0].size * (Cell::WIDTH - 1) + 10 height = @cells.size * (Cell::HEIGHT - 1) + 10 paper = [] height.times { paper << (' ' * width).split(//) } return paper end end Puzzle.new(ARGF).print
X _ _ _ _ X X _ _ X _ _ _ _ _ _ _ _ X _ _ _ X _ _ X X X _ _ _ X _ _ _ X _ _ _ _ _ X