Raymond Camden's Blog Rss

Friday Puzzler: Joe's Car Wash and the Cheap Employee Problem

54

Posted in ColdFusion | Posted on 03-25-2011 | 3,042 views

Meet Joe. Joe runs a local car wash and is notorious for having an odd smell and being notoriously cheap. So cheap in fact that he pays his employees the littlest possible and therefore has the laziest employees in existence. This causes many problems for Joe as he has to ensure each and every task he gives them is as simple and as direct as possible. No critical thinking allowed here. You're going to write a program for Joe (in exchange for 2 free car washes, except on days that end in Y) to help Joe with a particular problem: Signage.

Joe has one of those signs where you can put messages up one letter at a time. Not one of those fancy electronic ones. Of course not - that would cost too much. No - this is one of those signs where you have to use the 30 foot long hook and place up one letter at a time. This makes changing the message a real pain in the rear. Your program is going to help make this problem easier. Here's the basic idea.

Given a message, your code will:

a) Return each unique letter and the number of instances. This will tell the employee how many As they have to bring, how many Bs, etc.

b) Given a width attribute that represents how many "slots" are available per row. For now, don't worry about the number of rows (it's a tall sign). But assuming that each row is N slots wide, you need to determine which words will fit per row.

So all in all your API is something like this: getSignInfo(message,width) and returns a structure of two complex values - the results detailed in A and B.

So - I actually have a real prize to offer for this. By writing a Blackberry Playbook app (Hangman, I'm going to sell it for free but get rich on volume), I was awarded a copy of ColdFusion Builder and Flash Builder Pro. I'll be selecting the winner from comments posted to the blog entry by 5PM CST. Please note this contest is 100% arbitrary so unabashed flattery is a bonus. Have fun with it. I'd recommend Pastebin for your code samples.

Comments

[Add Comment] [Subscribe to Comments]

Fun puzzle Ray!

Here's my crack at it: http://pastebin.com/Vfyib49T
Actually - I made one minor mod to ucase() the letters since those signs typically only use upper case. Here's the modded version:

http://pastebin.com/XJjR1Htm
Fitting the words into each line was a little tricky but here's my code.

http://pastebin.com/8QQXby0H
@Robert: Sorry Robert, tag based solutions automatically lose and do not get to go home early on Fridays.

*joking*
This one was fun. Maybe I went overboard on the options, but I like options.

<cffunction name="getSignInfo" returntype="Struct">
   <cfargument name="message" type="string" required="true">
   <cfargument name="width" type="numeric" required="true">
   <cfargument name="includeSpaces" type="boolean" required="false" default="false"/>
   <cfargument name="fixWidthIssues" type="boolean" required="false" default="true">
   
   <cfset var signDetails = {}/>
   <cfset var adjustedMessage = trim(arguments.message)/>
   <cfset var charArr = listtoARray(adjustedMessage,'')/>
   <cfset var wordArr = listtoARray(adjustedMessage,' ')/>
   <cfset var i = ""/>
   <cfset var wordLength = ""/>
   <cfset var currentRow = 1/>
   <cfset var slotsFilled = 0/>
   
   <!---
      Examine each word in the message.
      If a word is too long for the width of the sign, we'll add spaces to adjust the message (Basically creating a "word wrap" feature)
      Optionally, we can throw an error when this happens.
    --->   
   <cfloop from="1" to="#arraylen(wordArr)#" index="i">
      <cfset wordLength = len(wordarr[i])/>
      <cfif wordLength gt arguments.width>
         <cfif arguments.fixWidthIssues>   
            <cfset wordArr[i] = left(wordArr[i],arguments.width) & " " & mid(wordArr[i],arguments.width,wordLength)/>
         <cfelse>
            <cfthrow message="Oh noes! One of your words is too long for the sign!"/>            
         </cfif>
      </cfif>
   </cfloop>
      
   <cfset adjustedMessage = arrayToList(wordArr," ")/>
   
   <!--- Loop and count each letter. We can also return the number of spaces if desired --->
   <cfset signDetails.letterData = {}/>
   <cfloop array="#charArr#" index="i">
      <cfif i eq " " && !arguments.includeSpaces>
         <cfcontinue/>
      </cfif>
      <cfif !structkeyexists(signDetails.letterData,i)>
         <cfset signDetails.letterData[i] = 0/>
      </cfif>
      <cfset signDetails.letterData[i] += 1/>
   </cfloop>
   
   <cfset signDetails.rowData = {}/>
   
   <cfset signDetails.rowData["row1"] = ""/>
   <!--- Let's fill those rows --->   
   <cfloop list="#adjustedMessage#" index="i" delimiters=" ">      
      <cfif (wordlength + slotsFilled) gt arguments.width> <!--- No more room, next row please. --->
         <cfset currentRow++/>
         <cfset signDetails.rowData["row" & currentRow] = ""/>
         <cfset slotsFilled = 0/>
      </cfif>
      
      <cfset signDetails.rowData["row" & currentRow] &= i/>
      <cfset slotsFilled += wordLength/>
      <cfif slotsFilled lt arguments.width>
         <cfset signDetails.rowData["row" & currentRow] &= " "/> <!--- Add spaces between wods on the same row --->
      </cfif>
   </cfloop>
   
   <cfreturn signDetails/>
</cffunction>
Probably should have followed your initial recommendation:
http://pastebin.com/B2e60KxP
Probably a bit more verbose than some of the slickness others can provide, but here's my take.
Or I could actually add the url to PasteBin......d'oh!

http://pastebin.com/hfPVdGJ0
Thank you for derailing my morning with a puzzle :) http://pastebin.com/UVuqHPB3
@Ray: Just for you ... the scripted version http://pastebin.com/Jxc7xWWV
Here's my attempt, also assuming uppercase letters only:

http://pastebin.com/MmmdA2K3
Here is my swing at it..

http://pastebin.com/pE541YL5
@Dave Ferguson:

You lose 5000 points for using the local scope.
Todd, no worries. Dave killed me quite a few times in COD sessions. There is no chance in hell he will win this contest.
Oops, that was supposed to be a DM. ;)
@todd

I am used to loosing lots of points for no reason so at least this time there is a reason.

However, Don't be a hater it still works.
Amen Dave.
I really like Dave's solution and loaded it up. I wonder if it's missing something though (or perhaps it's just me). The results return 7 rows, but wouldn't it be 6?
@Robert

Showoff

:-)
I personally give Dave Fergusonofcourse +5000 points for explicitly using local. Heck, I've even gone so far as to create a local scope in all my JavaScript functions, and a Variables scope as well!
Does it have to be written in ColdFusion? I didn't see that in the question, but it seems all the solutions are in ColdFusion. Do you have a preference?
Yep, sorry Drew. May I ask - what language did you want to use?
JavaScript, standing up a ColdFusion is bit more time than I have to do this :p
Ah ok. I'll consider JavaScript ones in the future.
Here's my solution, complete with unabashed flattery: http://pastebin.com/A3uV7tLX
You can test it out at: http://edbartram.com/FridayPuzzler/JoesCarWash.cfm...
Well you peeked my interest so I did it JavaScript anyways, please DON'T consider for the drawing for ColdFusion builder though.
http://jsfiddle.net/sFXLq/3/

It comes with a test string so you can just click run. I think a little more undistracted time, I could get this running in a lot less LOC.
Oh no can't edit comment!
Wasn't returning anything, fixed http://jsfiddle.net/sFXLq/5/
One problem that most of the submission all share is that they were all done in cold fusion. I would think that for a business solution you would want to use something more robust like C# or Java? Right?? Like what would happen for example if Joe's Car Wash had to swap out his servers or hosting providers in 5 years if cold fusion gets canned, know what I mean?
@Topper

CF is Java. Your argument is invalid.

Go away.
Wow, Topper. That's not very accurate, and not really on topic either. If you would like to debate the merits of ColdFusion, we can do so on another entry. You mention "in 5 years" - you do know Adobe is already working on ColdFusion 10 _and_ has the plans for 11, right? Certainly they might change their mind. But even if they did, it wouldn't magically make your ColdFusion code stop working. Also, there are alternative providers like Railo out there.

So... in my opinion - this is a non issue.
wait, shoudl we be concerned about cf going away??? do we need to look at different hosting choices?
I know I'm concerned when I look at job trends and see cf flatlining at the bottom! Buy into Groovy now (or Ruby)!
hey sorry wasn't trying to ruffle feathers. Yea cold fusion code wont stop working when adobe cans the product this is true. All i was saying was that i figured a more robust language that is better accepted by businesses and more widely used in general would make for better solutions to your puzzle. the puzzle itself is a great idea and I liked the contest a lot, so cheers! but yea for sure a good idea to learn something other than cold fusion. Best of luck! :)
wow actually to cornfed I will say you are dead wrong about groovy and ruby. For longevity in your career, forget groovy and ruby and go with Java or C#. apparently from what Todd says above cold fusion code is the same as Java??? but native Java and C# without a doubt. forget the others.
Ugh. Guys - can we please get on topic? Obviously I'm biased towards CF. Obviously some people are not. That's perfectly ok. But this blog entry is just a simple, fun, coding contest in ColdFusion. If the language isn't your thing, that's cool, but can we save that for later?
do you mean back on topic about "cold fusion canned in 5 years" thing or about the results all being in cold fusion?
Topper, honestly, do I have to answer that? I'm trying to take your comments at face value, and assume you aren't trying to troll here, but I'm having a hard time.
Here is my late entry: http://pastebin.com/kWVv902e
Quick and dirty, probably needs a run thru the car wash..

http://pastebin.com/NvPjRYi9
you kow what, I already won - even if I don't. I realized today that in CF 9 (maybe earlier?) you can have a . or a ; or a , or a ? or a - or a ! as a structure key name. Whodathunkit?

M
Doing a quick review and here are some random comments:

1) Jeremy Battle - you used this.* to call methods in your CFC. Avoid that. It acts like an outside call in terms of security and will fail if the method is private. Instead of this.foo(), just do foo().

You also missed var scoping X in your loop iterator. Watch out for that. ;)

Also, you init your arrays like this: var tempArray = [""]; Which is interesting - but incorrect. This creates a new array with one item, an empty string. You probably (I assume?) wanted an empty array to start off with: var tempArray=[];

2) All - those who used tag based CFCs, don't forget your output=false.

3) AXL - I like how you didn't do it all in one UDF - but don't like the use of script. If your going to write it mostly in script, go 100%. But that's just my opinion. :)

4) Ed - this is awesome: <cfset structSign.unabashedFlattery = "Ray is awesome!">

5) Drew - thank you for using jsfiddle. I've been hearing about it but haven't seen an example. It's awesome and now I'm going to try it myself. :)

6) Connor - nice nick name on the pastebin. ;) Lack of var scoping though. Ditto MikeG on the cool pastebin nick.
Ok - so I picked a winner. As I warned folks - it was very arbitrary and purely for fun. I appreciate everyone who tried. I know it's scary to share your code publicly sometimes. I took all of your names and entered it into a hat (aka an array). I doubled Robert since he actually rewrote his tag based version into script. I repeated Ed cuz he built a form I could run online (others did forms for testing too, but Ed appealed to my laziness). I then did a random selection:


<cfset entries = ["Todd Sharp","Robert Gatti","Robert Gatti","Danny","Joe Bodell","Jeremy Battle","Michael Mongeau","Dave Ferguson sucks at COD","AXL","Ed Bartram","Ed Bartram","Connor Middleton"]>

<cfset picked = randRange(1,arrayLen(entries))>
<cfoutput>Winner(winning)=#entries[picked]#</cfoutput>

And the winner was...
While I appreciate your fairness in judging the contest. I would like to file a formal protest as to the nature of how I was included.
Do you deny you don't suck at COD? I mean that's my only explanation as to how you kick my butt. I think you're so bad you "wrap around" the skill scale back to positive. ;)

Ok -sorry folks - leaving off the winner WAS intentional and I assumed people would notice and yell but I guess everyone is out. ;)

The winner is... Connor Middleton.

Connor - send me an email and I'll get you instructions on how to get your software.
@ray good catch on the function scope, and the var scoping of x was just an oversight, however, I did want my array initialized with one element that was empty. :) thanks for the contest I missed them!
Foul I say - Just foul...Whereis MikeG in the Entries Array <grin> Dang, now I am gonna have to enter the next contest too!
Wow, thanks for the feedback Ray, and especially for running these Friday Puzzlers!
BTW, add in me in to youe COD battles, my gamertag is connor2k
Raymond this reminds me of my course in Logic and Discrete mathematics carried in univerdad, was a good times!!!
I'm too late for the contest but I wanted to post my code anyway. (Thanks, Ray--this was fun!)

http://pastebin.com/AJjxb1wq
Doh! Sorry MikeG!
S'ok - now I know who to ask if I ever need a code review that I have to pass..
Thanks for the fun Ray and I appreciate the second entry. Congrats Connor!

[Add Comment] [Subscribe to Comments]