Thursday, October 19, 2017

The Sojourn of Daniel Gill

The night was cold, as cold as it had been all year. Maybe as cold as it had ever been. The wind didn’t exactly howl, but moaned forcefully enough to cause the trees to sway and creak mournfully in return. Not a single leaf was left on any branch of those trees by this time of the year, not that you could have seen them tonight. The sky was clear but black as pitch, the stars oddly missing from what should have been a canopy of brilliant constellations. The moon had either already risen and set, or wasn’t bound to be shining at all tonight. No, not this night.

Daniel Gill walked on through this night, unmoved by the cold, the wind, or the darkness. He had somewhere to go, somewhere to be. He thought he knew the way, or rather, his feet kept taking him in a deliberate direction, through the remains of a barren forest and into the foothills above a valley. He knew that in the center of the valley a town lay, but no friendly lights twinkled in the windows of the cottages there or in the paths between them. No lights at all, from hearth, lantern, or candle. Not this night.

As he crested the first of the foothills, an unexpected spark caught his eye. For a moment, he thought that a match had been struck directly before his face, so bright was the flame. Another moment later, he realized that just down the path, nestled in the leeward side of a hillock, was a warm campfire. He thought he could make out the shapes of two people huddled next to it, their hands extended toward its warmth. His feet continued to carry him forward, and only slowed when they touched the edge of the circle of light cast by the fire.

The two people turned at his approach. They were both wearing thick coats with hoods in an attempt to ward off the chill, but Daniel could see that it was an older man and woman in front of the fire. They sat close to each other, leaning towards each other was much as the fire. They were obviously in the habit of sitting this way, although they looked slightly uncomfortable squatting on the makeshift bench they’d assembled of logs balanced on small piles of rocks. The woman offered him a tentative smile and beckoned him to join them at the fire.

Daniel moved to sit opposite them on a narrow but relatively flat stone. It looked like it had been sat upon before. He wondered briefly why the couple hadn’t chosen to sit there instead of on their bench.

“Good evening,” Daniel began, surprising himself with the sound of his voice. It was low and grating, as if he hadn’t used it in a very long time. He cleared his throat and tried again. “Thank you for allowing me to share your fire. My name’s Daniel.”

“Angela,” said the woman, her voice huskier than Daniel had expected. Her face was well lined, both from care and from joy, Daniel could tell. “My husband, Oliver,” said the woman, glancing up at the man. The old man waved at Daniel, and Daniel returned the gesture. The man didn’t speak, but Daniel noticed a strange twinkling in the man’s eyes as the firelight danced and played across them in their recessed alcoves beneath the man’s light gray and bushy eyebrows.

“Seems like a bad night to be out and about,” Daniel offered, although he seemed indifferent to the night himself.

Angela nodded. “Yes, a bad night… to…” she trailed off, seemingly distracted by a thought. She turned her head to Oliver, who had been staring at Daniel the entire time. Oliver finally noticed that Angela had stopped talking, inhaled as if to speak, then cleared his throat before actually saying anything.

“We… uh…” he began, cleared his throat again, then carried on. “There is always at least one traveler along this path on nights like tonight. We like to be of service to them, to offer them help in any way we can.”

“I see,” said Daniel. “Have you met any other travelers this evening?”

“Not yet,” Oliver replied. Angela looked as if she were digging through a knapsack that had been at the couple’s feet. She straightened up a moment later with something in her hand.

“Here,” she said, offering the item to Daniel. It was the stub of a well-used but lovingly made candle.

“A candle?” he asked, looking confused. “The night’s darkness doesn’t bother me. I know the path well. I thank you, but have no need of the candle.”

Angela pressed it into Daniel’s hand. “You might come to a spot in the path where the light will come in handy. Please, take it.” She seemed so insistent that Daniel accepted the gift despite his conviction that it would be unnecessary. He rose from his seat and put the candle in his satchel. He noticed his satchel was similar to the one from which Angela had pulled the candle. These two people seemed somehow familiar to him.

“Excuse me,” Daniel asked as gently as he could, as the old couple seemed to be startled by his sudden movement. “Have we met somewhere before?”

“Maybe we have… in another lifetime, perhaps,” was Oliver’s reply. Angela had leaned herself fully into Oliver, burying her face in his shoulder as he wrapped his arms around her.

“I should be going now,” said Daniel, suddenly eager to be on his way. “Thank you again for the respite. And for the candle. If we meet again, and I can be of assistance to you, you have but to ask for it.”

“Thank you for taking the time to stop by our fire,” said Oliver, rocking Angela slowly back and forth. “Safe travels, Daniel.”

“Safe travels,” Daniel replied, turning to walk out of the firelight and further up the path. A moment later, he was beyond the fire’s circle of light and into the night’s inky blackness again. Just before the path rounded a bend, he turned back toward where Oliver and Angela were. To his surprise, the path behind him was just as dark as the path ahead. No trace of the friendly campfire remained.

***

Daniel Gill walked on. How far or how long, he could not know. The landscape changed slightly, sometimes climbing, sometimes descending, weaving left or right, the path undulating like an unending and slow-moving serpent beneath his feet. He couldn’t have gone on too long, as he wasn’t tiring, when he saw what appeared to be another campfire. This one was off of the path, perhaps fifty feet up a slight slope to the west of where he was. Daniel considered walking on, but felt an unexpected inclination to approach it when he heard a loud, raucous laugh come from the area, joined a moment later by another, separate hearty laugh.

Daniel approached the campsite, for a campsite it was. He could make out two figures reclining on the far side of a smallish campfire. Behind them were two single-person tents made of rough, nondescript canvas. The voices of the two figures carried a murmured conversation to Daniel’s ears, but he couldn’t make out specific words. He got the sense that the two men (for they were both men) were reminiscing about something. As before, Daniel walked up to the edge of the circle of light provided by the campfire. Noticing Daniel there, the two men abruptly ended their conversation and sat up straight. They favored each other, although one’s hair was more blonde than brown, with the other’s more brown than blonde. Their faces were slender, but not hawkish. The brunette-ish man, on the left, had a jaw that was slightly more square then the other. The blonde-ish man had a cigarette firmly between his lips, and started choking on its smoke as he took an involuntary gasp in with Daniel’s approach. He sputtered a bit, but quickly got his breathing under control while motioning to Daniel to join them. Daniel did so, taking a seat on the ground opposite them.

Remembering his faulty greeting from earlier, Daniel began by clearing his throat, then said “Hello! Thank you for letting me share your fire. My name is Daniel.”

Both men smiled at this, for some reason. The one that had been coughing earlier raised his hand in greeting. “I’m Robert, but you can call me Bob.”

The other similarly gestured, saying “Michael — Mike.”

Daniel raised his hand to them again. Bob said “Say, do you mind if I call you Danny?”

“No, not at all, Bob,” replied Daniel, warmed by the familiarity. And something about the setting, the pitch of the tents, the way the men sat, reclining next to the campfire… Had Daniel camped in this spot at sometime in the past?

Mike broke Daniel’s short contemplation. “Well, Danny. How are you this fine October evening?”

“Fine, except that I’ve got somewhere I need to be.”

“Don’t we all?” was Mike’s reply as he poked the fire with a stick, stirring it to life where it was flagging.

“What about you two?” ask Daniel.

Mike turned to Bob. Bob glanced around himself, taking in the fire, the tents, and the site in general. “We’re camping.”

“Yes,” Daniel said, a smirk curling one side of his mouth up, “it just seems an odd time of year and spot to randomly camp in.”

Bob leaned back against the thick log behind him, crossed his arms under his head, and stared up at the sky. “Odd, yes. Did you notice there aren’t any stars out tonight?” Daniel nodded. “Mike and I camp here around this time pretty much every year for the past few. It’s kind of a ‘last hurrah’ for us. The season’ll turn much worse by a week from now, and we’ll be cooped up at home for the better part of the winter. We used to camp up here with some other folks until…” Bob trailed off, still gazing at the starless heavens.

Daniel waited for Bob to continue, but seeing that Bob had fallen silent, asked “Are you two from the village?”

Both of them looked at Daniel in what might’ve been termed a peculiar way. They both nodded.

“Do either of you know an older couple named Oliver and Angela?”

Daniel caught both of the mens’ eyes widening for a moment. “Sounds familiar,” said Mike after another moment’s hesitation. “Why do you ask?”

“I ran into them a few miles back. Said they always offer help to travelers along this path on nights like tonight. Odd couple, if you ask me.”

Both men smiled at this. “Yes, their whole family is a bit odd,” offered Bob. “Did they help you at all?”

“I’m not sure,” Daniel said, fishing the candle out of his satchel. “They gave me this bit of candle, but I’m not sure what I’ll do with it. I don’t even have any matches.”

Bob gave Daniel a hard look, one that took him aback for a moment. He reached into his vest pocket and pulled out a pack of cigarettes. Tapping one out, he replaced the pack with care, then slowly and deliberately pulled a small box of matches from another of his pockets, took a match from the box, and carefully struck it, lighting his cigarette with it. He tossed the used match into the campfire, then, after taking a long drag off the cigarette, tossed the box of matches to Daniel. Daniel caught them with a dexterity that surprised him a little.

“Take those, Danny. You can use them to light that candle if need be.” He paused, apparently considering what to say next. Finally, he closed his mouth and smiled a half smile that looked very similar to Daniel’s smirk. “I wish I could do more for you buddy.”  He took to contemplating the sky again as Mike stared into the fire, a far away look on his face, as if he were caught in a memory that wouldn’t let him come back to the present.

Daniel once again felt the compulsion to move on. He stood, putting both candle and matches into his satchel. “I should be moving on. I’ve still got a way to go.”

“I know you do,” said Bob. “Take care, Danny. I hope you get there.”

“…Thanks…” replied Daniel, not quite sure how to take that salutation. “Take care, you too.”

“We always do, Danny,” said Mike, finally stirring from his reverie. “We always do.” His voice seemed to catch a little as he said this last statement. Daniel turned and walked out of the circle of firelight, back to the main path. And as before, just before he got to the next bend in the road, he glanced back at the incline. He could just make out where the camp should have been, but no fire was burning, not even a hint of smoke was detectable on the steadily rising wind. And also as before, Daniel walked on.

***

The night grew darker, or it seemed to. Perhaps it was just that Daniel’s feet were less sure of the path as he continued. He could tell that he was heading down towards the valley, but wasn’t at all sure where he was anymore. Eventually, he got to a point in the path where it split, and he came to a stop. His feet refused to choose a direction, so he tried to decide for himself.

Peering into the darkness, he quickly realized that he couldn’t tell one branch from another. It was only at that moment that he recalled the candle and matches. He took them both out, fumbling both a bit, but managed to get a match struck. He held it to the wick, but before it could catch, a gust of bitter cold wind blew the match out. Daniel turned his back to the wind, struck another match, and repeated the operation. The candle caught this time, but as he held it before him to investigate the path, another gust, far more insistent than the first, blew the candle out. Daniel opened the match box yet again, and as he did, a single drop of candle wax fell on his fingers. It burned with a flaring pain, far more intense than candle wax had any right to be. Daniel cried out in surprise, his voice joining with the rising wail of the suddenly blustering wind. The match box fell from his hands.

He quickly knelt in place, hands searching along the ground at his feet. After a moment he found the box. All but one of the matches had fallen out. His hands sought vainly for the other matches. He realized that they must have blown away as the match box fell. He carefully closed the match box and put it and the candle back in his satchel. Straightening up, he decided to take the path’s rightward branch. He was about to step forward when he caught a faint glimmer of light from over his left shoulder, somewhere down the left branch. It was the dimmest light he could imagine, and yet looked more inviting than either of the other fires he’d seen this evening. It was hardly more than a flicker, probably a candle similar to the one he’d just put away, but it was steady. Without thinking, he started down the branch on the left. Before long, he was at the entrance to a cottage. A single candle burned in an unshuttered window next to the door.

He was about to knock on the door when it opened. A young woman stood behind the door. She was wearing what looked like a warm nightgown, faded, but embroidered with a delicate set of wildflowers that stretched all the way around the collar, spilling onto the shoulders, chest, and back. Her hair, long but plaited, was a silvery platinum that enhanced her youth rather than make her seem aged. She had large eyes, although he couldn’t tell their color in the dim light, other than they were dark, a striking offset to her light hair. Her entire countenance impacted Daniel, causing him to feel an immediate and unexpected affection for the woman. It was all he could do to keep himself from taking her hand and kissing it. The hand he found himself reaching for was otherwise occupied with holding the much smaller hand of a little girl of perhaps four years. The girl was dressed similarly to the woman, presumably her mother, and was rubbing her eyes with the back of her hand. She held a small stuffed animal, either a horse or a dog, in the same hand, and it seemed to dance across the little girl’s face, prancing forward and backward as she rubbed. Daniel wondered briefly if the girl had been asleep, but woken by her mother in order to answer the door. Having finished rubbing them, the girl’s eyes locked onto Daniel’s. They were an icy sapphire blue, and as large as dreams. And as with the woman, he felt an indescribable and completely incomprehensible affection for the girl.

“Excuse me,” began Daniel, his voice the clearest it had been all night. “I’m very sorry to disturb you, but noticed your candle. I’m… I’m afraid that I’m lost.”

The woman smiled at him, and it was perhaps the saddest smile Daniel could have imagined. “You aren’t lost, Daniel. You’re here.”

“Well, I’m not exactly sure where ‘here’ is,” said Daniel, “but I think I still have somewhere else to go.”

The woman’s smile grew larger, still sad, but tinged with contentment. Her eyes sparkled in the dim candlelight. “Yes, you do. How can we help you on your way?”

“I can’t see the path. I have a candle and a single match, but the wind keeps playing havoc with them.”

“Ah, you need a lantern. Here,” she said, letting go of the little girl’s hand long enough to take the candle from the window. Daniel could now see that the candle was actually housed in an old, well-worn watchman’s lantern.

“Here Ella, hold this,” said the woman to the child, handing her the lantern. This child grasped it obediently by the handle, but her eyes never left Daniel’s face. Daniel reluctantly tore his gaze from the girl’s, long enough to realize what the woman was doing.

“No, you can’t do that. What will you do for light yourself?” he protested as the woman withdrew her candle from the lantern.

“Don’t worry about us,” the woman said, shielding the candle with her hand. “We’ll be fine. This candle will last us the rest of the night, and tomorrow, we’ll get another lantern. Ella, will you give him the lantern please?”

The girl turned her head toward her mother, a questioning look in her intense eyes. Her mother smiled at her and nodded. Ella grasped the handle with both hands and extended them toward Daniel.

Daniel accepted the lantern gravely and gratefully. As he took it from her, the girl’s stuffed animal accidentally fell to the ground. Daniel bent down to give it back to her. As he touched it, a flood of memories flew through his mind…

Of a sunny day spent in a field playing with two boys, brothers, one blonde-ish, the other brunette-ish, and a toy just like this one.

Of being ill in bed, holding the toy tightly as a woman with a husky voice and well-lined face tended to him, with a man with bushy eyebrows looking in from his bedroom door.

Of handing the toy to a young woman with platinum hair for mending, as the woman finished embroidering an infant-sized nightshirt with wildflowers.

Of placing the toy in the crib of a baby girl with sapphire blue eyes, as large as dreams.

The memories ceased as quickly as they’d begun as Ella took the toy from Daniel’s hand. Daniel straightened up slowly, not understanding everything he’d just seen. Ella continued to stare at him. His gazed drifted between Ella and the woman.

“Sonja. Your name is Sonja,” he finally said to the woman.

She nodded, her voice trembling a little. “And you’re Daniel. And you have somewhere to be.” Daniel noticed tears spilling over from the edges of her eyes. Her smile was resigned yet lovely.

“I still don’t know the way.”

“Put the candle in the lantern and use the match to light it. The lantern and candle will only cast light in the direction you should go.”

Daniel took the candle and match box from his satchel once more. He placed the candle in the lantern, carefully shielded and struck the match, then touched it to the candle’s wick. It caught immediately, causing the lantern to glow warmly, but unexpectedly dimly. Daniel turned in a slow circle, holding the lantern in front of him. As he turned to a point to his right, he noticed that the lantern illuminated the edges of a path that Daniel hadn’t seen before, one that led around the cottage.

“Now you know the way,” the woman said, retaking the girl’s hand and retreating a little further into the cottage. “Goodbye, Daniel.”

“Wait…” Daniel began, but, unsure of what else to say, finally asked, “will I see you again?”

“Yes,” she replied. “Someday.”

With that, she closed the door. Daniel began walking along the path slowly, glancing back at the cottage window until he saw the candle there snuffed out.

***

Daniel walked on through the night, more forlornly yet resolutely than before. The lantern never flickered, but the light seemed to bend at times to show him turns in the path as they emerged. The path was small but well-worn, as if it were only used for walking. It took him through open fields and up a hillock to a gated enclosure. The gate opened at his touch. Entering, he realized that he could see nothing outside of the light cast by the lantern. To the left and right of the path was absolute blackness. He steeled himself, locking his eyes on the visible path, continuing forward step by step despite the eeriness of the journey.

The next thing he was aware of, he could see another light coming from ahead of him. This light definitely was not a firelight. It was too white, although not blindingly so. A few steps further and he could see that the light was stranger than anything else he’d experienced during his journey. The light seemed to emanate from the ground itself, standing to a height just taller than he was, and slightly wider as well. It wasn’t pure white as he previously thought; rather, it was slightly yellow tinged, as if someone had mixed sunlight and moonlight together.

He had a sense that this was yet another gate of some kind, and that he should go through, and yet he hesitated. Despite not knowing anything about it, he felt like this gate would only allow passage in one direction, that once he crossed through it, he would not be able to return. He turned to look behind him, holding the lantern aloft. It cast no light in the direction he faced, and the glowing door behind him cast no light beyond either. As it was along the path, now he was completely surrounded by impenetrable darkness. If he were to go back, he would have to go through that darkness to get anywhere. And instinctively he knew that he wasn’t supposed to go back.

Finally, he turned to face the gateway. He took a deep breath, closed his eyes, and…

***

The next morning, All Saint’s Day, found three sets of people walking along a path bearing no footprints from last night’s sojourner. One set, an older couple, had made their start earlier than the others, having to walk up from the village into the foothills. On their way, they came across two younger men, brothers by their appearance, striking their camp. The brothers joined the older couple wordlessly. Finally, the four of them walked to a cottage whose door opened as they approached. Out walked a young woman with platinum hair holding the hand of a young girl with sapphire blue eyes. The six of them made the short walk up the hill together, the woman and girl in the lead, and stopped at the closed gate to the cemetery there.

“What do you think?” asked Mike to the group from where he stood behind the woman and girl.

“I don’t know, son,” came the husky-voiced reply from behind him where Angela and Oliver were bring up the rear. “He seemed… clearer… last night than he has the previous two times. He took the candle with hardly any objection, then moved straight on.”

“And the matches. He realized almost immediately that he needed them,” added Bob, who was staring at the platinum-haired woman expectantly.

“He remembered my name,” Sonja said quietly as she opened the gate, walking inside.

Ella took the lead at this point, holding the stuffed toy to her chest the way her father had when he was her age. She walked wide-eyed and sure-footed to the back of the cemetery where her father’s grave was. Arriving, she recited the inscription on the headstone there.

“Here lies Daniel Gill. Beloved son, brother, husband, and father.”

She paused a moment, then exclaimed, “Look mommy! Look. The lantern! And there’s still a little bit of the candle inside!”

Sonja bent close to examine the lantern with Ella. Sure enough, there was still a nub of the candle left, and she spied the match box on the ground near the lantern as well.

“What does it mean?” asked Oliver, his bushy eyebrows knitting together to form a straight, wildly fuzzy line on his forehead.

Sonja straightened up, taking the lantern in her hand. Tears filled her eyes and spilled over, from relief tinged with the grief of loss.

“It means he blew it out. He didn’t need it anymore. He’s finally gone to rest.”

The group looked at each other, feeling the same mixture of relief and grief that Sonja had already expressed, realizing that their yearly Halloween vigil had finally come to an end.

THE END

Author's note: there is an ancient Celtic custom of lighting bonfires or candles to guide the spirits of departed loved ones back to their earthly homes on Samhain (the festival we currently celebrate as Halloween). This is a twist on that legend. Hope you enjoyed it!

Tuesday, September 5, 2017

Your Random May Vary: Addendum

My coworker and good friend Tim has provided a bunch of good information in response to my last post. I'm posting it here (mostly) unedited, because it sheds light on the underlying principles.

"sir, you have posted something false to the internet and it is my duty to berate you for it!

combining multiple random numbers is not the same thing as choosing a random number between bigger values.

it flys in the face of one of the most beautiful underlying principles of statistics (possibly the only beautiful thing about statistics):

If you add together many random numbers, you get a NORMAL DISTRIBUTION!
Isn't that interesting... I wonder why the normal distribution pops up so often? Oh that's right because this sort of thing happens all the time. Think about it for a bit; if you flip a coin 6 times, what is the probability of getting all heads? Is it the same as rolling a 6? Nope... you have a might higher probability of getting 3 heads than getting 6 heads. You might not notice it much for six goes, but if you flip a coint 18 times, you are very unlikely to get 18 in a row. It's because the distribution of outcomes is very different, not because one is more or less random than the other.

d&d is built on this principle; creating pseudo normalized random distributions by combining dice rolls.

If you want a better explination of this (and the 3 principles of statistics) I highly recommend reading the short chapter on statistic from "The Art of Game Design: A Book of Lenses" -- Jesse Schell. You can get it from the local library for free and the chapter is really concise and illuminating. The whole book is excellent actually."

Many thanks to Tim for the info!

Monday, September 4, 2017

Your Random May Vary


At the beginning of the summer, we started hosting a semi-monthly Dungeons and Dragons game at our house. It's typically a lot of fun, involving much role-playing and dice rolling. We share Dungeon-Mastering duties; I think I've run most of the sessions, but at least two or three have been run by someone else. When I'm running the game, my character is typically an NPC. It's a bit of a shame, because my stats are pretty awesome.

I got to thinking this morning... I rolled my current character (as all my characters), using real dice and a "roll-4-keep-3" approach. I roll four 6-sided dice, discarding the lowest, and summing the other three. I do that six times in a row, and I've got the stats block for a new character.

I never use anything but real dice unless I happen not to have a set (and fyi: I carry a couple of sets around in my backpack just in case a spontaneous or random one-shot happens). Garrett used to roll great stats very often using his IPhone dice, and tended to roll challenges really highly as well. I started thinking about the difference in our results and decided to mess around with a rolling app this morning.

It's been a while since I posted anything technical. Bear with me.

I started by creating a little app that would roll a six-sided dice for me.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(ns stats-generator.main
  (:gen-class))

(def d6 [1 2 3 4 5 6])

(defn roll-d6 []
  (rand-nth d6))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

I quickly realized that I didn't quite know where to go. TDD to the rescue!

1
2
3
4
5
6
(ns stats-generator.main-test
  (:require [clojure.test :refer :all]
            [stats-generator.main :as main]))

(deftest test-roll-4-keep-3
  (testing "Given a roll of 4 6-sided dice, sums the highest 3"))

I started with a description of one of the end goals: a function that would roll four dice and keep the highest three. That... is probably too broad. Let's decompose for a moment. We know we'll need to roll four 6-sided dice and drop the lowest roll. Maybe that dropping function is easier to test and implement.

1
2
3
4
5
(deftest test-drop-lowest
  (testing "Given all different values, drops the lowest"
    (let [v [1 2 3 4]
          expected [2 3 4]]
      (is (= expected (main/drop-lowest v))))))

There's the test; we should stub a red (failing) function.

1
2
3
4
(defn drop-lowest
  "Given a vector, returns a vector sans its lowest value."
  [v]
  v)

Done! Run the tests!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(run-tests)

Testing stats-generator.main-test

FAIL in (test-drop-lowest) (main_test.clj:9)
Given all different values, drops the lowest
expected: [2 3 4]
  actual: [1 2 3 4]
    diff: - [2 3 4]
          + [1 2 3 4]

Ran 2 tests containing 1 assertions.
1 failures, 0 errors.
=> {:test 2, :pass 0, :fail 1, :error 0, :type :summary}

Failed as expected. What's a simple solution that will work?

1
2
3
4
(defn drop-lowest
  "Given a vector, returns a vector sans its lowest value."
  [v]
  (rest (sort v)))

Seems reasonable. Tests tell us...?

1
2
3
4
5
6
7
(run-tests)

Testing stats-generator.main-test

Ran 2 tests containing 1 assertions.
0 failures, 0 errors.
=> {:test 2, :pass 1, :fail 0, :error 0, :type :summary}

Yay! Green tests! Let's fill in some other assertions to make sure we're not fooling ourselves.

1
2
3
4
5
6
7
8
9
(deftest test-drop-lowest
  (testing "Given all different values in random order, drops the lowest"
    (is (= [2 3 4] (main/drop-lowest [1 2 3 4])))
    (is (= [2 3 4] (main/drop-lowest [4 2 3 1]))))
  (testing "Given identical values, drops one of them"
    (is (= [1 1 1] (main/drop-lowest [1 1 1 1]))))
  (testing "Given some duplicated values, drops whatever's lowest"
    (is (= [2 2 3] (main/drop-lowest [2 3 2 2])))
    (is (= [5 6 6] (main/drop-lowest [5 6 4 6])))))

That's good enough. A question I'm often asked is "how much automated test coverage should I implement?" The pedantic answer is "100%," but the pragmatic answer is "as much as you can that covers stuff you would do by hand anyway." That's my rule-of-thumb, in any case. Sometimes I do more, sometimes less. But if I find bugs, I try to reproduce them with tests and then they're covered into perpetuity. As such the test coverage grows organically.

Alright, let's fill in some more of the decomposed functions and see where we wind up.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(ns stats-generator.main
  (:gen-class))

(def d6 [1 2 3 4 5 6])

(defn roll-d6 [_]
  (rand-nth d6))

(defn roll-4
  "Rolls a d6 4 times"
  []
  (map roll-d6 (range 4)))

(defn drop-lowest
  "Given a vector, returns a vector sans its lowest value."
  [v]
  (rest (sort v)))

(defn sum [v]
  (apply + v))

(defn roll-4-keep-3
  "Rolls a 6-sided die 4 times, summing the highest 3 values"
  []
  (-> (roll-4)
      (drop-lowest)
      (sum)))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

That seems reasonable. Test coverage is minimal, but enough for me to know that I'm not doing something overtly silly.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(ns stats-generator.main-test
  (:require [clojure.test :refer :all]
            [stats-generator.main :as main]))

(deftest test-roll-4
  (let [roll (main/roll-4)]
    (is (= 4 (count roll)))
    (is (every? #(<= 1 % 6) roll))))

(deftest test-drop-lowest
  (testing "Given all different values in random order, drops the lowest"
    (is (= [2 3 4] (main/drop-lowest [1 2 3 4])))
    (is (= [2 3 4] (main/drop-lowest [4 2 3 1]))))
  (testing "Given identical values, drops one of them"
    (is (= [1 1 1] (main/drop-lowest [1 1 1 1]))))
  (testing "Given some duplicated values, drops whatever's lowest"
    (is (= [2 2 3] (main/drop-lowest [2 3 2 2])))
    (is (= [5 6 6] (main/drop-lowest [5 6 4 6])))))

(deftest test-sum
  (is (= 18 (main/sum [6 6 6])))
  (is (= 3 (main/sum [1 1 1])))
  (is (= 12 (main/sum [3 4 5]))))

(deftest test-roll-4-keep-3
  (testing "Given a roll of 4 6-sided dice, sums the highest 3"
    (is (<= 3 (main/roll-4-keep-3) 18))))

At this point, I'm ready to do the REAL implementation. We'll set up a little recursion to see if we can generate a set of six 18s. I'm not good at math, but the likelihood seems pretty low that I'll actually do it. Let's compromise and try 100 MILLION times. We'll also squawk if we get close.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
(defn roll-all-18s []
 (let [counter (atom 0)]
   (println "STARTED AT:" (now))
    (loop [current-roll []]
      (swap! counter inc)
      (cond
        (= 100000000 @counter)
        (do
          (println "Gave up after ONE HUNDRED MILLION sets...")
          (println "ENDED AT:" (now)))

        (= [18 18 18 18 18 18] current-roll)
        (do
          (println "All 18s took" @counter "attempt(s).")
          (println "ENDED AT:" (now)))

        :default
        (do
          (when (<= 5 (count (filter #(= 18 %) current-roll)))
            (println "close call!" current-roll))
          (recur (roll-stats)))))))

Alrighty! We can run this in the REPL. The first try's results follow.

1
2
3
4
5
(roll-all-18s)
STARTED AT: #inst "2017-09-04T17:26:34.981-00:00"
close call! (5 18 18 18 18 18)
Gave up after ONE HUNDRED MILLION sets...
ENDED AT: #inst "2017-09-04T17:44:07.983-00:00"

It took us 22 minutes to try 100 MILLION times, and in all those tries, we only got close once. The frequency lines up with my (admittedly bad-at-math-and-fuzzy) expectations.

So how in the world did Garrett's IPhone roll so well for him so often? I suspect they weren't using the same approach as I was (roll-4-keep-3). After a little more research, I found that another approach that simply took a random integer between 3 and 18. This seemed like a pretty simple and probably more efficient implementation, but I suspected that the level of randomness in that picking was LOWER than that of the roll-4-take-3 approach. Again, I don't have the math or computer science to tell you why -- it was just my instinct.

So, I implemented a couple more rolling functions...

1
2
3
4
(def range-3-18 (range 3 19))

(defn roll-d3-18 [_]
  (rand-nth range-3-18))

...and refactored the roll-stats function into a multi-method that dispatches on the roll style you wanted to use.

1
2
3
4
5
6
7
8
9
(defmulti roll-stats identity)

(defmethod roll-stats :roll-4-keep-3
  [_]
  (map roll-4-keep-3 (range 6)))

(defmethod roll-stats :range-3-to-18
  [_]
  (map roll-d3-18 (range 6)))

No tests were added. I wanted to get to the bottom of this silly thing. Let's do some more REPLing!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
(roll-all-18s :range-3-to-18)
STARTED AT: #inst "2017-09-04T21:56:32.650-00:00"
close call! (18 12 18 18 18 18)
close call! (18 18 17 18 18 18)
close call! (18 18 18 18 18 10)
close call! (18 18 18 18 17 18)
close call! (18 18 18 18 12 18)
close call! (18 18 18 18 18 7)
close call! (18 18 18 9 18 18)
close call! (18 18 18 17 18 18)
close call! (18 18 18 18 8 18)
close call! (18 13 18 18 18 18)
close call! (18 9 18 18 18 18)
close call! (18 18 14 18 18 18)
close call! (18 18 18 4 18 18)
close call! (18 18 18 18 12 18)
close call! (18 18 18 18 10 18)
close call! (18 18 18 18 18 3)
close call! (18 3 18 18 18 18)
close call! (18 18 18 18 10 18)
close call! (18 18 10 18 18 18)
close call! (18 18 18 18 18 3)
close call! (18 18 18 18 15 18)
close call! (18 18 15 18 18 18)
close call! (18 18 18 18 12 18)
close call! (18 18 18 11 18 18)
close call! (3 18 18 18 18 18)
close call! (18 18 15 18 18 18)
close call! (18 18 18 9 18 18)
close call! (18 18 18 18 18 7)
close call! (17 18 18 18 18 18)
close call! (18 18 18 18 15 18)
close call! (18 18 18 18 11 18)
close call! (18 17 18 18 18 18)
close call! (18 4 18 18 18 18)
close call! (18 18 16 18 18 18)
close call! (18 18 18 8 18 18)
close call! (18 18 18 18 9 18)
close call! (18 18 15 18 18 18)
close call! (9 18 18 18 18 18)
close call! (18 18 18 18 7 18)
close call! (18 11 18 18 18 18)
close call! (18 18 18 6 18 18)
close call! (18 18 18 18 8 18)
close call! (18 18 18 15 18 18)
close call! (18 18 18 18 18 9)
close call! (18 18 9 18 18 18)
close call! (18 18 18 18 4 18)
close call! (18 18 18 18 11 18)
close call! (7 18 18 18 18 18)
close call! (18 18 18 18 18 3)
close call! (18 11 18 18 18 18)
close call! (18 18 12 18 18 18)
close call! (18 18 18 5 18 18)
close call! (18 18 18 18 18 6)
close call! (18 17 18 18 18 18)
close call! (18 6 18 18 18 18)
close call! (18 18 18 9 18 18)
close call! (18 18 18 11 18 18)
close call! (18 11 18 18 18 18)
close call! (18 16 18 18 18 18)
close call! (14 18 18 18 18 18)
close call! (18 18 10 18 18 18)
close call! (18 18 18 18 18 12)
close call! (18 18 18 18 18 16)
close call! (18 18 18 4 18 18)
close call! (18 18 18 18 12 18)
close call! (18 11 18 18 18 18)
close call! (18 18 18 18 16 18)
close call! (18 18 5 18 18 18)
close call! (18 18 18 18 18 16)
close call! (18 18 18 17 18 18)
close call! (5 18 18 18 18 18)
close call! (18 18 18 11 18 18)
close call! (18 18 18 18 18 12)
close call! (18 18 18 18 18 7)
close call! (18 18 18 3 18 18)
close call! (18 18 18 18 8 18)
close call! (18 18 18 5 18 18)
close call! (15 18 18 18 18 18)
close call! (18 5 18 18 18 18)
close call! (18 18 18 16 18 18)
close call! (18 18 18 18 4 18)
close call! (18 18 17 18 18 18)
close call! (18 14 18 18 18 18)
close call! (18 18 18 18 18 7)
close call! (18 18 18 4 18 18)
close call! (18 18 18 18 4 18)
All 18s took 13880334 attempt(s).
ENDED AT: #inst "2017-09-04T21:57:12.554-00:00"

WHOA! There were a TON more close calls, and an actual hit about 14 milllion rolls in.

I decided to re-run the roll-4-take-3 strategy to verify its performance.

1
2
3
4
5
6
7
(roll-all-18s :roll-4-keep-3)
STARTED AT: #inst "2017-09-04T22:51:37.468-00:00"
close call! (18 18 18 18 14 18)
close call! (13 18 18 18 18 18)
close call! (18 18 18 18 18 9)
Gave up after ONE HUNDRED MILLION sets...
ENDED AT: #inst "2017-09-04T23:09:02.571-00:00"

A few more close calls were had, but roughly the same result. I re-ran the range-3-to-18 strategy:

1
2
3
4
5
6
(roll-all-18s :range-3-to-18)
STARTED AT: #inst "2017-09-04T23:24:08.765-00:00"
close call! (18 18 8 18 18 18)
close call! (18 18 18 5 18 18)
All 18s took 321349 attempt(s).
ENDED AT: #inst "2017-09-04T23:24:09.925-00:00"

Wowzers. SO MUCH MORE LIKELY to roll 18s using that approach. The second run only took a second and slightly more than 321,000 tries.

So, what's the moral of the story? I think it's that the amount of randomness you get from computers may vary, based on what the underlying strategy is. Random numbers aren't truly random in the machine. Always keep that in mind when they rise up and become our overlords.

Also: rolling 321,000 times by hand, at one set of rolls per second, would still take you almost 4 straight days of rolling to get that all 18s set. So for you DMs out there... if a player comes to you with a character they said they rolled, and it's got six 18s, you give them a knowing wink while handing them your dice and telling them "roll again..."

Friday, September 1, 2017

Cabin In The Woods, Pt 4

I woke up early the next day in order to call the kayaking company. You see, the quality of the kayaking is heavily dependent on the river authority and how much water they release from the nearby reservoir. Strangely, the recent rains hadn't raised the river level very much, and I was worried that the river wouldn't be high enough to boat on. Luckily for us, they had released a little water, and the river was good enough (according to the proprietor).

As such, we were off for our kayaking adventure!

Day 5: Kayaking

There are *lots* of kayaking companies in the Broken Bow area. After researching, we decided to go with Yippie Kayo Kayaks. We had a good experience with them. If you're ever in the area and want to get out on the river a bit, you should definitely look them up.

There was another family in our launch, so the van was quite full on the way to the river. You might say we were crammed in there like sardines, but I'm pretty sure sardines are saltwater fish. Is there a tiny, tight-schooled freshwater fish?

Sorry, back to the actual story. We had high hopes of high adventure as we entered the river.

...except Gabriel, full of trepidation.
What a great day to kayak that was. The temperature remained in the 80s or below and the sun was occasionally obscured by clouds. The river itself wasn't very high, which was good and bad. In the area we kayaked, if the river gets too high, the rapids become... more challenging. As it was, we only had a single waterfall to go over.

Tanya was our official photographer, by the way. The following picture was taken after we all successfully made through the rapids (most of which I had to walk through, since my boat dragged the bottom) and then over the falls.

Those are the falls, under the arrow.
I had water cooler duty.

Water cooler duty
You see the cooler? It's full of bottled water. It also happens to have a small bottle of bug spray in it. It's lashed to my kayak by a single, small bungee cord. Given how I've described the river, you'd think that would be good enough to keep the cooler securely fastened. Not so much, as it turns out. There were a couple of spots where the kayak went up on its side or completely turned over (hey, it happens even on simple little turns or rocks -- for the record, I had to get out and right the kayak or move it past rocks several times).

The reason I bring this up is that even though the cooler's lid was super-tight, the cooler still got river water in it. In addition, the bug spray bottle must've leaked a little, because at the end of the trip there was a very delicious lemony smell expelled from the cooler as we opened the lid. Needless to say, no bugs wound up drinking from those water bottles...

At one point, a bald eagle put on a display for us... a display of confusion. We were paddling along when this majestic creature swooped out of a tree, diving straight towards the far bank of the river. It grabbed what looked like a rock, started ascending back towards its tree, but dropped the rock about halfway over the stream. The rock landed with a less than thrilling splash as the eagle resumed its perch.

I have no idea what I'm doing.
Our best guess is that the eagle had grabbed one of the turtles from the water near the far bank, and then either instinctively dropped it in order to crack its shell or was getting snapped severely enough by the turtle itself that the eagle decided it wasn't worth the effort. In either case, we couldn't figure out why the silly bird didn't make a grab at the fish that we could clearly see splashing around all over the river. Maybe it's a turtle-tarian?

I was surprised to learn that everyone actually enjoyed the kayaking. I had doubts about whether or not the boys would think it was fun. Photographic proof follows.

Gabriel approves of not getting sunburnt

Garrett and I try a tandem experiment. It failed.
Tanya makes me take a picture without goofy sunglasses. I smiled despite myself.
Victory! Raise your paddles for a job well done!
I've got to let you guys in on a secret. I've saved the best part of the experience for last. Are you ready?

.

.

.

SPACE PANTS!

Space Pants!
And if you're wondering, these are *not* mine, they're Tanya's, but yes, I absolutely want a pair.

This was the last of our adventures on the surprise summer vacacay. It was unexpectedly enjoyable. If you ever have a hankering to rent a Cabin In The Woods, indulge that hankering. And get out on some zip lines, and onto the river in some kayaks. If you can avoid or embrace the thunderstorms, you're bound to have a grand ol' time!