Ruby word unscrambler script not working

Ive been trying to get this script to work for way too long and I have
no idea how to solve my problem. Towards the end, when I open the text
file to find the word to compare my permutations to, the program only
finds the first word on the list which is “Aarhus”. I can put scrambled
versions of “Aarhus” and it will print out “Aarhus” but if I try to
write the unscrambled version of the next word down the text file, it
only spits out “Aarhus” again!

def to_array
print "Enter input string: "
input_str = STDIN.gets.chomp

letter_array = [ ]
input_str.each_char do |c|
  letter_array << c
end

return letter_array
end

result = to_array

a = result
perms = [ ]
a.permutation(a.length) do |perm|
perms << perm.join
end

fin = File.open(“dict.txt”, “r”)

while line = fin.gets
word = line.chomp
if perm = word
print perm
exit
end

end
fin.close

You put “if perm = word”, which will assign the value of “word” to
“perm”. That will then return that value to “if”, which will evaluate to
true because it is a string.

What you meant to put is “if perm == word”.

= is assignment operator, and == is used to check for equality. Also,
you probably want to use Array#index or Array#include?, as you want to
check if one of
many possible permutations match the word.

There seems to be something missing in the code you posted. Inside the
while loop, tge variable “perm” is not defined. Lastly, use break
instead of exit so that the file gets closed properly.

The least amount of changes to make the code work:

def to_array
    input_str = STDIN.gets.chomp

    letter_array = [ ]
    input_str.each_char do |c|
      letter_array << c
    end
  return letter_array
end

result = to_array
a = result

perms = [ ]
a.permutation(a.length) do |perm|
  perms << perm.join
end

fin = File.open("dict.txt", "r")

while line = fin.gets
  word = line.chomp
  if perms.index(word)
    puts word
    break
  end

end

fin.close

But ruby provides many functions to make this task easier:

dic = "dict.txt"
perms = gets.chomp.chars.to_a.permutation.map(&:join)
word = File.read(dic).lines.find{|line|perms.include?(line.chomp)}

if word
  puts "matching word found: #{word}"
else
  puts "no matching word found"
end

Using a Hash is faster, but the main problem remains: For a word of size
n, there are n! permutations. Try it with the infamous German word
“Donaudampfschifffahrtskapitaen”, it takes a long time.

A better approach is to use an invariant: the number if occurences of
each letter in a word. All you need to do is search the dictionary for a
word with the same counts.

input, scrambled word

scramble = “Donaudampfschifffahrtskapitaen”

count how often each character occurs

counts = []
scramble.downcase!
scramble.chars.to_a.uniq.each do |c|
counts << [c,scramble.count©]
end

open the dictionary and search for any word with the same character

counts
File.open(“dict.txt”) do |io|
io.each_line do |line|
line = line.chomp.strip.downcase
next if line.empty?
if counts.all?{|char,count|line.count(char)==count}
puts “matching word: #{line}”
break
end
end
end

If you’re going to check multiple word, read and process the dictionary
once and store it in a hash.

input

scrambled word

scramble = “Donaudampfschifffahrtskapitaen”

path to dictionary

dic_file = “dict.txt”

counts the number of characters

def hash(x)
Array.new(26){|i|x.count((i+97).chr)}.pack(“c*”)
end

create dictionary

DIC = {}
File.open(dic_file) do |io|
io.each_line do |line|
line = line.chomp.strip.downcase
next if line.empty?
DIC[hash(line)] = line
end
end

checks dictionary for matching word

def unscramble(x)
DIC[hash(x.downcase)]
end

solve input

puts unscramble(scramble) || “no matching word”

Might be faster to use Symbol or Bignum instead of String for the hash,
but you need to try it. The function hash might be faster if you loop
over each letter only once.

Dansei Yuuki wrote in post #1173787:

dic = "dict.txt"
perms = gets.chomp.chars.to_a.permutation.map(&:join)
word = File.read(dic).lines.find{|line|perms.include?(line.chomp)}

if word
  puts "matching word found: #{word}"
else
  puts "no matching word found"
end

Using a Hash in place of a Array :

dict = “dict.txt”
lperms = (ARGV[0]||gets.chomp).chars.to_a.permutation.map(&:join)
hperms=lperms.inject({}) {|h,a| h[a]=true ; h}
word = File.read(dic).lines.find{|line|
#lperms.include?(line.chomp)
hperms[line.chomp]
}
puts word ? “matching word found: #{word}” : “no matching”

ruby yellowest

Duration 1077.062 ms, with Hash
Duration more than 10 minutes, with Array.include?

In theory, sort should be at most O(n log(n)); and creating a fixed-size
char array and iterating over each character and increasing the
corresponding count should be O(n).

But I don’t think it matters much in practice, and if speed were
super-important, it should probably be written in C etc., so I like the
sort approach and I think it allows for cleaner code.

Wow, you have all helped me greatly. Thank you Dansei Yuuki for making
it clear to me what I have done wrong.

Dansei Yuuki wrote in post #1173817:

Using a Hash is faster, but the main problem remains: For a word of size
n, there are n! permutations. Try it with the infamous German word
“Donaudampfschifffahrtskapitaen”, it takes a long time.

nice.
I have used another solution: comparaison with word.split(//).sort

dic = “dict.txt”
ref=(ARGV[0]||gets.chomp).split(//).sort.join("")
word = File.read(dic).lines.find{|line|
w=line.chomp
w.size==ref.size && w.split(//).sort.join("")==ref
}
puts word ? “matching word found: #{word}” : “no matching”

chrono ruby a.rb disestablishmentarianismanti
Chrono of : > ruby a.rb disestablishmentarianismanti
matching word found: antidisestablishmentarianism
true
Duration 416.024 ms

chrono ruby a.rb oologicallyz
Chrono of : > ruby a.rb oologicallyz
matching word found: zoologically
true
Duration 441.025 ms