-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathword-warp.lisp
executable file
·358 lines (329 loc) · 13.5 KB
/
word-warp.lisp
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
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
;; Meghan Hollenbach & ultasun
;; Common Lisp Word Scramble Game
;; 11-23-11
;;------------------------------------------------------------------------------
;;------------------------------------------------------------------------------
;; PARAMETER DEFINITIONS BELOW
;;------------------------------------------------------------------------------
;;------------------------------------------------------------------------------
;; the wordlist is populated with a dictionary file (later in the code)
;; the more words you have, the faster (init-game) completes!
(defparameter *wordlist* nil)
;; the collection of characters to choose from randomly
(defparameter *valid-chars* nil)
;; how many words match-chars-words() has to find in order for it's
;; proposed game to be "playable"
(defparameter *playable* 4)
;; minimal word length
(defparameter *min-word-length* 4)
;; the player characters - characters that are in the words to solve
;; (given to user at run-time)
(defparameter *player-chars* nil)
;; the words the player must find for the game to be completed
(defparameter *player-words* nil)
;; new list of hyphens representing a parallel array of the words
(defparameter *words-hyphened* nil)
;;------------------------------------------------------------------------------
;; DICTIONARY TO FILESYSTEM FUNCTIONS
;;------------------------------------------------------------------------------
;; load a dictionary file from a text file. the words in the text file should
;; be one entry per line, and sorted in alphabetical order.
;; load-dictionary-txt (n filename)
;; params: n chars for max sized word
(defun load-dictionary-txt (n filename)
(let ((w '()))
(with-open-file (stream filename)
(loop for line = (read-line stream nil 'foo)
until (eq line 'foo)
do
(and
;; test the length of this word
(if (>= (length line) *min-word-length*) line ())
(if (<= (length line) n) line ())
;; apostrophied words require ||'s to be symbols.
;; ||'s aren't compatible as letters in our game.
;; dictionary files tend to have apostrophied entries
(if (find #\' line :test #'equal) ()
;; convert the read string into a symbol
(push
(string-upcase line) w))))
;; merges new wordlist with existing wordlist without creating
;; duplicate entries
;; *wordlist* becomes unsorted, however
(setf *wordlist*
(sort-strings
(set-difference w *wordlist*))))))
;;------------------------------------------------------------------------------
;; FUNCTION DEFINITIONS : UTILITY FUNCTIONS
;;------------------------------------------------------------------------------
;; implode (l)
;; params: l is a list
;; implode a list of strings into a single string
(defun implode (l)
(let
((word nil))
(loop for x in l do (setf word (concatenate 'string word x))) word))
;; explode (e)
;; params: e is a string
;; Explode a string into a list of single character strings
(defun explode (e)
(let ((chars nil))
(loop for i below (length e) do
(push (subseq e i (1+ i)) chars))
(reverse chars)))
;; string-to-symlist (l)
;; params: l is the string accepted by the function
;; loop through each single char string (after l is exploded)
;; collect returns the list of symbols intern has created
(defun string-to-symlist (l)
(loop for i in (explode l) collect (intern (string-capitalize i))))
;; symlist-to-string (l)
;; params: l is the symbol being passed
;; (defun symlist-to-string (l)
;; (let ((w ""))
;; set i equal to each single char string in l, and
;; set w equal to w+i concat
;; (loop for i in l do (setf w (concatenate 'string w (string i)))) w))
;; countchar (c str)
;; params: c is the character you're interested in, str is the word it's
;; evaluating against
;; counts how many times a character shows up in a word
(defun countchar (c str)
(let ((n 0))
(loop for x across str do
(if
(string= x c)
(setf n (1+ n))))
n))
;; sort-string (l)
;; params: l is the list of strings
;; alphabetically sorts the strings with
;; (sort l (function to define how to sort))
(defun sort-strings (l)
(sort l (lambda (x y) (string< x y))))
;;------------------------------------------------------------------------------
;; FUNCTION DEFINITIONS: GAME FUNCTIONS
;;------------------------------------------------------------------------------
;; init-valid-chars ()
;; find a list of non-repeating characters of the words from our wordlist
;; loops through every word extracted from the dictionary finding chars that
;; have not been found yet
(defun init-valid-chars ()
(let ((alphabet nil)) ;; create a local alphabet variable = nil
(dolist (x *wordlist*) ;; for every string in the word list put in x
(dolist (y (explode x)) ;; for every single character string put in y
;; next line would disable repeats from appearing in the alphabet
;;(if (set-difference (list (intern y)) alphabet)
(push y alphabet))) ;;) ;; push new symbol in y into the alphabet var
;; set *valid-chars* equal to the strings in alphabet
(setf *valid-chars* (sort-strings alphabet))))
;; random-char ()
;; get a random character from the characters extracted from the *wordlist*
;; nth returns the single char string
(defun random-char () (nth (random (length *valid-chars* )) *valid-chars*))
;; random-chars (n)
;; params: n is the # of random characters to get
;; get n random charactters by calling the random-char function
(defun random-chars (n)
(let ((rchars nil) (rchar nil))
(loop until
(= (length rchars) n) do
(progn (setf rchar (random-char))
(push rchar rchars)))
rchars))
;;------------------------------------------------------------------------------
;; match-chars-words (chars)
;; params: chars
;; from our list of valid letters, find at least *playable* words to play with.
;; return nil if no such case was found (see init-game)
(defun match-chars-words (chars)
;; set chars-fit to True and charstr to the string of valid-chars
(let ((playable-words nil) (chars-fit T) (charstr chars))
;; loop through *wordlist* giving each word to w
(loop for w in *wordlist* do
(progn
;; loop across the string w and give each value to i
(loop for i across w do
;; if i's position in w is <= i's position in charstr
;; in other words, make sure that every char in the word is
;; in the accepted char list
;; (we play with every word until we prove we can't)
(if
(<=
(countchar i w) (countchar i (implode charstr)))
() ;; do nothing
(setf chars-fit nil)) ; else set clone to nil
) ;; end inner loop
;; if there's a clone
(if chars-fit (push w playable-words))
(setf chars-fit T)
)
) ;; end loop
;; is this words list any good to play with?
;; enough words to play?
(if
(> (length playable-words) *playable*)
(setf *player-words* playable-words) nil)))
;; list-contains-string (str wordlist)
;; params: str is the string we are comparing, wordlist is the list we compare
;; str to
;; small utility function...does the wordlist have the string str in it?
;; this is case sensitive...
;; returns T if str is a member of wordlist
(defun list-contains-string (str wordlist)
(let ((match nil))
(dolist (x wordlist)
(if (string= (string-upcase x) str) (setf match T)))
match))
;; init-game (n)
;; params: n is the amount of characters being used
;; search for a playable game
(defun init-game (n)
;; set *player-chars* equal to the randomly selected chars from the
;; *wordlist* generated by the dictionary
(setf *player-chars* (sort-strings (random-chars n)))
;; loop for every *player-chars* thrown into match-chars-words (chars)
(loop (match-chars-words *player-chars*)
;; check for something in *player-words*, then return to caller function
(if *player-words* (return)
;; empty brackets is the else cond
(setf *player-chars* (sort-strings (random-chars n)))
) ;; end if
) ;; end loop
)
;; hypens (l)
;; params: n is the number of hyphens needed
(defun hyphens (n)
(let ((hyph "---"))
(loop for i below (- n 3) do
(setf hyph (concatenate 'string hyph "-")))
;; need to restate the local variable as the last thing done because
;; we are returning it
hyph))
;; convert-to-hyph ()
;; loops through the *player-words* list checking the length of each word
;; once it finds the length, the loop pushes that many hyphens onto local var w
;; by calling hyphens, after the loop ends, *words-hyphened* is set to w stack
(defun convert-to-hyph ()
(let ((w '()))
(loop for i in *player-words* do
(push
(hyphens
(length
(explode i)))
w)) ;;end loop
(setf *words-hyphened* w)))
;; check-guess (n)
;; params: n is a string of a word
;; check if the guess is a symbol in the player-words list, if it is,
;; call the function checking with
;; params guess start_position position_of_guessed_word
(defun check-guess (n)
(let ((newlist ()))
(if (list-contains-string (string-upcase n) *player-words*)
(progn
(loop for i in *words-hyphened*
for j in *player-words*
do
(progn
(if (string= j n) (push j newlist)
(push i newlist)) ; else
))
;; if n was a correct guess...
;; we must make a reversed copy of the newlist var
;; set the previous *words-hyphened* to a revised list
;; including the correct guess
(setf *words-hyphened* (reverse newlist))))))
;; get-hint ()
;; pick a random word from the words the player hasn't found yet
(defun get-hint ()
;; let var unknown equal everything that's not in *words-hyphened*
;; but in *player-words*
(let ((unknown
(set-difference
*player-words*
;; take out all hyphened words (unknown)
;; you'll only have these 5 hyphen lengths
(set-difference
*words-hyphened*
'(--- ---- ----- ------ -------)
:test 'equal))))
;; return a string in the (random) position in unknown
(nth (random (length unknown)) unknown)))
;; hinter ()
;; give the player a hint by filling in one of the words for them
;; calls check-guess with get-hint's return value
(defun hinter ()(check-guess (get-hint)))
;; run-game ()
;; run-game holds all of the stmts to initialize a new game
(defun run-game ()
;; print user friendly statements
(print "------------------------------------")
(print "...Your words to guess are...")
;; loop through every hyphened word printing them to console
(loop for i below (length *words-hyphened*) do
(print (nth i *words-hyphened*)))
;; if all hyphened words have been found
;; (i.e. match everything in *player-words*)
;; print you've won
;; else initiate a guessing sequence
(if (equal *words-hyphened* *player-words*)
(print "...You have WON the game!")
(progn
(print "...You have to guess with the characters...")
(print *player-chars*) ;; print valid chars in words
(print "...Take a guess! Or ? for a hint!...")
(fresh-line)
;; read in the user input
(let ((in (string-upcase (read-line))))
(cond ((string= in "?") ;; '?' is help case - gives word
(hinter)) ;; calls hinter () for word
((check-guess in) ;; calls check-guess (input)
(print "...Correct guess!"))
(T (print "...Incorrect guess!"))))
(run-game) ;; recursive call to run the game again !
))
nil)
;;------------------------------------------------------------------------------
;; start-me (n) ************MAIN FUNCTION****************
;; in console: (start-me n)
;; params: n is the number of letters the game should use for the words found.
;; ex: n = 6 ... max size word you can have is 6 letters.
;; number of words for the user to unscramble, ranging from 4-6 letters
(defun start-me (n)
(load-dictionary-txt n "english.0")
(init-valid-chars)
(init-game n)
(convert-to-hyph)
(setf *player-words* (reverse *player-words*))
(run-game))
;;------------------------------------------------------------------------------
;;-------------- NOTES ---------------------------------------------------------
(start-me 4)
;;------------------------------------------------------------------------------
;;EXTRA FUNCTIONS
;;------------------------------------------------------------------------------
;; r is the random symbol sequence and str is the string
;;(defun random-chars-fit-word (r str)
;; (let ((nomatch nil) (letters (explode r)) (expl-str (explode str)))
;; (loop for i in letters do
;; (if (intersection (list (intern i)) r) (setf nomatch T)) )(not nomatch)))
;;------------------------------------------------------------------------------
;;get n random words
;; (defun random-words (n)
;; (let ((words nil))
;; (loop for i below n do
;; (push (nth (random (length *wordlist*)) *wordlist*) words)) words))
;;------------------------------------------------------------------------------
;; Save the *wordlist* to the local disk for later use
;; (defun save-db (filename)
;; (with-open-file (out filename
;; :direction :output
;; :if-exists :supersede)
;; (with-standard-io-syntax
;; (print *wordlist* out))))
;;Load a *wordlist* from the local disk
;; (defun load-db (filename)
;; (with-open-file (in filename)
;; (with-standard-io-syntax
;; (setf *wordlist* (read in)))))