--/fjOwTFLoeqy+p+1VGt
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
Late response, with a question about the solvabilty of these
kind of problems in general.
I used a similar approach and get similar answers to Glenns, only
much slower (although mine finishes in finite time :) When running
Glens program, I get many partial solutions in < 1 Sec (including most
that my program finds) - then his progam consumes 99% of my CPU
time indefinately. On my PC, mine runs in ~10 S for crypto1
(partial answer) ~16 S for crypto2, full correct answer,
and ~80 S for crypto3, producing garbage. (can I buy a
vowel? :)
Actually I added the ablity to provide a partial map
to help prime the solver - still cant make sense of crypto3.
The main question I have is how does one handle non-dictionary
words that match a dictionary word signature - and then force
other wrong choices. The specific case is "alva" which can
map to "aqua". Once I have "aqua" as plain text, it prevents
the solver from using the letter 'u' in genius - making that
word match denims.
In fact I can envsion quotes with non-dictionary words that
could map, wrongly, to a set of all dictionary words - i.e.
the solution with 1 or more "unsolved" words would be more
correct than the solution with no unsolved words...
There's also the situation of "bent, cent, vent, went" - where
the choice of any of them doesn't affect any other words in the
cypher text - providing no help selecting between them.
In both these cases, I don't think backtracking can help - other
than to provide a number of possible solutions that a person
could read and pick from?
Addionally, while I agree that brute force w/26! possible maps
is impractial, after a certain point, would it make sense to
use the brute force approach for the "remaining" letters -
somwhere in the 8-10 letters remaining?
Thanks to all involved in providing this - been reading for a
while, but this is my first try at solving one ... took *way*
more time than I intended, but was lots of fun. Looking forward
to more in the future.
Vance
--/fjOwTFLoeqy+p+1VGt
Content-Disposition: attachment; filename=rq13
Content-Type: application/x-ruby; name=rq13
Content-Transfer-Encoding: 7bit
#! /bin/env ruby
def sig(w)
# rtn '
rtn
ltrs .split(//)
ndx
while l trs.shift
if fnd trs.index(l)
# rtn + dx.to_s + ',' + (fnd+ndx+1).to_s + ' '
rtn tn*100 + ndx*10 + fnd
end
ndx +
end
return rtn
end
def get_words (fname, ary )
file ile.open(fname)
while word ile.gets
ary.push(word.chomp)
end
file.close
end
def choices (dict, word, dpairs, pairs, re il)
re .'* word.length unless re
c val("dict.grep(\/^#{re}\$\/)")
c .delete_if{ |w| dpairs[w] ! airs[word] }
return c
end
def decode (cyphertext, cypher, plain)
code }
('a'..'z').each { |l| code[l] .'}
kl ypher.split(//)
wl lain.split(//)
kl.each_index { |i|
code[kl[i]] l[i] if code[kl[i]] '.'
}
clear ]
cyphertext.each {|w|
next unless w
word "
w.each_byte { |c|
word << code[c.chr] if c
}
clear << word
}
return clear
end
def count_options (puzzle, dictionary, pairs, dpairs, sizes, clear)
min_choices 0000
savi 1
savl 1
savt ]
puzzle.each_index { |i|
t hoices(dictionary, puzzle[i], dpairs, pairs, clear[i])
sizes[i] .length
next if (clear[i] && clear[i] !~ /\./)
if sizes[i] > 0 && sizes[i] < min_choices
min_choices izes[i]
savt
savi
end
}
if savi -1
return nil, nil
else
return puzzle[savi], savt
end
end
### MAIN
dictionary ]
puzzle ]
to_try }
get_words(ARGV[0], dictionary)
#dictionary.push('a')
#dictionary.push('i')
get_words(ARGV[1], puzzle)
#puzzle.each {|a| a.uniq! if a }
# find words w/dup letters
pairs }
puzzle.each {|w| pairs[w] ig(w) }
dpairs }
dictionary.each {|w| dpairs[w] ig(w) }
sizes ]
clear ]
eword ]
tries []
newtries []
etxt "
ctxt "
trial "
c2p }
cta ]
# first time
if ARGV[3]
etxt ¨ÂÖ۲ݮôïß ctxt ¨ÂÖ۳ݮôïß clear ecode(puzzle, etxt, ctxt)
end
eword[0], tries ount_options(puzzle, dictionary, pairs, dpairs, sizes, clear)
puts "#{puzzle.join(' ')}\n"
puts "#{sizes.join(' ')}"
begin
tries_s ]
trial_s ]
clear_s ]
sizes_s ]
zeros ]
num_opts ]
etxt + word[0]
eword_s word[0]
i
tries.each{ |trial|
next if ( sig(etxt) ! ig(ctxt+trial)) # incompatible signatures
clear ecode(puzzle, etxt, ctxt+trial)
eword[i], tries_s[i] ount_options(puzzle, dictionary, pairs, dpairs, sizes, clear)
puts "#{clear.join(' ')}"
# puts "#{sizes.join(' ')}"
clear_s[i] lear
sizes_s[i] izes
trial_s[i] rial
zeros[i] izes.grep(0).length
i +
}
break unless i > 0
# find best "trial"
best 1
num_choices 0000
sizes_s.each_index { |i|
sizes_s[i].each_index { |j|
if (sizes_s[i][j] > 0 &&
sizes_s[i][j] < um_choices &&
clear_s[i][j] /\./)
best
num_choices izes_s[i][j]
end
}
}
if best -1 # no unresolved options w/1 choice ...
best eros.rindex(zeros.min) || -1
end
if best ! 1
ctxt + rial_s[best]
tries ries_s[best]
clear lear_s[best]
eword[0] word[best]
puts "DBG - selected #{eword_s} -> #{trial_s[best]}"
end
end while eword[0]
exit
--/fjOwTFLoeqy+p+1VGt--