Skip to content

Instantly share code, notes, and snippets.

@valchonedelchev
Last active August 29, 2015 14:01
Show Gist options
  • Save valchonedelchev/35d66c0bbb5ab508d17c to your computer and use it in GitHub Desktop.
Save valchonedelchev/35d66c0bbb5ab508d17c to your computer and use it in GitHub Desktop.
Dodgson doublets puzzle
#!/usr/bin/env ruby
#
# ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-darwin12.0]
# PriorityQueue (0.1.2)
#
# Author: Valcho Nedelchev
#
require 'priority_queue'
class Node
attr_accessor :state, :parent, :path_cost
def initialize(state, parent=nil, path_cost=0)
self.state = state
self.parent = parent
self.path_cost = path_cost
end
def empty?
return self.state.empty?
end
def expand(dictionary)
list = Array.new
for s in generate_words(self.state)
if dictionary.include? s
list.push(Node.new(s, self, self.path_cost+1))
end
end
return list
end
def chain
node = self
trial = []
while node
trial.push(node.state)
node = node.parent
end
return trial.reverse!
end
end
def load_words(length, filename='/usr/share/dict/words')
filename = '/usr/share/dict/words' if filename.nil?
lines = []
for line in File.read(filename).lines
line = line.chomp
if line.downcase == line and line.size == length
lines.push(line)
end
end
return lines
end
def generate_words(word)
words = []
for i in 0..word.length-1
_word = word.dup
for char in ("a".."z").to_a
_word[i] = char
if _word != word
words.push(_word.dup)
end
end
end
return words
end
def heuristic(item, goal)
result = 0
0.upto(goal.size-1) do |i|
if item.state[i] == goal[i]
result += 1
end
end
return result
end
def search(start_word, goal_word, filename)
if start_word == goal_word then
puts "* The words should be different."
exit 1
end
if start_word.size != goal_word.size
puts '* Two words should be with the same length.'
exit 2
end
puts 'Working, plase wait...'
dictionary = load_words(goal_word.size, filename)
prio_func = lambda { |n| n.path_cost + heuristic(n, goal_word) }
pqueue = PriorityQueue.new()
node = Node.new(start_word)
pqueue.push(node, 0)
explored = Array.new
while not pqueue.empty?
node = pqueue.delete_min.shift
if explored.include? node.state
next
end
if node.state == goal_word
return node.chain
end
explored.push(node.state)
for child in node.expand(dictionary)
if not explored.include? child.state
pqueue.push(child, prio_func.call(child))
end
end
end
return ['* Could not find the path!']
end
if ARGV.count < 2
puts "Usage: #{$0} <start_word> <end_word> <words_file:optional>"
exit
else
puts search(ARGV.shift, ARGV.shift, ARGV.shift).join(' -> ')
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment