Friday Puzzler: Family Tree Simulation

This post is more than 2 years old.

Ok, so it's been a while (too long) since I ran a Friday Puzzler. As a reminder, these are meant to be very small, very quick code puzzles that you can solve in (hopefully!) five minutes or so. You have to use ColdFusion. But outside of that, you are welcome to solve the puzzle as you see fit. These puzzlers tend to be hit and miss. Sometimes I get a lot of people responding, sometimes, not so much. But I had this idea while exercising this morning and I thought it would be fun to see what you guys do with it. Don't forget. This is for fun. That means you can totally disobey the "rules" and just go crazy. I'm going to try to dig up a prize perhaps (who knows...), but the point is to have fun. Remember - do not post your code in the comment. Post a PasteBin link and a description of what you did. Since the puzzle involves output, you can also link to your demo.

For today's puzzler, you are going to write code that simply simulates a family line. You will begin by creating a Person component. This component can be very simple. It doesn't even have to have a name. It does have to have a gender, a spouse pointer, and an array of children. Your family line will begin with two people.

Every generation your couple will have 0-N children.

Every generation those children will have a chance to marry and also have 0-N children.

Your simulation ends when no more children or born (or none get married), or when you reach some other practical limit. It is entirely possible your family line could grow big enough to impact server memory.

Your simulation must output something that displays the tree. It need not be graphical.

Some things to consider to make things simpler:

  • There is no gay marriage, divorce, remarriage because of spousal death, or adoption. I think my readers know me well enough to know I'm pretty liberal, and as I have three adopted kids myself I'm definitely pro-adoption. But for the sake of simplicity, let's just assume those don't exist. (And please - if folks want to debate those topics with me, let's do it off blog. As it stands, I'm right, you're wrong. We're done. :)
  • Bonus points if you assign random names to people and have male children carry on the family name. In theory, it's as simple as an array of firstnames and lastnames and just randomly selecting them. You could pick 20 each and have quite a variety
  • Super bonus points if you randomly decide that the child of a marriage will use a hyphenated named. But I have no idea what two kids with hyphenated names would do when they get married.

Since this was maybe a bit larger than I expected, I'm extending this contest till EOD Monday. That's 5PM Cool Standard Time. Oh - and if you want a free copy of ColdFusion Builder 2... just enter. :) (Actually, it's Flash Builder and CFBuilder 2!)
Family tree picture courtesy of this blog post.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Edward posted on 9/23/2011 at 5:47 PM

"I have no idea what two kids with hyphenated names would do when they get married. "

Easy ... git pull origin ...
git checkout -b kids --track origin/kids
git rebase master ...

Comment 2 by Raymond Camden posted on 9/23/2011 at 5:52 PM

Heh - ok - good response. ;)

Comment 3 by CD posted on 9/23/2011 at 9:48 PM

I'm intrigued, but I'll have to try it tonight or over the weekend.

Comment 4 by Raymond Camden posted on 9/23/2011 at 9:50 PM

I normally do an End of Day deadline, but for this one, let's say Monday.

Comment 5 by Joshua Miller posted on 9/23/2011 at 11:46 PM

My solution was pretty straightforward. I created simple methods for creating people, getting married and procreating and then dumped everything out in bulleted lists. This did indeed run long enough twice to kill my local CF server, but can also just return a couple of families - call it bad genetics. I'm sure there are 1000 solutions, and probably simpler ones than mine, but it was quick and fun!

http://pastebin.com/mn4MUFVq

Comment 6 by Raymond Camden posted on 9/23/2011 at 11:50 PM

Line 56 is epic:

// Procreation boiled down to script; maybe my wife is right ...

Comment 7 by Joshua Miller posted on 9/23/2011 at 11:50 PM

Here's my screenshot ... I couldn't think of a better way to post the image, sorry.

Comment 8 by Joshua Miller posted on 9/23/2011 at 11:51 PM

One more try on the image ... http://twitpic.com/6pesau

Comment 9 by Dan Wilson posted on 9/24/2011 at 1:42 AM

I made my version a little gamified. Since Family Trees and fortunes all depend on the economic conditions, I made my application able to vary the size of the family depending on some conditions. Depending on a bit of chance and a bit of fortune, a family member (if attractive enough) will get married and possibly (if fertile enough) have children.

When you run my application, choose some conditions and it'll spit out what happened based on the genetics and economic conditions.

Code: http://pastebin.com/Tr65NbT5
Live Application: http://nodans.com/familytre...

Lemme know if you need anything else,

Dan Wilson

I made an OO version.

Comment 10 by Raymond Camden posted on 9/24/2011 at 1:57 AM

That's fascinating. Have you considered making the simulation randomly change the economic situation?

Comment 11 by Raymond Camden posted on 9/24/2011 at 2:00 AM

Did you prefix your struct keys with numbers just to force a proper sort order?

Comment 12 by Dan Wilson posted on 9/24/2011 at 2:10 AM

I prefixed the structure keys with numbers purely for sorting.

Otherwise, some stuff was under the kids and it looked wack.

DW

Comment 13 by Tyson posted on 9/24/2011 at 7:59 AM

Here's my solution...
http://pastebin.com/L4tiWBzM

For each descendant, there is a chance they will get married. If they get married, they try for kids. Occasionally, the mother decides that she wants the kids to have hyphenated names. Once the family tree is generated, I just output them as lists.

I do confess that this took a little longer than 5 minutes :)

Comment 14 by Mike Hodgson posted on 9/25/2011 at 7:55 PM

Here's my attempt:
https://github.com/mikehodg...

The code could be cleaned up a bit, but it works alright. I don't track which names are used, so sometimes you will have families where two children have the same name. It puts a limit of 5 children per family and the chance of getting married is 50/50.

Comment 15 by Zach Stevenson posted on 9/27/2011 at 4:15 AM

Probably not as good as everyone elses, but I tried. Alose, i'm very bummed the syntax highlighting on pastebin doesn't recognize cfscript

http://pastebin.com/WXTvX98A

Comment 16 by Raymond Camden posted on 9/27/2011 at 2:58 PM

Ok - anyone who posts AFTER this comment, is not going to be considered for the prize. But I highly encourage folks to share anyway. (In fact, I may actually code this myself as well.) Going to do my review either today or tomorrow and post the winner here.

Comment 17 by Raymond Camden posted on 9/28/2011 at 5:25 PM

I'm going to do some reviews via comments. This is meant to be off the cuff, blunt, etc. When I point out mistakes, know that I make them too, and that I'm certainly far from always correct, so push back, disagree, etc.

Working on Joshua Miller's first.

1) Would be cool if your logic to display "The Foos" handled last names that ended with S.

2) Nice names. :)

3) nit picky - listToArray, the comma is implied. I'd just have written: function gender() { return ["m","f"]; }

4) function person(i,g,f,t)
I normally prefer to use argument names that are a bit more descriptive. It's more typing, but makes it easier to understand the logic.

5) Interesting syntax here:
p.gender = gender()[randrange(1,2)];

I don't think I've seen that in CF before.

6) Love some of your comments.

That's it - good entry with some interesting syntax choices.

Comment 18 by Raymond Camden posted on 9/28/2011 at 5:34 PM

Dan's entry:

1) -10000 points for a tag based CFC. ;)
2) +10000 points for the use of economic conditions. Lookin gat your logic now and I love how you did it.
3) Bit confused by the code that makes a duplicate of getConfig(). You don't seem to use it - like in definePatriachNode.
4) Any reason why person.cfc just didn't use automatic accessors?

Comment 19 by Dan Wilson posted on 9/28/2011 at 5:43 PM

I quickly wrote my example on a CF8 machine so no script based CFCs nor Automatic Accessors were available.

The duplicate() call is reflexive behavior for me, since structs are passed by reference and changing struct contents would ripple through the application (possibly causing side effects). Looking at my code, it looks like I never actually cause a change to the config struct so it's not needed.

The economic conditions thing was fun to do. I'm a bit into gamification these days and it's fun to goof around and gamify things a bit. One day I'll show you what we are doing with ChallengeWave...

Thanks for the puzzler Ray. That was the most fun I've had programming in a while.

DW

Comment 20 by Raymond Camden posted on 9/28/2011 at 5:44 PM

Tyson's entry:
1) Doesn't your init function need to return this?

2) Nit picky - I'd write isMarried as one line: return isObject(variables.spouse);

3) addAnotherMouthToFeed - I think that the functions you guys are using have had some epic names .Thanks. :) Heh - "Spouse Factory"

4) Would have given you a bit more points (am I counting points, not really ;) if you had made boyOrGirl accurate. I believe it's actually 55% for Girls.

Comment 21 by Raymond Camden posted on 9/28/2011 at 5:47 PM

Mike's entry:

1) Your note on duplicate child name - I bet many people forgot that. I know I did when I was thinking about the code in my head.

2) Slick bit of logic here:

arguments.gender == 'M' ? setFirst(firstnames['M'][randRange(1, arrayLen(firstnames['M']))]) : setFirst(firstnames['F'][randRange(1, arrayLen(firstnames['F']))]);

I'm not a fan of these operators as I find them a bit hard to read... but I dig that.

3) if (!isDefined("variables.children"))

I've seen this a few times now - I mean isDefined versus structKeyExists. Not a complaint - just noting it.

4) Bonus points for using Github. :)

Comment 22 by Raymond Camden posted on 9/28/2011 at 5:58 PM

Zach's entry:

1) Kinda important mistake you should make note of - in App.cfc, you should use this.name="...", not Application.name. That sets an Application scoped variable called name. It does not "name" the application.

2) Hmmm - interesting use of onRequest there. I normally do what you did in onRequestStart. But I kinda like how you used onRequest.

3) Hmm - I ran your code, but it only generates one generation. Was that intentional? Did you leave out something?

Comment 23 by Tyson posted on 9/28/2011 at 6:13 PM

Before you pointed out the init function not returning this I would have agreed with you, but since the code runs I guess it doesn't. :) When using createObject(...).init() you have to return this, but maybe by using new Person() you don't need to anymore?

Ha, I should have thought to look up the boy/girl ratio.

Thanks for puzzler & for reviewing all the code. It's fun to see all the different takes on the same problem.

Comment 24 by Joshua Miller posted on 9/28/2011 at 6:40 PM

I appreciate the feedback. Those listToArray() function calls are a product of the "abbreviations" I have built into my editor (jEdit) - when I type "listtoarray" and press "space" then the full syntax for the function replaces my text. That's something I might change since 99 times out of 100 I use commas for lists.

As for the argument names you're right, I used shorthand because I know what they mean (identity, gender, family name and type) and I don't live verbose code, but they're non-intuitive for others.

Total oversight on the "s" ending on the names - that could have been handled so simply with an IIF().

If I were to refactor this I would switch to using implicit struct/array creation rather than longhand StructNew() and ArrayNew() - again, I need to update my abbreviations in jEdit or maybe take a look at CFBuilder.

It was a quick solution and I really enjoyed seeing everyone else's take on solving the puzzle. Thanks for putting this together - looking forward to future puzzlers!

Comment 25 by Zach Stevenson posted on 9/28/2011 at 7:25 PM

1) Yeah, that was an oversight, i ran through making my Application.cfc really quickly.
3) It only dumps out the first person created, but you should be able to go down through his spouse and children.

I think the weirdest thing I did was fake static functionality. It drives me bonkers that CF doesn't have any good way to make static properties.

Comment 26 by AXL posted on 9/28/2011 at 10:54 PM

I know it's a bit late, but here is mine.
http://pastebin.com/uFVS5ffs
http://www.mymatisse.com/pr...

Any comments will be appreciated.

Comment 27 by Raymond Camden posted on 9/29/2011 at 7:45 PM

AXL:

1) I don't believe you need to set variables.*="" in your CFC. I think just the act of enabling accessors is enough. Now, you may get a null if you do getX() w/o running setX() first, so that (setting defaults) may be a good reason to do it.

BTW - your init function... next week I'm going to reveal something that will make that a LOT smaller in Zeus. ;)

2) Interesting use of a big array to handle randomness. Any reason why you just didn't use a randRange type check? Err what I mean is, something like randRange(1,10) < 2 or some such.

3) Nice job on the display page. Thanks.

Comment 28 by AXL posted on 9/30/2011 at 9:08 AM

Thank you for the comments, Ray.

1) I guess I tend to do variables.*="" things because of the book "Object-Oriented Programming in ColdFusion" I have read. They are not necessary just like you said. If I want to set default values, I could do that within 'init' functions by calling setters and getters. It's just another way of setting default values to properties.

Interesting! I won't be at MAX, but Ben mentioned a live online thing at his blog.
http://forta.com/blog/index...

If yours aren't available online in some reasons next week, could you post some details in here? I know you would do, but just making sure. That would be awesome.

2) I like yours. I just didn't think about that. That's a clever way comparing with mine. ^^;

3) Thank you very much for spending time on reviewing my code. It was fun and I really appreciate your feedback. Learning something every day.

Comment 29 by Raymond Camden posted on 9/30/2011 at 6:16 PM

My preso won't be online immediately, but it will be soon I think. As for details - I'm working on a plan for that now. Stay tuned.

Btw... thank you all for not complaining about how long it took for me to pick a winner. Personally I like all the entries. These contests were always decided in a very arbitrary manner (it IS meant to be fun - I run, from time to time, larger more series contests where the winner is definitely decided more seriously) but I have as of yet decided who that winner will be. By lunch - I'll decide, even if I have to randRange to pick. :)

Comment 30 by Joshua Miller posted on 9/30/2011 at 6:30 PM

In case you need some help deciding on a winner, I wrote a function for that:

function getWinner(prize){
var entries = ["Josh","Dan","Tyson","Mike","Zach","Axl"];
return IIF(arguments.prize == "awesome",DE(entries[1]),DE(entries[RandRange(1,ArrayLen(entries))]));
}

writeoutput(getWinner("awesome"));

Comment 31 by Raymond Camden posted on 9/30/2011 at 6:31 PM

Hahah, ok, so for having the cajones to do that, you're my winner.

Any objections from the rest of the crowd? If not - Joshua - ping me via email for your serial #s.

Comment 32 by Emilio Rubio posted on 10/1/2011 at 1:02 PM

Hi, there:
I now that this Puzzle is just for fun but, if you want to write real life in this kind of Trees, what about if either father and/or mother has brother/s and or sister/s of several father/s and/or mothers? This stands for several marriadge.
Please, forget it if already done. :)
Best regards.

Comment 33 by AXL posted on 10/1/2011 at 3:21 PM

You know What I think you should do. Give all of them who have submitted their code a free copy except me since I didn't submit it on time. At least these five people spent a quit time on this puzzle to entertain, and come on in a few days it's MAX. Can Adobe help you to have a little preopening party here? Where did Oprah go?

Comment 34 by Raymond Camden posted on 10/1/2011 at 3:26 PM

+Emilo: Yeah, that would definitely be more realistic. But also quite a bit more complex. :)

+AXL: Heh, I'm no Oprah. ;)