about summary refs log tree commit diff stats
path: root/js/games/nluqo.github.io/~bh/downloads/csls-programs/master
blob: f876c6a0535dfcee01222d1eccaa598b27a8827a (plain) (blame)
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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
; [Mastermind game]

cslsload "buttons
cslsload "streams

to master [:numsquares 4] [:dup.ok "false] [:mysecret "true]
; Mastermind game program.
; Program is controlled by mouse clicks or keystrokes.
if :LogoPlatform = "Windows [maximize.window "true]
window
if :LogoPlatform = "wxWidgets [localmake "fontsize labelsize]
localmake "colors "ROYGBV
localmake "colornums [[R 4] [O 14] [Y 6] [G 2] [B 1] [V 13]]
localmake "exact "true
local [numguesses numcolors column guess gotnum winloop permuting]
local [perms oldcount newcount guess.exact guess.inexact guess.word]
catch "quit [forever [
  catch "master [
    make "numguesses 0
    make "numcolors 0
    make "column 0
    make "winloop "false
    initdraw		 ; Clear screen, draw color palette 
    ifelse :mysecret [
	ifelse :dup.ok	 ; Choose secret permutation
	   [make "secret (choose.dup :numsquares :colors)]
	   [make "secret (choose.nodup :numsquares :colors)]
        newguess	 ; Display first guess frame
        action.loop	 ; Read keyboard characters or mouse clicks 
    ] [
	catch "win [	 ; User's secret, program has to guess.
	  ifelse :dup.ok [
	    make "permuting "false	; Lots of cases with dups okay, so
	    make "perms (list copies :numsquares "x)
	    make "newcount 0		; find colors systematically first.
	    catch "perm [
	      for [i 1 6] [	; Learn how many red, then orange, etc.
		make "oldcount :newcount
		doguess subst :i "x head :perms
		make "newcount :guess.exact + :guess.inexact
	    	make "perms flatten stream.map
		   `[insert ,[:newcount-:oldcount] ,:i ?] :perms
		make "perms stream.filter
                          `[okay? ? ,:guess.exact ,:guess.inexact ,:guess.word]
			  :perms
		check.consistency :perms
		if equalp :newcount :numsquares [throw "perm]
	      ]
	      check.consistency []	; Tried all colors, user lied.
	    ]
	    make "permuting "true
	  ] [
	    make "perms perms "123456 :numsquares	; not :dup.ok
	    make "permuting equalp :numsquares 6
	  ]
	  forever [			; common portion
	    doguess head :perms
	    if equalp :numsquares :guess.exact + :guess.inexact ~
	      [make "permuting "true]
	    make "perms stream.filter
                          `[okay? ? ,:guess.exact ,:guess.inexact ,:guess.word]
			  :perms
	    check.consistency :perms
	  ]
	  ; Can't get here; either doguess finds a winner or
	  ; check.consistency complains.
	] ; We get here on throw "win from doguess.
	move [15 12]
	setpc 7 label "WIN!
	ct print (sentence [I win in] :numguesses "turns.)
	make "winloop "true
	action.loop
    ]
]]]
cs ct setpc 7 st
end

;;; ================== LOGIC FOR MY GUESSES (USER SECRET) =================

to doguess :guessword
; Present computer's guess to user and ask about matches.
newguess				; Draw frame for guess.
make "guess.word :guessword		; Remember my colors.
foreach :guessword [apply "putguess item ? :colornums]	; Show colors.
askexact				; Ask user for exact matches.
make "gotnum "false
catch "ready [action.loop]
pu setpos [150 205] setpc 0 filled 0 [repeat 2 [fd 35 rt 90 fd 110 rt 90]]
setpc 7
ifelse :guess.exact < :numsquares [	; Not all colors are exact.
  ifelse :permuting [			;   If we know all the colors,
    make "exact "false			;   compute how many are inexact
    getnum :numsquares-:guess.exact	;   without asking.
  ] [
    askinexact				;   Otherwise, ask for inexact.
    make "gotnum "false
    catch "ready [action.loop]
    pu setpos [150 205] setpc 0 filled 0 [repeat 2 [fd 35 rt 90 fd 110 rt 90]]
    setpc 7
    if :guess.exact + :guess.inexact > :numsquares ~
       [check.consistency []]	; Quick error message if too many matches.
  ]
] [
  throw "win				; All colors are exact, we win.
]
end

to subst :new :old :word
; For dups-okay guessing:  Substitute the next trial color for
; all unknown squares in a partial permutation.
output map [ifelse equalp ? :old [:new] [?]] :word
end

to copies :num :letter
output cascade :num [word ? :letter] "
end

to insert :num :new :word
; For dups-okay guessing:  We've learned that there are :NUM instances
; of color :NEW in the secret combination, so stick that many of them into
; a still-possible partial permutation, in every possible size=:NUM
; subset of the unknown slots.
; The result is a *stream* of possible (partial) permutations.
if :num=0 [output (list :word)]		; No slots needed, just one result.
if emptyp :word [output []]		; Not enough slots, no results!
if equalp first :word "x 		; Else combine results of choosing or ~
   [op flatten				; not choosing to replace into this X.
	 stream insert :num-1 :new word :new butfirst :word
	      `[(list stream.map [word "x ?] insert ,:num ",:new bf ",:word )]]
output stream.map `[word ",[first :word] ?] insert :num :new butfirst :word
end

to check.consistency :str
; If the stream of still-possible permutations is empty, then
; the user has lied to us.
if emptyp :str [ct print [Error -- inconsistent answers!]
		repeat 2 [setbg 4 wait 1 setbg 0 wait 1]
		type [Click or type anything to restart.] wait 0
		waitforclick
		throw "master]
end

to perms :word :num
; Output the stream of permutations of :NUM letters chosen from :WORD.
if :num=0 [output (list "|| )]
if emptyp :word [output []]	; Can't happen (would mean :num>count :word).
output flatten stream.map ~
   `[[letter] stream.map `[word ,:letter ?]
                         perms remonce :letter ,:word ,[:num-1]] ~
   :word
end

to okay? :perm :guess.exact :guess.inexact :guess.word
output and (equalp :guess.exact exact :perm :guess.word) ~
           (equalp :guess.inexact inexact :perm :guess.word)
end

to askexact
; ct type "|How many EXACT matches? |
; pu setpos [185 210] setpc 6 label "EXACT?
localmake "caption.scrunch 1.5
setbutton [152 210] [100 25] [] "true 0 "EXACT? []
ern "caption.scrunch
make "exact "true
end

to askinexact
; ct type "|How many INEXACT matches? |
; pu setpos [185 210] setpc 6 label "INEXACT?
localmake "caption.scrunch 1.5
setbutton [152 210] [100 25] [] "true 0 "INEXACT? []
ern "caption.scrunch
make "exact "false
end

;;; ================== LOGIC FOR USER GUESSES (MY SECRET) =================

to choose.dup :number :colors
if :number = 0 [output "]
output word (pick :colors) (choose.nodup :number-1 :colors)
end 
 
to choose.nodup :number :colors
if :number = 0 [output "]
make "color pick :colors
output word :color (choose.nodup :number-1 remonce :color :colors)
end

;;;;; ================ Used by both kinds of logic ======================
 
to exact :secret :guess
if empty? :secret [output 0]
output ehelp + (exact butfirst :secret butfirst :guess)
end

to ehelp
ifelse equal? (first :secret) (first :guess) [output 1] [output 0]
end 

to inexact :secret :guess
output (anymatch :secret :guess) - (exact :secret :guess)
end

to anymatch :secret :guess
if empty? :secret [output 0]
if member? first :secret :guess ~
   [output 1 + anymatch (butfirst :secret) (remonce first :secret :guess)]
output anymatch butfirst :secret :guess
end 

to remonce :this :those
if empty? :those [output "]
if equal? :this first :those [output butfirst :those]
output word (first :those) (remonce :this butfirst :those)
end

;;;;; =================== USER INTERFACE (DRAWING) =======================

to initdraw
fs init.buttons
localmake "bigwidth ifelse :LogoPlatform = "wxWidgets [5*first :fontsize] [40]
localmake "bigbutton list :bigwidth 25
localmake "tallheight ifelse :LogoPlatform="wxWidgets [2+2*last :fontsize] [30]
localmake "tallbutton list :bigwidth :tallheight
ifelse :mysecret ~
  [colorchart 6 "ROYGBV [4 14 6 2 1 13] 165
   setbutton [-245 -15] :bigbutton [clear] "false 0 "erase "DEL] ~
  [numchart 0 165]
setbutton pos0 [-245 -45] :bigbutton [if not :winloop [guess]] "true 0 "OK "RET
setbutton nxt :tallheight :tallbutton [throw "master] "false 0 [new game] "N
setbutton nxt 25 :bigbutton [throw "quit] "false 0 "quit "Q
ignore nxt 10
setbutton nxt :tallheight :tallbutton [make "mysecret "true  throw "master] ~
   :mysecret 0 [I guess] "I
setbutton nxt :tallheight :tallbutton [make "mysecret "false  throw "master] ~
   (not :mysecret) 0 [Logo guess] "L
caption [-245 206] [65 29] [Number |of colors:|]
numsquares -170 2 6
caption [-10 206] [65 29] [Duplicates allowed:]
ifelse :LogoPlatform = "wxWidgets [
localmake "buttonx ((first :fontsize)*10)-5
setbutton list :buttonx 210 [25 25] [make "dup.ok "true throw "master] ~
   :dup.ok 0 "yes []
setbutton list :buttonx+30 210 [25 25] [make "dup.ok "false throw "master] ~
   (not :dup.ok) 0 "no []
] [
setbutton [70 210] [25 25] [make "dup.ok "true throw "master] ~
   :dup.ok 0 "yes []
setbutton [100 210] [25 25] [make "dup.ok "false throw "master] ~
   (not :dup.ok) 0 "no []
]
end

to numsquares :xcor :num :last
if :num > :last [stop]
setbutton (list :xcor 210) [25 25] `[make "numsquares ,:num throw "master] ~
   (:num = :numsquares) 0 :num []
numsquares :xcor+30 :num+1 :last
end

to colorchart :num :names :colors :ycor
if :num = 0 [stop]
setbutton (list -245 :ycor) [25 25] ~
  `[putguess ",[first :names] ,[first :colors]] "false ~
  (first :colors) [] (first :names)
colorchart :num-1 bf :names bf :colors :ycor-30
end

to numchart :num :ycor
if :num > :numsquares [stop]
setbutton (list -245 :ycor) [25 25] ~
  `[if not :winloop [getnum ,:num]] "false 0 :num :num
numchart :num+1 :ycor-30
end

to pos0 :pos
make "controlpos :pos
output :pos
end

to nxt :height
make "controlpos list (first :controlpos) ((last :controlpos) - (:height + 5))
output :controlpos
end

to move :start
; Move the turtle to the given coordinates
; relative to the lower left corner of the first empty square
; in the current frame.
; Depends on :COLUMN (0 or 1 for >14 guesses), :NUMGUESSES, and :NUMCOLORS
; Note, since :NUMGUESSES starts at 1,
; first frame is at [-180 170] not [-180 200].
pu
setpos (list (-145 + (first :start) + 210*:column + 25*(:numcolors-1))
	     (200 + (last :start) - 30*(:numguesses - 14*:column)))
pd
end 

; -----------------------------------------------

to newguess
; Called from MASTER for first guess frame,
; then from GUESS for later guess frames (my secret),
; or from DOGUESS (user's secret).
make "numguesses :numguesses+1
if :numguesses > 14 [make "column 1]
make "numcolors 1
move [0 0]
drawframe
make "guess "
end

to drawframe
setpc 7 seth 0
repeat :numsquares [square 25 rt 90 fd 25 lt 90]
end

to square :side
repeat 4 [fd :side rt 90]
end

;;;;; =================== USER INTERFACE (READING) =======================

to waitforclick
action.off
; Wait for any key or mouse click, then return, ignoring which/where.
if buttonp [while [buttonp] []  stop]
if keyp [ignore rc  stop]
waitforclick
end

; ----------- Procedures to carry out user commands ---------------

to getnum :num [:cursor cursor]
; Called for digit key or mouse click on digit button.
make ifelse :exact ["guess.exact] ["guess.inexact] :num
move list ifelse :exact [15] [35] 12
setpc 0 filled 0 [repeat 4 [fd 20 rt 90]]
setpc 7 label :num
; type :num setcursor :cursor
make "gotnum "true
end

to putguess :colorletter :colornumber
; Called from mouse click in color palette;
;   first input is a letter for :GUESS (e.g. R for red),
;   second input is a Logo color number for SETPC (e.g. 4 for red).
if :numcolors < 1 [stop]
if :numcolors > :numsquares [stop]
if not :dup.ok [if member? :colorletter :guess [stop]]
make "guess word :guess :colorletter
move [0 0]
filled :colornumber [repeat 4 [fd 25 rt 90]]
make "numcolors :numcolors+1
end

to clear
; Called by clicking ERASE button
if :numcolors < 2 [stop]
make "guess butlast :guess
make "numcolors :numcolors-1
move [0 0]
filled 0 [repeat 4 [fd 25 rt 90]]
end

to guess
; Called by clicking GUESS button.
if not :mysecret [if :gotnum [ct wait 0 throw "ready] stop]
if not (:numcolors > :numsquares) [stop]
ifelse equal? :guess :secret [
    move [15 12]
    setpc 7 label "WIN!
    print (sentence [You win in] :numguesses "turns.)
] [
    move [15 12]
    setpc 7 label exact :secret :guess
    move [35 12]
    setpc 7 label inexact :secret :guess
    newguess
]
end