Well this is my first attempt at solving one of these quizzes. Please
be gentle, especially with the comments I think.
This solution uese Ranges extensivley and relies on a method
choose_uniq_members on any given range. This will randomly select the
specified number of entries from the range.
The output grid is constructed by first selecting which columns will
be filled, ie 5 columns per row, and 3 rows. This is then used as a
specification for each column which tells the choose_uniq_members
method on the column range how many, 0 to 3, members of the range to
select. It's then collected up into the @rows variable and displayed.
Please be gentle ;)
# Ruby quiz 114
#
class Range
# Choose the specified number of random elements of the range
def choose_uniq_members( num = 1 )
# Make sure the range is large enough to select correct number of
uniq members
raise "RangeBoundaryError" if self.size < num
return self.to_a if self.size == num
# Select the specified number of random entries from the range
tmp = self.dup.to_a
selected = []
num.times { selected << tmp.delete_at( rand( tmp.size ) ) }
selected.sort
end
def size
@size ||= self.to_a.size
end
end
class HousieTicket
COLUMNS = [ 1..9, 10..19, 20..29,30..39,40..49,50..59,60..69,70..79,80..90]
def initialize
# an array of arrays for rows and columns for the final data
@rows = Array.new(3){ Array.new(9) }
# Maps out in a 3 x 5 array, which of the final @rows indicies
should contain numbers
row_map = (1..3).inject([]){ |arr, i| arr <<
(0...COLUMNS.size).choose_uniq_members(5) }
# Maps the indicies of row_map into column counts so that each
column may be populated.
# The number found for each column will be used to choose from the
relevant range.
# ie column 0 = 2, therefore two numbers from the range 1..9 should
be selected
the_map = row_map.flatten.inject( Hash.new(0) ){ |col_map, i|
col_map[i] += 1; col_map }
# Populate the final @rows array with the real numbers based on the
prototype matrix developed
# in row_map by choosing the number of uniq values from the columns
range as specified in the_map
(0...9).each do | col_index |
numbers = COLUMNS[col_index].choose_uniq_members(
the_map[col_index] ).reverse
(0...3).each do | row_index |
@rows[ row_index ][ col_index ] = numbers.pop if row_map[
row_index ].include?( col_index )
end
end
end
# From here down is display methods
# Various display methods to print out the ticket to the terminal
def display
array = stringify_rows
print_line_seperator
array.each do |row|
puts "|" << row.join( "|" ) << "|"
print_line_seperator
end
end
def stringify_rows
rows = @rows.dup
rows.map do |row|
row.map{ |e| if(e) then sprintf(" %02d ", e) else " " end }
end
end
def print_line_seperator
puts "|" << "----|" * 9
end
end
# Runs the program from the terminal
number_of_tickets = ARGV[0]
unless number_of_tickets.to_i > 0
puts "How many tickets would you like to generate?\n"
until number_of_tickets.to_i > 0
number_of_tickets = gets.chomp
end
end
1.upto(number_of_tickets.to_i ) do |n|
ticket = HousieTicket.new
puts "\n\n"
puts "Ticket #{n}"
ticket.display
end