Posted in ColdFusion | Posted on 10-07-2005 | 5,092 views
Welcome to the third entry in the ColdFusion contest that I'm running. For those who are wondering, there were seven entries, and my goal is to wrap this up by next Friday, otherwise I won't be able to name a winner till after MAX. If you haven't looked at the previous entries, here are the links: Entry 1, Entry 2.
The third entry may be viewed here. As before, I suggest you go play with it a bit before reading the rest of the entry. The code for the entry is listed below:
2<!---
3HiLo
4We likes games...
5
6To do:
71) Handle creative users (guessing number outside of new hi-lo range, guessing same number twice, etc.)
82) Tighten up code to conform to xhtml transitional and css standards.
93) More appealing display.
104) Mask goal value when saved in URL.
115) etc.
12--->
13
14<cfset temp="#StructInsert(Request,'s_CurrentTemplateFile',GetFileFromPath(GetCurrentTemplatePath()),True)#" />
15
16<cfset temp="#StructInsert(Request,'i_GoalMin',1,True)#" />
17<cfset temp="#StructInsert(Request,'i_GoalMax',100,True)#" />
18
19<cfparam name="URL.i_Goal"
20 default="" />
21<cfparam name="Form.i_Goal"
22 default="#URL.i_Goal#" />
23<cfset temp="#StructInsert(Request,'i_Goal',Form.i_Goal,True)#" />
24<cfset temp="#StructInsert(Request,'i_GoalFormValue',Request.i_Goal,True)#" />
25
26<cfparam name="URL.lsti_GuessHistory"
27 default="" />
28<cfparam name="Form.lsti_GuessHistory"
29 default="#URL.lsti_GuessHistory#" />
30<cfset temp="#StructInsert(Request,'lsti_GuessHistory',Form.lsti_GuessHistory,True)#" />
31
32
33<cfparam name="URL.i_GuessLast"
34 default="" />
35<cfparam name="Form.i_GuessLast"
36 default="#URL.i_GuessLast#" />
37<cfset temp = "#StructInsert(Request,'i_GuessLast',Form.i_GuessLast,True)#" />
38
39<cfset temp = "#StructInsert(Request,'s_GoalDisplay','???',True)#" />
40
41
42<cfset temp = "#StructInsert(Request,'str_Comp',StructNew(),True)#" />
43<cfset temp = "#StructInsert(Request.str_Comp,'lsti_GuessHistory','',True)#" />
44<cfset temp = "#StructInsert(Request.str_Comp,'lsti_GuessHistorySorted','',True)#" />
45<cfset temp = "#StructInsert(Request.str_Comp,'i_GuessCount',0,True)#" />
46<cfset temp = "#StructInsert(Request.str_Comp,'idx_Hi',0,True)#" />
47<cfset temp = "#StructInsert(Request.str_Comp,'idx_Lo',1,True)#" />
48<cfset temp = "#StructInsert(Request.str_Comp,'s_GoalDisplay','???',True)#" />
49
50
51<cfswitch expression="#Len(Request.i_Goal)#">
52 <cfcase value="0">
53
54 <cfset temp = "#StructInsert(Request,'s_HTMLTitle','New Game',True)#" />
55
56 <cfset temp = "#Randomize(DayOfYear(Now()) + Hour(Now()) + Minute(Now()) + Second(Now()))#" />
57 <cfset temp = "#StructInsert(Request,'i_Goal',RandRange(Val(Request.i_GoalMin),Val(Request.i_GoalMax)),True)#" />
58 <cfset temp = "#StructInsert(Request,'i_GoalFormValue',Request.i_Goal,True)#" />
59
60 <cfset temp = "#StructInsert(Request,'s_Message','Starting a new game. Enter a number below to begin.',True)#" />
61 <cfset temp = "#StructInsert(Request,'s_FormLegend','Let#Chr(39)#s get started...',True)#" />
62 <cfset temp = "#StructInsert(Request,'s_FormPrompt','Enter your first guess:',True)#" />
63 <cfset temp = "#StructInsert(Request,'s_FormButton','Click here to submit',True)#" />
64 <cfset temp = "#StructInsert(Request,'lsti_GuessHistory','',True)#" />
65
66 </cfcase>
67 <cfdefaultcase>
68
69 <cfswitch expression="#((IsNumeric(Request.i_GuessLast)) AND
70 (Request.i_GuessLast GE Request.i_GoalMin) AND
71 (Request.i_GuessLast LE Request.i_GoalMax))#">
72 <cfcase value="True">
73
74 <cfset temp = "#StructInsert(Request,'lsti_GuessHistory',ListAppend(Request.lsti_GuessHistory,Request.i_GuessLast,','),True)#" />
75
76 <cfswitch expression="#CompareNoCase(Request.i_GuessLast,Request.i_Goal)#">
77 <cfcase value="0">
78
79 <cfset temp = "#StructInsert(Request,'s_HTMLTitle','You win!',True)#" />
80 <cfset temp = "#StructUpdate(Request,'s_GoalDisplay',Request.i_Goal)#" />
81
82 <cfset temp = "#StructInsert(Request,'i_GoalFormValue','',True)#" />
83
84 <cfset temp = "#StructInsert(Request,'s_Message','Congratulations! Your guess of #Val(Request.i_GuessLast)# was correct!',True)#" />
85 <cfset temp = "#StructInsert(Request,'s_FormLegend','Start a new game?',True)#" />
86 <cfset temp = "#StructInsert(Request,'s_FormPrompt','',True)#" />
87
88
89 <cfset temp = "#StructInsert(Request.str_Comp,'i_GuessHi',Request.i_GoalMax,True)#" />
90 <cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLo',Request.i_GoalMin,True)#" />
91
92 <cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLast',Val((Request.str_Comp.i_GuessLo + Request.str_Comp.i_GuessHi + RandRange(0,1))\2),True)#" />
93
94 <cfloop condition="(Request.str_Comp.i_GuessLast NEQ Request.i_Goal)">
95 <cfset temp ="#StructUpdate(Request.str_Comp,'lsti_GuessHistory',ListAppend(Request.str_Comp.lsti_GuessHistory,Request.str_Comp.i_GuessLast,','))#" />
96
97 <cfswitch expression="#(Request.str_Comp.i_GuessLast GT Request.i_Goal)#">
98 <cfcase value="True">
99 <cfset temp = "#StructUpdate(Request.str_Comp,'i_GuessHi',Request.str_Comp.i_GuessLast - 1)#" />
100 </cfcase>
101 <cfdefaultcase>
102
103 <cfswitch expression="#(Request.str_Comp.i_GuessLast LT Request.i_Goal)#">
104 <cfcase value="True">
105 <cfset temp = "#StructUpdate(Request.str_Comp,'i_GuessLo',Request.str_Comp.i_GuessLast + 1)#" />
106 </cfcase>
107 </cfswitch>
108
109 </cfdefaultcase>
110 </cfswitch>
111
112 <cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLast',Val((Request.str_Comp.i_GuessLo + Request.str_Comp.i_GuessHi + RandRange(0,1))\2),True)#" />
113
114 <cfswitch expression="#(ListFindNoCase(Request.str_Comp.lsti_GuessHistory,Request.str_Comp.i_GuessLast,','))#">
115 <cfcase value="True">
116
117 <cfswitch expression="#(Request.str_Comp.i_GuessLast LT Request.str_Comp.i_GuessHi)#">
118 <cfcase value="True">
119 <cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLast',Val(Request.str_Comp.i_GuessLast + 1),True)#" />
120 </cfcase>
121 <cfdefaultcase>
122 <cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLast',Val(Request.str_Comp.i_GuessLast - 1),True)#" />
123 </cfdefaultcase>
124 </cfswitch>
125
126 </cfcase>
127 </cfswitch>
128
129 </cfloop>
130
131 <cfset temp = "#StructInsert(Request.str_Comp,'lsti_GuessHistorySorted',
132ListSort(Request.str_Comp.lsti_GuessHistory,'Numeric','Desc',','),True)#" />
133 <cfset temp = "#StructInsert(Request.str_Comp,'i_GuessCount',ListLen(Request.str_Comp.lsti_GuessHistory,','),True)#" />
134
135
136 <cfset temp = "#StructInsert(Request.str_Comp,'idx_Hi',ListFindNoCase(Request.str_Comp.lsti_GuessHistorySorted,Request.str_Comp.i_GuessHi + 1,','),True)#" />
137 <cfset temp = "#StructInsert(Request.str_Comp,'idx_Lo',Request.str_Comp.idx_Hi + 1,True)#" />
138
139
140 </cfcase>
141 <cfdefaultcase>
142
143 <cfset temp = "#StructInsert(Request,'s_HTMLTitle','Guess Again',True)#" />
144
145 <cfset temp = "#StructInsert(Request,'s_Message','You are almost there...',True)#" />
146 <cfset temp = "#StructInsert(Request,'s_FormLegend','Guess Again',True)#" />
147 <cfset temp = "#StructInsert(Request,'s_FormPrompt','Enter your next guess:',True)#" />
148 </cfdefaultcase>
149 </cfswitch>
150
151 </cfcase>
152 <cfdefaultcase>
153
154 <cfset temp = "#StructInsert(Request,'s_HTMLTitle','Oops',True)#" />
155
156 <cfset temp = "#StructInsert(Request,'s_Message','Resuming from saved game or you entered an invalid number. Please enter a number between #Val(Request.i_GoalMin)# and #Val(Request.i_GoalMax)# to continue.',True)#" />
157 <cfset temp = "#StructInsert(Request,'s_FormLegend','Try again',True)#" />
158 <cfset temp = "#StructInsert(Request,'s_FormPrompt','Enter your next guess:',True)#" />
159
160 </cfdefaultcase>
161 </cfswitch>
162
163 </cfdefaultcase>
164</cfswitch>
165
166<cfset temp="#StructInsert(Request,'lsti_GuessHistorySorted',ListSort(Request.lsti_GuessHistory,'Numeric','Desc',','),True)#" />
167
168<cfset temp="#StructInsert(Request,'i_GuessCount',ListLen(Request.lsti_GuessHistorySorted,','),True)#" />
169
170<cfset temp="#StructInsert(Request,'idx_Hi',Request.i_GuessCount,True)#" />
171<cfset temp="#StructInsert(Request,'idx_Lo',Request.idx_Hi+1,True)#" />
172
173
174<cfloop index="Request.idx" from="1" to="#Request.i_GuessCount#">
175
176 <cfswitch expression="#(ListGetAt(Request.lsti_GuessHistorySorted,Request.idx,',') EQ Request.i_Goal)#">
177 <cfcase value="True">
178 <cfset temp="#StructUpdate(Request,'idx_Hi',Request.idx - 1)#" />
179 <cfset temp="#StructUpdate(Request,'idx_Lo',Request.idx + 1)#" />
180
181 <cfbreak />
182
183 </cfcase>
184 <cfdefaultcase>
185
186 <cfswitch expression="#(ListGetAt(Request.lsti_GuessHistorySorted,Request.idx,',') LT Request.i_Goal)#">
187 <cfcase value="True">
188 <cfset temp="#StructUpdate(Request,'idx_Hi',Request.idx - 1)#" />
189 <cfset temp="#StructUpdate(Request,'idx_Lo',Request.idx)#" />
190
191 <cfbreak />
192
193 </cfcase>
194 </cfswitch>
195
196 </cfdefaultcase>
197 </cfswitch>
198
199</cfloop>
200
201<cfsetting enablecfoutputonly="No" />
202
203<html>
204 <head>
205 <cfoutput>
206 <title>Hi-Lo's Helper: #Request.s_HTMLTitle#</title>
207 </cfoutput>
208 <basefont face="Trebuchet MS" color="Navy" />
209 <style>
210body {
211 font-family: "Trebuchet MS";
212 color: Black;
213}
214h1 {
215 color: Navy;
216 font-size: 140%;
217 text-align: center;
218 margin: 0 0 0 0;
219}
220p.Hi {
221 text-align:right;
222 color:Navy;
223 font-size:300%;
224}
225p.Lo {
226 text-align:left;
227 color:Navy;
228 font-size:300%;
229}
230small {
231 font-size:84%;
232}
233 </style>
234
235 </head>
236 <body onload="document.forms[0].i_GuessLast.focus()">
237
238 <cfoutput>
239
240 <table border="0"
241 cellpadding="2"
242 cellspacing="0"
243 width="600">
244 <tr>
245 <td colspan="9" valign="middle">
246 <h1 align="center"><sup>Hi</sup>-<sub>Lo</sub><small>'s Helper</small></h1>
247 </td>
248 </tr>
249 </table>
250 <table border="0" cellpadding="2" cellspacing="0" width="600">
251 <tr>
252 <td colspan="3"
253 width="30%"
254 valign="bottom"
255 bgcolor="silver">
256 <p align="center"><strong>YOU</strong></p>
257 </td>
258 <td rowspan="5"
259 width="4"
260 bgcolor="silver">
261 <p> </p>
262 </td>
263 <td>
264 <p> </p>
265 </td>
266 <td rowspan="5"
267 width="4"
268 bgcolor="silver">
269 <p> </p>
270 </td>
271 <td colspan="3"
272 width="30%"
273 valign="bottom"
274 bgcolor="silver">
275 <p align="center"><strong>COMPUTER</strong></p>
276 </td>
277 </tr>
278 <tr>
279 <td valign="bottom">
280 <p class="Hi">Hi</p>
281 </td>
282 <td valign="bottom">
283 <p align="center" style="text-align:center;font-size: 140%;"><span style="color:Silver;">#Request.i_GoalMax#</span><br />
284 <strong>
285 <cfloop index="Request.idx"
286 from="1"
287 to="#Request.idx_Hi#">
288 <br />#ListGetAt(Request.lsti_GuessHistorySorted,Request.idx,',')#
289 </cfloop>
290 </strong></p>
291 </td>
292 <td>
293 <p> </p>
294 </td>
295 <td rowspan="3"
296 valign="top">
297 <p>#HTMLEditFormat(Request.s_Message)#</p>
298 <form method="post"
299 action="#Request.s_CurrentTemplateFile#">
300 <fieldset>
301 <legend>#HTMLEditFormat(Request.s_FormLegend)#</legend>
302 <p align="center">#HTMLEditFormat(Request.s_FormPrompt)# <input type="text" name="i_GuessLast" value="" size="3" maxlength="3" /><br />
303 <input type="submit" name="btn_Guess" value="Click here to submit" /><input type="hidden" name="i_Goal" value="#Request.i_GoalFormValue#" /><input type="hidden" name="lsti_GuessHistory" value="#Request.lsti_GuessHistorySorted#" /></p>
304 </fieldset>
305 </form>
306 </td>
307 <td valign="bottom">
308 <p class="Hi">Hi</p>
309 </td>
310 <td valign="bottom">
311 <p align="center"
312 style="text-align:center;font-size: 140%;"><span style="color:Silver;">#Request.i_GoalMax#</span><br />
313 <strong>
314 <cfloop index="Request.idx" from="1" to="#Request.str_Comp.idx_Hi#">
315 <br />#ListGetAt(Request.str_Comp.lsti_GuessHistorySorted,Request.idx,',')#
316 </cfloop>
317 </strong></p>
318 </td>
319 <td>
320 <p> </p>
321 </td>
322 </tr>
323 <tr>
324 <td colspan="3">
325 <p align="center"
326 style="text-align:center;font-size:200%;border:outset thin silver;margin:0 0 0 0"> <strong>#HTMLEditFormat(Request.s_GoalDisplay)#</strong></p>
327 </td>
328 <td colspan="3">
329 <p align="center"
330 style="text-align:center;font-size:200%;border:outset thin silver;margin:0 0 0 0"> <strong>#HTMLEditFormat(Request.s_GoalDisplay)#</strong></p>
331 </td>
332 </tr>
333 <tr>
334 <td>
335 <p> </p>
336 </td>
337 <td valign="top">
338 <p align="center"
339 style="text-align:center;font-size: 140%;"><strong>
340 <cfloop index="Request.idx"
341 from="#Request.idx_Lo#"
342 to="#Request.i_GuessCount#">
343 #ListGetAt(Request.lsti_GuessHistorySorted,Request.idx,',')#<br />
344 </cfloop>
345 </strong><br />
346 <span style="color:Silver;">#Request.i_GoalMin#</span></p>
347 </td>
348 <td valign="top">
349 <p class="Lo">Lo</p>
350 </td>
351 <td>
352 <p> </p>
353 </td>
354 <td valign="top">
355 <p align="center"
356 style="text-align:center;font-size: 140%;"><strong>
357 <cfloop index="Request.idx"
358 from="#Request.str_Comp.idx_Lo#"
359 to="#Request.str_Comp.i_GuessCount#">
360 #ListGetAt(Request.str_Comp.lsti_GuessHistorySorted,Request.idx,',')#<br />
361 </cfloop>
362 </strong><br />
363 <span style="color:Silver;">#Request.i_GoalMin#</span></p>
364 </td>
365 <td valign="top">
366 <p class="Lo">Lo</p>
367 </td>
368 </tr>
369 <tr>
370 <td colspan="3"
371 width="30%"
372 bgcolor="silver">
373 <p align="center"><strong><small>COUNT:</small> #Request.i_GuessCount#</strong></p>
374 </td>
375 <td>
376 <p title="Add this link to your Favorites to save game"><small>Save game as:<br />
377<!--- line below broken up a bit to display better on blog --->
378 <a href = "#Request.s_CurrentTemplateFile#?
379i_goal=#Request.i_Goal#&lsti_GuessHistory=
380#Request.lsti_GuessHistorySorted#">Hi-Lo -- Saved Game #DateFormat(Now(),'mmm dd')# #TimeFormat(Now(),'h.m')# #LCase(TimeFormat(Now(),'TT'))#</a></small></p>
381 </td>
382 <td colspan="3"
383 width="30%"
384 bgcolor="silver">
385 <p align="center"><strong><small>COUNT:</small> #Request.str_Comp.i_GuessCount#</strong></p>
386 </td>
387 </tr>
388 </table>
389
390 </cfoutput>
391
392 </body>
393</html>
So, let me start with my general observations before digging into the code. While I didn't want to make this a "visual" contest, I do like the design of this one. The only thing that confused me is the "Computer" box on the right. It seems to reflect the computer's attempt to guess the number at the same time you do - but you only see the computer's guesses at the end. That's why I'm a bit confused. (I'm thinking the author may post a comment and clear this all up.) I think that is kind of neat as it gives you a competitor, but it is kind of odd that you only see it at the end. Either way, he made the computer "imperfect" as well, which is good game design. Another note - like the first entry, the author reversed my original intent of making the computer guess. Again though, that's fine. (For the next contest I'll try to be more clear in the specs.) Now let's dig into the code a bit.
Like the other entries, this one doesn't do proper validation of the variables. The author does know this and notes it in the header. I hate to harp on it - but my readers know this is one of the things that I like to be anal about. I always bring it up because far too many public sites don't do a good job of it.
There are two things in particular I want to point out about the code. I'm not calling these mistakes, but differences in style. This line is repeated (with variations in the values of course) throughout the template:
The first thing I'd point out is that for functions that return a "throw away" value, or a value you simply don't need, as above, you can rewrite the code like so:
This is a bit less typing, and in my opinion, a bit cleaner. Secondly, I've never been a fan of structInsert. It isn't a bad function per se, I just don't like the extra typing. I've never heard a good reason to use it. I have heard people say they use it to insert dynamic keys into a structure, but bracket notation works fine that as well:
2<cfset s = structNew()>
3<cfset s[key] = "Jacob">
Again, this is just personal preference on my part.
So, my last comment, and this is definitely in the realm of being picky - I don't care for the use of cfswitch. I find it makes it a bit hard to follow the logic flow. How do others feel?
p.s. In general, the comments I've seen on my blog postings about this contest have been very fair and polite. I ask that people keep this in mind. These are beginners, and we all make mistakes. So please be gentle.


And personally, I like cfswitch for any case where I have multiple if statements using the same variable.
It took me 6 guesses to get 88 as the magic number:
50,75,87,95,90,88
It took the computer 6 guesses also
51,76,82,86,89,88
However, under the computer answer it says it took only a COUNT: 5
supposedly this URL will show the final results:
http://ray.camdenfamily.com/demos/contest1/entry3/...
It really depends on the condition you are evaluating, and how many possibilities there are. I personally find it easier to read, when used properly.
For example (can we use code in the comments, I'm about to find out):
<code>
<cfif MyVar is "1">
do something
<cfelseif MyVar is "2">
Do something else
<Cfelseif MyVar is "3">
Do yet another something
etc.. etc.. etc..
</cfif>
</code>
That would be easier to read as a cfswitch, especially with lots of nested cfifs.
Either you need to be able to instantly start a game, or remove the text input.
I really like the idea of the competitor. Since he programmed the logic of the computer playing both sides, it would be cool if when you started playing you selected which game you wanted to play, you could either make the computer guess your number, or you guess the computer's number. Rather than the computer and you both guessing the computer's number.
Other UI flaw:
The "You are almost there..." text. Two things: 1) I thought this was an actual clue the first game I played. I guessed 50 and it told me I was almost there... so I thought the end number was close to 50, however it was actually 100. Doh! 2) Clues would ruin the game IMHO.
Also wanted to say thanks for doing this contest and evaluations, I didn't enter because I don't consider myself a novice, but I'm tempted to write my own solution just to compare against how others have done it. It's fascinating to see people's interpretations of your task and the variety of ways to solve the problem.
I think it illustrates an important point to us all. If you are really stuck on something, it often times can be best to start over and try a different approach since there are so many ways to solve a problem.
I hadn't really looked at the code previously.
The comment about the count bug (Posted By Bill / Posted At 10/7/05 9:08 AM) is a great catch. I ran across this myself playing the game over the weekend. Doh! Let's just chalk it up to stacking the odds in the house's favor;-)
The UI flaw with the text input box (Posted By ErikG / Posted At 10/7/05 10:36 AM) needs to be fixed. Just plain ugly.
The computer can't show its guesses each time the player guesses because this would aid the player. So it actually just does all of it's guessing once the player finds the match. Then the counts are compared. The interface should explain this.
The computer doesn't actually just randomly guess. It does use a random solution to an interesting situation that crops up. If there are an odd number of integers left in the valid set, then picking the middle number is easy. If there are an even number of integers, then how do you pick the middle number. There is none, or two, depending on your point-of-view. You could always default to the lowest, but this does seem too rigid. So it randomly chooses to go high or low. With a big enough statistical set it probably makes no diff, but it's just more fun. (Also in the design of game where the user gets to set the goal number, it reduces the ability of the user to outsmart the computer. e.g A number like 51 could take 7 steps to reach w/ a defaulting to low algorithm, or just 1 step in a defaulting to high.)
The use of CFSWITCH and CFSET (w/ StructInsert and a throw way result) is an interesting habit. For a while I was playing around with a strongly validating editor that wanted everything in a tag written as attribute_name=quoted_value. I could 'teach' the editor to recognize a few new attribute names for the CFSET tag (e.g. 'temp'). Actually even though I've since switched to CF Studio, all my code still tries to look like well-formed XML. I'm hoping one day it will pay-off, but I agree that it is much less clear to read. If only I could get Macromedia to create two new cfset attributes such as varname= and varvalue=.
Thanks again. (If I can squeeze in some time I may try a version 2 where the user and computer truly go head-to-head.)
[Add Comment] [Subscribe to Comments]