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:
<cfsetting enablecfoutputonly="Yes" />
<!---
HiLo
We likes games...
To do:
- Handle creative users (guessing number outside of new hi-lo range, guessing same number twice, etc.)
- Tighten up code to conform to xhtml transitional and css standards.
- More appealing display.
- Mask goal value when saved in URL.
- etc.
--->
<cfset temp="#StructInsert(Request,'s_CurrentTemplateFile',GetFileFromPath(GetCurrentTemplatePath()),True)#" />
<cfset temp="#StructInsert(Request,'i_GoalMin',1,True)#" />
<cfset temp="#StructInsert(Request,'i_GoalMax',100,True)#" />
<cfparam name="URL.i_Goal"
default="" />
<cfparam name="Form.i_Goal"
default="#URL.i_Goal#" />
<cfset temp="#StructInsert(Request,'i_Goal',Form.i_Goal,True)#" />
<cfset temp="#StructInsert(Request,'i_GoalFormValue',Request.i_Goal,True)#" />
<cfparam name="URL.lsti_GuessHistory"
default="" />
<cfparam name="Form.lsti_GuessHistory"
default="#URL.lsti_GuessHistory#" />
<cfset temp="#StructInsert(Request,'lsti_GuessHistory',Form.lsti_GuessHistory,True)#" />
<cfparam name="URL.i_GuessLast"
default="" />
<cfparam name="Form.i_GuessLast"
default="#URL.i_GuessLast#" />
<cfset temp = "#StructInsert(Request,'i_GuessLast',Form.i_GuessLast,True)#" />
<cfset temp = "#StructInsert(Request,'s_GoalDisplay','???',True)#" />
<cfset temp = "#StructInsert(Request,'str_Comp',StructNew(),True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'lsti_GuessHistory','',True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'lsti_GuessHistorySorted','',True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'i_GuessCount',0,True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'idx_Hi',0,True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'idx_Lo',1,True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'s_GoalDisplay','???',True)#" />
<cfswitch expression="#Len(Request.i_Goal)#">
<cfcase value="0">
<cfset temp = "#StructInsert(Request,'s_HTMLTitle','New Game',True)#" />
<cfset temp = "#Randomize(DayOfYear(Now()) + Hour(Now()) + Minute(Now()) + Second(Now()))#" />
<cfset temp = "#StructInsert(Request,'i_Goal',RandRange(Val(Request.i_GoalMin),Val(Request.i_GoalMax)),True)#" />
<cfset temp = "#StructInsert(Request,'i_GoalFormValue',Request.i_Goal,True)#" />
<cfset temp = "#StructInsert(Request,'s_Message','Starting a new game. Enter a number below to begin.',True)#" />
<cfset temp = "#StructInsert(Request,'s_FormLegend','Let#Chr(39)#s get started...',True)#" />
<cfset temp = "#StructInsert(Request,'s_FormPrompt','Enter your first guess:',True)#" />
<cfset temp = "#StructInsert(Request,'s_FormButton','Click here to submit',True)#" />
<cfset temp = "#StructInsert(Request,'lsti_GuessHistory','',True)#" />
</cfcase>
<cfdefaultcase>
<cfswitch expression="#((IsNumeric(Request.i_GuessLast)) AND
(Request.i_GuessLast GE Request.i_GoalMin) AND
(Request.i_GuessLast LE Request.i_GoalMax))#">
<cfcase value="True">
<cfset temp = "#StructInsert(Request,'lsti_GuessHistory',ListAppend(Request.lsti_GuessHistory,Request.i_GuessLast,','),True)#" />
<cfswitch expression="#CompareNoCase(Request.i_GuessLast,Request.i_Goal)#">
<cfcase value="0">
<cfset temp = "#StructInsert(Request,'s_HTMLTitle','You win!',True)#" />
<cfset temp = "#StructUpdate(Request,'s_GoalDisplay',Request.i_Goal)#" />
<cfset temp = "#StructInsert(Request,'i_GoalFormValue','',True)#" />
<cfset temp = "#StructInsert(Request,'s_Message','Congratulations! Your guess of #Val(Request.i_GuessLast)# was correct!',True)#" />
<cfset temp = "#StructInsert(Request,'s_FormLegend','Start a new game?',True)#" />
<cfset temp = "#StructInsert(Request,'s_FormPrompt','',True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'i_GuessHi',Request.i_GoalMax,True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLo',Request.i_GoalMin,True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLast',Val((Request.str_Comp.i_GuessLo + Request.str_Comp.i_GuessHi + RandRange(0,1))\2),True)#" />
<cfloop condition="(Request.str_Comp.i_GuessLast NEQ Request.i_Goal)">
<cfset temp ="#StructUpdate(Request.str_Comp,'lsti_GuessHistory',ListAppend(Request.str_Comp.lsti_GuessHistory,Request.str_Comp.i_GuessLast,','))#" />
<cfswitch expression="#(Request.str_Comp.i_GuessLast GT Request.i_Goal)#">
<cfcase value="True">
<cfset temp = "#StructUpdate(Request.str_Comp,'i_GuessHi',Request.str_Comp.i_GuessLast - 1)#" />
</cfcase>
<cfdefaultcase>
<cfswitch expression="#(Request.str_Comp.i_GuessLast LT Request.i_Goal)#">
<cfcase value="True">
<cfset temp = "#StructUpdate(Request.str_Comp,'i_GuessLo',Request.str_Comp.i_GuessLast + 1)#" />
</cfcase>
</cfswitch>
</cfdefaultcase>
</cfswitch>
<cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLast',Val((Request.str_Comp.i_GuessLo + Request.str_Comp.i_GuessHi + RandRange(0,1))\2),True)#" />
<cfswitch expression="#(ListFindNoCase(Request.str_Comp.lsti_GuessHistory,Request.str_Comp.i_GuessLast,','))#">
<cfcase value="True">
<cfswitch expression="#(Request.str_Comp.i_GuessLast LT Request.str_Comp.i_GuessHi)#">
<cfcase value="True">
<cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLast',Val(Request.str_Comp.i_GuessLast + 1),True)#" />
</cfcase>
<cfdefaultcase>
<cfset temp = "#StructInsert(Request.str_Comp,'i_GuessLast',Val(Request.str_Comp.i_GuessLast - 1),True)#" />
</cfdefaultcase>
</cfswitch>
</cfcase>
</cfswitch>
</cfloop>
<cfset temp = "#StructInsert(Request.str_Comp,'lsti_GuessHistorySorted',
ListSort(Request.str_Comp.lsti_GuessHistory,'Numeric','Desc',','),True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'i_GuessCount',ListLen(Request.str_Comp.lsti_GuessHistory,','),True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'idx_Hi',ListFindNoCase(Request.str_Comp.lsti_GuessHistorySorted,Request.str_Comp.i_GuessHi + 1,','),True)#" />
<cfset temp = "#StructInsert(Request.str_Comp,'idx_Lo',Request.str_Comp.idx_Hi + 1,True)#" />
</cfcase>
<cfdefaultcase>
<cfset temp = "#StructInsert(Request,'s_HTMLTitle','Guess Again',True)#" />
<cfset temp = "#StructInsert(Request,'s_Message','You are almost there...',True)#" />
<cfset temp = "#StructInsert(Request,'s_FormLegend','Guess Again',True)#" />
<cfset temp = "#StructInsert(Request,'s_FormPrompt','Enter your next guess:',True)#" />
</cfdefaultcase>
</cfswitch>
</cfcase>
<cfdefaultcase>
<cfset temp = "#StructInsert(Request,'s_HTMLTitle','Oops',True)#" />
<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)#" />
<cfset temp = "#StructInsert(Request,'s_FormLegend','Try again',True)#" />
<cfset temp = "#StructInsert(Request,'s_FormPrompt','Enter your next guess:',True)#" />
</cfdefaultcase>
</cfswitch>
</cfdefaultcase>
</cfswitch>
<cfset temp="#StructInsert(Request,'lsti_GuessHistorySorted',ListSort(Request.lsti_GuessHistory,'Numeric','Desc',','),True)#" />
<cfset temp="#StructInsert(Request,'i_GuessCount',ListLen(Request.lsti_GuessHistorySorted,','),True)#" />
<cfset temp="#StructInsert(Request,'idx_Hi',Request.i_GuessCount,True)#" />
<cfset temp="#StructInsert(Request,'idx_Lo',Request.idx_Hi+1,True)#" />
<cfloop index="Request.idx" from="1" to="#Request.i_GuessCount#">
<cfswitch expression="#(ListGetAt(Request.lsti_GuessHistorySorted,Request.idx,',') EQ Request.i_Goal)#">
<cfcase value="True">
<cfset temp="#StructUpdate(Request,'idx_Hi',Request.idx - 1)#" />
<cfset temp="#StructUpdate(Request,'idx_Lo',Request.idx + 1)#" />
<cfbreak />
</cfcase>
<cfdefaultcase>
<cfswitch expression="#(ListGetAt(Request.lsti_GuessHistorySorted,Request.idx,',') LT Request.i_Goal)#">
<cfcase value="True">
<cfset temp="#StructUpdate(Request,'idx_Hi',Request.idx - 1)#" />
<cfset temp="#StructUpdate(Request,'idx_Lo',Request.idx)#" />
<cfbreak />
</cfcase>
</cfswitch>
</cfdefaultcase>
</cfswitch>
</cfloop>
<cfsetting enablecfoutputonly="No" />
<html>
<head>
<cfoutput>
<title>Hi-Lo's Helper: #Request.s_HTMLTitle#</title>
</cfoutput>
<basefont face="Trebuchet MS" color="Navy" />
<style>
body {
font-family: "Trebuchet MS";
color: Black;
}
h1 {
color: Navy;
font-size: 140%;
text-align: center;
margin: 0 0 0 0;
}
p.Hi {
text-align:right;
color:Navy;
font-size:300%;
}
p.Lo {
text-align:left;
color:Navy;
font-size:300%;
}
small {
font-size:84%;
}
</style>
</head>
<body onload="document.forms[0].i_GuessLast.focus()">
<cfoutput>
<table border="0"
cellpadding="2"
cellspacing="0"
width="600">
<tr>
<td colspan="9" valign="middle">
<h1 align="center"><sup>Hi</sup>-<sub>Lo</sub><small>'s Helper</small></h1>
</td>
</tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="600">
<tr>
<td colspan="3"
width="30%"
valign="bottom"
bgcolor="silver">
<p align="center"><strong>YOU</strong></p>
</td>
<td rowspan="5"
width="4"
bgcolor="silver">
<p> </p>
</td>
<td>
<p> </p>
</td>
<td rowspan="5"
width="4"
bgcolor="silver">
<p> </p>
</td>
<td colspan="3"
width="30%"
valign="bottom"
bgcolor="silver">
<p align="center"><strong>COMPUTER</strong></p>
</td>
</tr>
<tr>
<td valign="bottom">
<p class="Hi">Hi</p>
</td>
<td valign="bottom">
<p align="center" style="text-align:center;font-size: 140%;"><span style="color:Silver;">#Request.i_GoalMax#</span><br />
<strong>
<cfloop index="Request.idx"
from="1"
to="#Request.idx_Hi#">
<br />#ListGetAt(Request.lsti_GuessHistorySorted,Request.idx,',')#
</cfloop>
</strong></p>
</td>
<td>
<p> </p>
</td>
<td rowspan="3"
valign="top">
<p>#HTMLEditFormat(Request.s_Message)#</p>
<form method="post"
action="#Request.s_CurrentTemplateFile#">
<fieldset>
<legend>#HTMLEditFormat(Request.s_FormLegend)#</legend>
<p align="center">#HTMLEditFormat(Request.s_FormPrompt)# <input type="text" name="i_GuessLast" value="" size="3" maxlength="3" /><br />
<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>
</fieldset>
</form>
</td>
<td valign="bottom">
<p class="Hi">Hi</p>
</td>
<td valign="bottom">
<p align="center"
style="text-align:center;font-size: 140%;"><span style="color:Silver;">#Request.i_GoalMax#</span><br />
<strong>
<cfloop index="Request.idx" from="1" to="#Request.str_Comp.idx_Hi#">
<br />#ListGetAt(Request.str_Comp.lsti_GuessHistorySorted,Request.idx,',')#
</cfloop>
</strong></p>
</td>
<td>
<p> </p>
</td>
</tr>
<tr>
<td colspan="3">
<p align="center"
style="text-align:center;font-size:200%;border:outset thin silver;margin:0 0 0 0"> <strong>#HTMLEditFormat(Request.s_GoalDisplay)#</strong></p>
</td>
<td colspan="3">
<p align="center"
style="text-align:center;font-size:200%;border:outset thin silver;margin:0 0 0 0"> <strong>#HTMLEditFormat(Request.s_GoalDisplay)#</strong></p>
</td>
</tr>
<tr>
<td>
<p> </p>
</td>
<td valign="top">
<p align="center"
style="text-align:center;font-size: 140%;"><strong>
<cfloop index="Request.idx"
from="#Request.idx_Lo#"
to="#Request.i_GuessCount#">
#ListGetAt(Request.lsti_GuessHistorySorted,Request.idx,',')#<br />
</cfloop>
</strong><br />
<span style="color:Silver;">#Request.i_GoalMin#</span></p>
</td>
<td valign="top">
<p class="Lo">Lo</p>
</td>
<td>
<p> </p>
</td>
<td valign="top">
<p align="center"
style="text-align:center;font-size: 140%;"><strong>
<cfloop index="Request.idx"
from="#Request.str_Comp.idx_Lo#"
to="#Request.str_Comp.i_GuessCount#">
#ListGetAt(Request.str_Comp.lsti_GuessHistorySorted,Request.idx,',')#<br />
</cfloop>
</strong><br />
<span style="color:Silver;">#Request.i_GoalMin#</span></p>
</td>
<td valign="top">
<p class="Lo">Lo</p>
</td>
</tr>
<tr>
<td colspan="3"
width="30%"
bgcolor="silver">
<p align="center"><strong><small>COUNT:</small> #Request.i_GuessCount#</strong></p>
</td>
<td>
<p title="Add this link to your Favorites to save game"><small>Save game as:<br />
<!--- line below broken up a bit to display better on blog --->
<a href = "#Request.s_CurrentTemplateFile#?
i_goal=#Request.i_Goal#&lsti_GuessHistory=
#Request.lsti_GuessHistorySorted#">Hi-Lo -- Saved Game #DateFormat(Now(),'mmm dd')# #TimeFormat(Now(),'h.m')# #LCase(TimeFormat(Now(),'TT'))#</a></small></p>
</td>
<td colspan="3"
width="30%"
bgcolor="silver">
<p align="center"><strong><small>COUNT:</small> #Request.str_Comp.i_GuessCount#</strong></p>
</td>
</tr>
</table>
</cfoutput>
</body>
</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:
<cfset temp="#StructInsert(Request,'s_CurrentTemplateFile',GetFileFromPath(GetCurrentTemplatePath()),True)#" />
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:
<cfset StructInsert(Request,'s_CurrentTemplateFile',GetFileFromPath(GetCurrentTemplatePath()),True) />
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:
<cfset key = "name">
<cfset s = structNew()>
<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.