Last active
August 29, 2015 14:01
-
-
Save valchonedelchev/35d66c0bbb5ab508d17c to your computer and use it in GitHub Desktop.
Dodgson doublets puzzle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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