My solution drops the numbers 1-90 into the tickets at random. If
there are more than 3 numbers of the same column for a ticket, or it
already has 15 numbers then it kicks out one of its other numbers at
random. This number is put back to be distributed to some other ticket.
After all the numbers are distributed, each Housie generates itself
using the values it has been given. It will add one number at a time
to the row with the least amount of numbers. If two rows have the
same amount of numbers then their positions will be chosen randomly.
I also included a method to generate a single ticket.
The code got a bit messier than I'd like it, but it avoids brute
force-generation and seem to generate tickets quickly.
/Christoffer
#####
class Housie
def initialize
@colset = Array.new(9) { [] }
@numbers = 0
end
# Push a number to this ticket.
#
# If this number can't fit with the numbers already in this
housie, we return
# one of the old numbers in the housie that we removed to make
this number fit.
#
def push(number)
raise "Tried to push to generated housie ticket" if @housie
column = number == 90 ? 8 : number / 10
@colset[column] << number
if @colset[column].size == 4
@colset[column].shift
elsif @numbers == 15
value = @colset[rand(9)].shift while value.nil?
value
else
@numbers += 1
nil
end
end
# Generates a ticket from added data
# Since we have 15 numbers, not more than 3 of each column type we
know we
# can create a ticket, but we want a randomized look to it.
def generate
raise "Not enough data to generate ticket" unless complete?
@housie = Array.new(3) { Array.new(9) }
(0..8).sort_by { rand }.each do |column|
@colset[column].size.times do
rows = @housie.sort_by { rand }.sort { |row1, row2|
row1.compact.size <=> row2.compact.size }
rows.shift until rows.first[column].nil?
rows.first[column] = true
end
end
9.times do |column|
@colset[column].sort!
@housie.each { |row| row[column] = @colset[column].shift if row
[column] }
end
self
end
# Ugly code to display a ticket.
def to_s
return "Not valid" unless @housie
@housie.inject("") do |sum, row|
sum + "+----" * 9 + "+\n" +
row.inject("|") { | sum, entry | sum + " #{"%2s" % entry} |" }
+ "\n"
end +
"+----" * 9 + "+"
end
def complete?
@numbers == 15
end
def Housie.new_book
housies = Array.new(6) { Housie.new }
numbers = (1..90).to_a
while numbers.size > 0 do
pushed_out = housies[rand(6)].push(numbers.shift)
numbers << pushed_out if pushed_out
end
housies.collect { |housie| housie.generate }
end
def Housie.new_ticket
housie = Housie.new
random_numbers = (1..90).sort_by { rand }
until housie.complete?
returned = housie.push random_numbers.shift
random_numbers << returned if returned
end
housie.generate
end
end
puts "A book of tickets:"
Housie.new_book.each_with_index { |housie, index| puts "Ticket #
{index + 1}"; puts housie.to_s }
puts "A single ticket:"
puts Housie.new_ticket.to_s