Welcome to the eleventh entry in the Intermediate ColdFusion Contest. (Only one more to go after this!) The earlier entries may be found at the end of this post. Today's entry is from Tony Weeg. Before reading on, please check his application here. You can download his code from the download link at the bottom. Please respect the copyright of the creator.
So far - this is my favorite design - although it gets points off for the buttons being a bit confusing. I had to ask Tony what the ^ button did. (It increases your bet by whatever button you had hit last.) Outside of that - I love the look of this entry. Now - in previous entries I spoke about how you can't trust JavaScript. I quickly found two ways to break this application. First - the bet is a read only form field. However, the Web Developer extension for Firefox let's me turn that setting off. Remember when I said to not trust JavaScript? You can't trust read only form fields either. When I changed the value to a string, the application immediately threw an error. Oh - and guess what happens if you enter a negative number? I entered -99999999. I then made myself lose by hitting too much. Then my bank was nice and rich.
The second way to break the application was in the initial screen. When it asks for the amount of money to start with, you can enter a string, and the application correctly ignores it. (Although it should probably tell you instead.) However - you are allowed to enter both 0 and a negative number. The game doesn't crash though - it just asks for more money. (Hey, just like a real casino!)
Ok, so let's dig into the code. Like an earlier entry, he resets his application variables on every hit. Not every variable, but still, none should be reset once set. He also creates card data from XML, which he does using cfsavecontent. He could have used cfxml as well. I normally would have placed the XML outside Application.cfm. But that's just a personal preference. His XML does seem a bit unnecessary though. Let me stop talking about it and show it for those who can't download the code:
<cfsavecontent variable="application.cardsXML"><?xml version='1.0' encoding='ISO-8859-1' ?><cards>
<Clubs>
<Ace>1</Ace>
<King>5</King>
<Queen>9</Queen>
<Jack>13</Jack>
<Ten>17</Ten>
<Nine>21</Nine>
<Eight>25</Eight>
<Seven>29</Seven>
<Six>33</Six>
<Five>37</Five>
<Four>41</Four>
<Three>45</Three>
<Two>49</Two>
</Clubs>
<Spades>
<Ace>2</Ace>
<King>6</King>
<Queen>10</Queen>
<Jack>14</Jack>
<Ten>18</Ten>
<Nine>22</Nine>
<Eight>26</Eight>
<Seven>30</Seven>
<Six>34</Six>
<Five>38</Five>
<Four>42</Four>
<Three>46</Three>
<Two>50</Two>
</Spades>
<Hearts>
<Ace>3</Ace>
<King>7</King>
<Queen>11</Queen>
<Jack>15</Jack>
<Ten>19</Ten>
<Nine>23</Nine>
<Eight>27</Eight>
<Seven>31</Seven>
<Six>35</Six>
<Five>39</Five>
<Four>43</Four>
<Three>47</Three>
<Two>51</Two>
</Hearts>
<Diamonds>
<Ace>4</Ace>
<King>8</King>
<Queen>12</Queen>
<Jack>16</Jack>
<Ten>20</Ten>
<Nine>24</Nine>
<Eight>28</Eight>
<Seven>32</Seven>
<Six>36</Six>
<Five>40</Five>
<Four>44</Four>
<Three>48</Three>
<Two>52</Two>
</Diamonds>
</cards></cfsavecontent>
Looking at this I can't help but wonder if a simple loop could have worked as well. One loop over the suits and one over the string names of the cards. I'm not saying this is bad - I'm just saying there are many ways to skin the cat.
Something else I noticed - in fullTilt.cfm he has this code block:
<cfif isDefined("url.clearStartOver") and len(url.clearStartOver)>
<cfset structDelete(session,"player")>
<cfset structDelete(session,"dealer")>
<cfset structDelete(session,"yourName")>
<cfset structDelete(session,"lastBetValue")>
<cfset structDelete(session,"gameInProgress")>
<cfset structDelete(session,"winnerDisplay")>
<cfset structDelete(session,"youAlreadyWon")>
<cfset structDelete(session,"bankValue")>
<cfset structDelete(session,"bankValueForDisplay")>
<cfset structDelete(session,"gameOver")>
<cflocation url="fullTilt.cfm?step=welcomeToTheTable">
</cfif>
<cfif isDefined("url.makeDeposit") and len(url.makeDeposit)>
<cfset structDelete(session,"player")>
<cfset structDelete(session,"dealer")>
<cfset structDelete(session,"lastBetValue")>
<cfset structDelete(session,"gameInProgress")>
<cfset structDelete(session,"winnerDisplay")>
<cfset structDelete(session,"youAlreadyWon")>
<cfset structDelete(session,"bankValue")>
<cfset structDelete(session,"bankValueForDisplay")>
<cfset structDelete(session,"gameOver")>
<cflocation url="fullTilt.cfm?step=makeDeposit">
</cfif>
I would have considered putting the game data in a sub-struct of the Session scope. That way you could nuke the entire game with one structDelete. Or I'd consider making a UDF that does the same as the lines above. That way if you add a new key, you can just update the UDF. (Yes, you can make UDFs that work with the session scope. Normally it is a no-no, but for a utility function like this, I think it would be fine.) Ah - I just reread the code and each block is doing something different. I would still recommend though a more modular approach - again - in case your game data keys change.
Let's turn to his CFC. As you know, I'm going to complain about the lack of var scoping. Sorry Tony. :) Something else I don't agree with - he loads a file of UDFs into his CFC. Not the end of the world, of course, but if I need to use UDFs like that in a CFC, I typically turn them into a CFC.
What does everyone else think?
Earlier Entries:
Archived Comments
yeah, Tony and I actually gave each other previews. I knew his design was gonna be one of the better ones. my only suggestion on the design would be to put the dealer on top. other than that, very sweet. it looks like a "real" blackjack game :)
I am not sure that aces are getting counted as 1 or 11. I had a king and 3 for a total of 13, my next card was an ace and right away the dealers cards were flipped, he was showing 20 and I lost the game. I didnt see it again after trying another 10 hands or so, i am wondering if anyone else came across this?
so, its all willy nilly now, as its already done, but a few things....
1. the xml for the cards was one of the last things i was messing around with, so i just wanted to get it done, and got lazy :) same with the udf's in there... i normally turn those into functions, but got to the final hours, and was more worried about the UI than anything (hey, im a designer, not a ninja)
2. dan, you are correct, i have a few funky situations where the aces are counted wrong, and even another situation where if the dealer has 17 or higher, and you have 18 or higher, it automatically gives you the win :) (im a nice dealer)
3. also, with the read only form field, you can break it another way and that is on the VERY first hand, its not read only. i think, its due to a session variable not existing until the first bet is made or something like that.
but anyway, this was cool, this was fun, and it was at times a bit challenging. had i had another two days to mess with it, probably would have caught some of the stuff that i went "DOH!!!!" to, right after i finally submitted. anyway. critique away, please. i want to get better. thanks!
Nice work Tony, the game looks pretty cool. I did get a little confused on what I needed to do each turn, and I dont really understand the reshuffle button.
I had the same problem with Aces not getting counted as 1. I had a 3,A, I hit and busted with a 10.
I noticed a lot of people kept track of all the card data, such as suit and rank. I can see how this would be valuable if you were using your cards in another game, but in blackjack you really dont need it. In my app, my deck is just a random array of 52 numbers, each representing a card. From those numbers, I just use a pretty simple formula to get the card value from the number. I started my app trying to keep track of suit, rank, and value, but found I could do it much simpler with just an array of numbers.
two more things :)
1. ray, where did i not var scope? i made SURE that i var scoped everything in the functions.
2. there are tool tips on EVERY one of the buttons... :)
thanks scott. i know, there are odd things with the counting... i effed that up. and reshuffle probably should have be relabeled :)
Since the XML is hard-coded (in its current form) I would keep it as is. Why use a loop to generate hard coded values?
I wish that reshuffle and bet were one step instead of two.
I wish that the number buttons would add to the current amount (as opposed to resetting it).
Tony,
You did a good job var scoping, but you missed every single one of your indexes... When you loop, the index get assigned to a variable, just like everything else.
-Rob
so, what you are saying is that i should have something like this:
<cfset var i = 0>
like that?
perhaps, or because coldfusion is mostly typeless, you could do <cfset var i = '' />
i always feel bad for the variable if i dont at least feed it something.
Visually the game looks pretty slick, but it felt "sluggish" sometimes. For example, I'd click to bet and it would take a second or so before the game reacted by reloading the page and showing the new cards.
Also, the HTML title of the page is "Full Tilt Poker", not "Full Tilt Blackjack" :)
My only other comment is that the startup logic at the top of fullTilt.cfm should be moved into include files or UDFs so that its easier to follow. This could be as simple as replacing the body of each cfif block with a single UDF call, but it would go a long way towards making the code easier to read.
the sluggishness might just be ray's box... from my server (at cfdynamics) its fast... when i hit it from my office, and my house :)
http://www.revolutionwebdes...
and yeah, i DEFINITELY would have done a bunch more towards some optimization in the code and making it easier to read, etc... but im sooo busy with work, i just didnt have time... its all good, like i said, im more developer/designer than coder... but i'd love to hear about what i could do to make it better.
thanks!
> but i'd love to hear about what i could do to make it
> better.
One thing I like to do is plan out my program flow using "pseudo-code", which basically just captures the high-level actions that need to take place. For instance:
if is new game
init user's session
init dealer
init deck
get opening bet
end if
Then I'll go back and create UDFs or CFC methods for each line of pseudo code, so that my final code doesn't look that different from the pseudo code I started with. I've found that this helps me group the code into logical chunks and makes it more manageable and readable.