Here is my solution. It is my first Ruby program. I was able to get two
of the extra credit with virtually no work. I had snaking already
because I generated the snaked answers before eliminating the ones that
weren't a straight line. I had an alternative output format because I
was using it to debug my code as I went along before I wrote the code
to generate the answer grid. Using the sample input set, here is my
output:
+++R+R++++
++++U+++++
ROCKSB++++
++K+++Y+++
+S+++++++M
+++++++DAN
+++++++T++
+++ND+Z+++
++++A+++++
YBUR++++++
RUBY
(3,0)(4,1)(5,2)(6,3)
(5,0)(4,1)(5,2)(6,3)
(3,9)(2,9)(1,9)(0,9)
ROCKS
(0,2)(1,2)(2,2)(3,2)(4,2)
(0,2)(1,2)(2,2)(2,3)(1,4)
DAN
(7,5)(8,5)(9,5)
(4,7)(4,8)(3,7)
MATZ
(9,4)(8,5)(7,6)(6,7)
The coordinates in the output set are 0-based x,y originating from the
upper-left corner.
-Ben
require "enumerator"
class Point
attr_reader(:x, :y)
def initialize(x, y)
@x = x
@y = y
end
def to_s
"(#{@x},#{@y})"
end
def adj?(p)
((@x - p.x).abs <= 1) & ((@y - p.y).abs <= 1) & !(self == p)
end
def -(p)
Point.new(@x - p.x, @y - p.y)
end
def ==(p)
(@x == p.x) & (@y == p.y)
end
end
class Array
def diff
return to_enum(:each_cons, 2).map{|a,b| a-b}
end
def same?
return false if length < 1
return true if length == 1
return to_enum(:each_cons, 2).all? {|a,b| a==b}
end
end
def findletter(puzzle, c)
locations = []
puzzle.each_with_index do |line, y|
line.split(//).each_with_index do |letter, x|
locations << Point.new(x, y) if letter == c
end
end
return locations
end
def getletters(puzzle, term)
term.split(//).map{|c| findletter(puzzle, c)}
end
def mixarrays(arr)
return [] if (arr.empty?)
return arr.first.zip if (arr.length == 1)
temp = []
head = arr.first
tail = arr.slice(1, arr.length-1)
head.each do |x|
mixarrays(tail).each do |y|
temp << [x] + y
end
end
return temp
end
def connectedword(word)
return false if word.length < 1
return true if word.length == 1
return word.to_enum(:each_cons, 2).all? {|a,b| a.adj?(b)}
end
def showpoints(term, points)
puts term
points.each {|x| print x, "\n" }
end
def answergrid(puzzle, points)
answer = puzzle.map {|line| line.gsub(/./, '+')}
points.flatten.each do |p|
answer[p.y][p.x] = puzzle[p.y][p.x] if p.kind_of?(Point)
end
return answer
end
puzzle = []
while (line = gets.chomp) != ''
puzzle << line
end
terms = gets.chomp.upcase.split(/\s*\,\s*/)
terms_words = terms.map{|term|
[term, mixarrays(getletters(puzzle, term))]}
terms_connectedwords = terms_words.map{|term, words|
[term, words.select {|word| connectedword(word)}]}
terms_samediffconnectedwords = terms_connectedwords.map{|term, words|
[term, words.select {|word| word.diff.same?}]}
answerkey = terms_connectedwords
puts
puts answergrid(puzzle, answerkey)
puts
answerkey.each {|term, words| showpoints(term, words) }
Ruby Quiz wrote:
> The three rules of Ruby Quiz:
>
> 1. Please do not post any solutions or spoiler discussion for this quiz until
> 48 hours have passed from the time on this message.
>
> 2. Support Ruby Quiz by submitting ideas as often as you can:
>
> http://www.rubyquiz.com/
>
> 3. Enjoy!
>
> Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
> on Ruby Talk follow the discussion. Please reply to the original quiz message,
> if you can.
>
> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>
> by Daniel Finnie
>
> Today's quiz would've been most useful in elementary school, where over half of
> the homework assignments were word search puzzles. The concept of these puzzles
> is simple enough that an elementary school student could understand it: given a
> box of letters, find a line containing the letters of a specified word in order.
>
> For example, find the words ruby, dan, rocks, and matz in the following text:
>
> U E W R T R B H C D
> C X G Z U W R Y E R
> R O C K S B A U C U
> S F K F M T Y S G E
> Y S O O U N M Z I M
> T C G P R T I D A N
> H Z G H Q G W T U V
> H Q M N D X Z B S T
> N T C L A T N B C E
> Y B U R P Z U X M S
>
> The correct answer in the correct output format:
>
> + + + R + + + + + +
> + + + + U + + + + +
> R O C K S B + + + +
> + + + + + + Y + + +
> + + + + + + + + + M
> + + + + + + + D A N
> + + + + + + + T + +
> + + + + + + Z + + +
> + + + + + + + + + +
> + + + + + + + + + +
>
> Notice that the words can go backwards and diagonally, and can intersect one
> another. Searching is case insensitive.
>
> The word search solver should accept input entered by the user after running the
> program, i.e., not exclusively through STDIN or a file, by entering the puzzle
> line by line, pressing return after each line. A blank line indicates the end of
> the puzzle and the start of the comma separated words to find. The following
> example shows how a user would enter the above puzzle, with descriptive text
> from the program removed.
>
> $ ./wordsearch.rb
> UEWRTRBHCD
> CXGZUWRYER
> ROCKSBAUCU
> SFKFMTYSGE
> YSOOUNMZIM
> TCGPRTIDAN
> HZGHQGWTUV
> HQMNDXZBST
> NTCLATNBCE
> YBURPZUXMS
>
> Ruby, rocks, DAN, matZ
>
> Now, by itself, this quiz is fairly simple, so I offer an additional challenge.
> Write a beautiful, extensible, and easily-modifiable program without looking at
> the extra credit before starting. When you're done, try implementing extra
> credit options using less than 5 or 6 (reasonable) lines of code.
>
> * An output format superior to the one given. The output format given
> should remain the default unless both formats don't differ on a
> textual basis. That should sound cryptic until pondered, I can't
> give too much away!
> * Allow for "snaking" of answers, in other words, the letters
> composing a word don't have to be in a straight line.
> * An option to give a hint, i.e., "The word ruby traverses the bottom
> left and bottom right quadrants."
> * Decide what to do with accented letters.
> * Allow for wildcard letters.