-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfallout_terminal_hack
More file actions
303 lines (261 loc) · 6.03 KB
/
Copy pathfallout_terminal_hack
File metadata and controls
303 lines (261 loc) · 6.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#!/usr/bin/env ruby
require 'set'
MAX_TRIES = 4
class Word
attr_reader :text, :likeness, :tried
attr_writer :tried
def initialize(text, likeness)
@text = text
@likeness = likeness
@tried = true
end
def password?
return text.length == likeness
end
def length
return @text.length
end
def likeness_to(other)
(0...@text.length).count { |i| @text[i] == other.text[i] }
end
def likeness_matches(others)
others.inject(true) { |a,w| a && likeness_to(w) == w.likeness }
end
def <=>(other)
text <=> other
end
def to_s
@tried ? "#{@text} (#{@likeness})" : @text
end
end
class WordList
attr_reader :words
def initialize(words)
@words = words
end
def word_length
@words[0].length
end
def length
@words.length
end
def password
@words.detect { |w| w.password? }
end
def playable?
min_total = (0.5 * 0.5 + 0.5 * 0.5 * word_length) * length
total_likeness = @words.inject(0.0) { |s,w| s + w.likeness.to_f }
return total_likeness >= min_total
end
def tries
@words.count { |w| w.tried }
end
def reset
@words.each { |w| w.tried = false }
end
def each
@words.each { |w| yield w }
end
def hints
already_tried = @words.select { |w| w.tried }
return Array.new if already_tried.empty?
return @words.select { |w| !w.tried && w.likeness_matches(already_tried) }
end
end
class WordMap
def initialize
@words = Hash.new { |hash,key| hash[key] = Array.new }
load_file("words.txt")
end
def random_words(length, num_words)
all_words = @words[length]
max_zeros = num_words / 4
raise "insufficient words" if all_words.length < num_words
r = Random.new
1.upto(1000000).each do |try_number|
answer = Set.new
pwd = nil
num_zeros = 0
while answer.length < num_words
word = all_words[r.rand(all_words.length)]
if pwd.nil?
pwd = word
answer << Word.new(word, length)
else
word_likeness = likeness(pwd, word)
if word_likeness == 0
next if num_zeros >= max_zeros
num_zeros += 1
end
next if word_likeness == length
answer << Word.new(word, word_likeness)
end
end
words = WordList.new(answer.to_a.shuffle)
return words if words.playable?
end
raise "unable to find enough words with non-zero likeness"
end
def load_file(filename)
File.open(filename, "r") do |f|
f.each do |line|
line = line.chomp.upcase
next unless line.length >= 4
next unless line.length <= 15
@words[line.length] << line
end
end
end
private :load_file
def likeness(pwd, other)
(0...pwd.length).count { |i| pwd[i] == other[i] }
end
private :likeness
end
class GameMenu
attr_reader :words
def initialize(words)
@words = words
end
def print_choices
choice = 1
@words.each do |word|
prefix = ""
if word.tried
prefix = " "
else
prefix = sprintf("%2d)", choice)
choice += 1
end
print "#{prefix} #{word}\n"
end
print " h) hint\n" if @words.tries > 0
print " r) reset\n"
print " q) quit\n"
end
def print_hints
print "\nThese are all the possible passwords:\n"
@words.hints.each { |w| puts w }
print "\n"
end
def read_choice
while true
remaining = MAX_TRIES - @words.tries
if remaining == 1
print "Last try: "
else
print "#{remaining} tries remaining: "
end
choice = STDIN.readline.chomp
if choice.length > 0
mapped = map_char(choice)
return mapped unless mapped.nil?
end
end
end
def map_char(c)
return c if c == "r" || c == "q" || (c == "h" && @words.tries > 0)
c = c.to_i
choice = 1
@words.each do |word|
unless word.tried
return word if c == choice
choice += 1
end
end
return nil
end
private :map_char
end
class GameLoop
attr_reader :words
def initialize(words)
@words = words
end
def run
menu = GameMenu.new(@words)
@words.reset
while @words.tries < MAX_TRIES
puts
menu.print_choices
choice = menu.read_choice
if choice == "r"
@words.reset
elsif choice == "q"
print "#{@words.password} was the password\n"
return "quit"
elsif choice == "h"
menu.print_hints
else
if choice.password?
print "Success! #{choice} is the password!\n"
if @words.tries == 0
print "Sorry - lucky guesses don't count!\n"
return "lucky"
else
return "win"
end
else
choice.tried = true
end
end
end
print "Failure... #{@words.password} was the password.\n"
return "loss"
end
end
class Scenario
attr_reader :length, :num_words
def initialize(length, num_words)
@length = length
@num_words = num_words
end
end
class Game
attr_reader :scenarios, :completed
def initialize
@scenarios = Array.new
@word_map = WordMap.new
end
def add(length, num_words)
@scenarios << Scenario.new(length, num_words)
end
def play
completed = 0
while completed < @scenarios.length
print "\nCreating next word set..."
scenario = @scenarios[completed]
words = @word_map.random_words(scenario.length, scenario.num_words)
puts
loop = GameLoop.new(words)
result = loop.run
if result == "win"
completed += 1
elsif result == "quit"
return
end
end
print "\n\nWell done! You have completed all the scenarios!\n"
end
end
def dump_combinations
wm = WordMap.new
(6..10).each do |length|
(10..20).each do |num_words|
puts "NEW: length=#{length} num_words=#{num_words}"
puts wm.random_words(length, num_words).words
end
end
exit
end
#dump_combinations
game = Game.new
game.add(4, 12)
game.add(5, 13)
game.add(6, 14)
game.add(7, 15)
game.add(8, 15)
game.add(9, 15)
game.add(10, 15)
game.add(10, 19)
game.play