Author Archives: charlesjohnclark

About charlesjohnclark

I am a software engineer for Wargaming Seattle (http://wargaming.com/). I've worked in the games industry as a gameplay programmer, AI programmer and UI programmer for the last 10 years. I enjoy hiking, yoga and making games in Unity for my daughter.

Denali 2022

History

I’ve spent the last 13 years taking my personal days to climb bigger and bigger peaks. My daughter was born and my wife and I agreed we’d each get a day a month to ourselves. Mine were spent in the mountains. I’ve always been passionate about seeing what it looks like at the top of that mountain, or hill, or whatever. I started with Mt Si, then eventually Mailbox, and each other peak along the I-90 corridor. Eventually I climbed Snoqualmie Peak. Then I climbed King County’s tallest, Mt Daniel. I couldn’t stop, I loved it.

In the following 10 years or so I proceeded to climb each of Washington’s 5 volcanoes. First St Helens, then Adams, then the technical Baker with a few rope teams, then Rainier, and a couple years later the often forgotten Glacier Peak. Rainier was a sweet one for me. In 1997 I first found myself on Rainier. I’d gone on a road trip with my Mom, flying into San Francisco where I met her and we drove up the coast. Being from Minnesota, I’d never been on a mountain. My Mom has. We arrived at Rainier in June, 20 foot snow walls around the edges of Paradise’s parking lot. I was enthralled. I told her, “Can I see what’s over that hill? I’ll be back in 30 minutes”. She said ok. So here I went, a McDonalds employee (so in good leg shape) with jeans, a t-Shirt and steel toe boots, heading up Rainier in the snow. No food, no water. At the top of the hill was another hill. I had to check that out, so I did. Over that hill was a big snowfield with people heading up. I had to check that out. I was in love. I’d totally forgotten about Mom and the time. A few hours later someone came up behind me and said, “Are you CJ?”. I said, “Yeah”. He said “Your mom’s really worried about you”. I suddenly remembered mom! I asked how far to Camp Muir, he said about 30 minutes. I was pretty tired, no water or food you know and sitting at around 9500 feet. I kept going, but knew I’d never make it in a reasonable amount of time. I ran down and Mom said she understood. She shares my passion.

So here I am, 2017, signed up through RMI to climb Rainier via the DC route. 20 years and roughly 1 month later I’m there, standing on the summit of the mountain I didn’t realize I’d fallen in love with 20 years before. I wish it hadn’t taken me so long to get there, but I just didn’t know what I’d been missing until then. I was grateful to be there.

Last Year

Fast forward 4 years. Now I’ve signed up to climb Rainier once again, with RMI of course. This time it’s the expedition seminar on the Emmons glacier. We spent 4 days on the mountain. Prior to this I was getting tired of mountaineering. I didn’t like the long days, hard work, sleeping in tents, training and the heat of the slog on the way down. But here I am, waking up looking at Rainier, standing on the side of it. Stunning views. Going to sleep looking at the mountain. I was in heaven. It was the best place in the world. I wanted more.

This trip was a “go/no go” for Denali. I knew that going into it, and knew I was pretty much a no. I had no interest in spending 3 weeks on a glacier. That sounded terrible. And the training. And my lungs being problematic at altitude. I felt it was impossible, but also not desirable. We finish the climb, for me it was easy, I felt strong, my lungs worked brilliantly for the first time. I had fun, and a lot of it.

Back at RMI headquarters we’re sitting around enjoying beers and food. The lead guide is chatting with us and he brings up Denali. Looking around at the team he says “I speak for all of you, you are a no-go for Denali. You all need some more experience mountaineering. You need to have everything dialed for Denali”. He then said “CJ, I’m intentionally not looking at you. You are a go”.

The next day I knew I was going to go for it.

6 Months Ago

I signed up for TrainingPeaks/UphillAthlete. Last year on Shuksan with RMI the guides recommended that has the best program for climbing Denali. Denali cost $11,000, so $100 more for a training program is a slam dunk sale. I worried I was computing my aerobic threshold incorrectly, so I talked with Scott, one of the owners there and he helped work out the details and confirm that I was doing the training right. This was a huge help. I worked hard, followed the program to the T, at least as best as I could. I ran into two illnesses, first was a cold in January that lead to a 2 week lung infection. Then in April or so my daughter got Covid from school. This fortunately didn’t impact us much, but I took it easy to ensure I recovered quickly. Overall I lost 3-4 weeks and I was paranoid this would impact my fitness.

The Start

June 8th is my fly out date. I’m nervous, paranoid about fitness and excited. I didn’t start getting excited until I arrived at the airport. Upon arriving my daughter pointed to someone who was just being dropped off. She asked, “is she on your climb?”. I looked and said “yeah, maybe”. The giveaway was the same 105 liter pack I rented from Whittaker Mountaineering. She also had the same huge “mammoth” duffel bag I had. Of course, I recognized her from a team Zoom call a month before. Her name is Inga-Lisa.

I said my goodbyes to Jean and Claudia and just before leaving Claudia grabbed me and held something out to me. A small shoe from an old doll, just 1. She held the other. She said when I get back they will be together again. I knelt down and thanked her, promising I’ll return. This meant a lot of me. It was important. I was going to take this shoe to the summit with me and back down.

I was curious of course at the baggage check how much my packs would weigh. One came in at about 64 lbs, the other right about 50. Arriving at the gate I spotted Inga and sat with her to chat and get to know one of our team members. It turns out another Seattle team member Nick was also on the flight, we just didn’t spot him until we landed.

I was on the left side of the plane, a bummer since Alaska is on the right as we fly. However, I scanned my ticket at the gate and it printed me a new one. Right side! Even better, the passenger next to me was an ex-mountaineer. He climbed some Oregon peaks, nothing too crazy, but he was fun to talk to about his experiences and about Denali.

When I arrived in Anchorage I had butterflies in my stomach as well as nervousness and paranoia. It was a melting pot of emotions.

We arrived about 2 hours before the official team meetup. Inga and I hung out at baggage claim 1. Nick we found later when wandering around and convinced him to sit with us. Shortly after the rest of the team arrived. For me this was a nervous time. 2 hours until the meet up, X flights flying into Anchorage and only 3 of us were there!? Then 30 minutes from the meetup time everyone else shows up. My programmer brain was very surprised that more didn’t arrive sooner. Crazy timing.

The full team was:

Guides:

  • Hannah
  • Kiira
  • Dan

Clients:

  • Me
  • EWald – going for his 6th of the 7 summits
  • Erin – going for her 6th of the 7 summits
  • Nathan
  • Yvonne
  • Nick – Olympia
  • Jason M
  • Jason N
  • Inga-Lisa – Seattleite

We did our introductions and the guides did a high level of all absolutely required gear before leaving Anchorage. A couple people missed one item, so that was a quick stopover at a local gear shop. Shortly after the van showed up with a trailer for our gear. We filled the trailer to the brim and climbed into the van. It was a tight squeeze, but comfortable enough for a 3 hour ride. On the way we swung by a Fred Meyer in Eagle River to pick up fresh foods. I got a few packages of salami and pepperoni, some cheese as well as 6 fresh rolls with 6 packets of mayo. These made for excellent snacks on the mountain. I also picked up my pee bottle, a necessity at 17 camp if you don’t want to totally freeze going to the bathroom at night.

A few hours later we were in Talkeetna. We stopped at the airport and unloaded our gear in the hanger. We walked to the motel, the Swiss Alaska Inn. Naturally, I was bit by mosquitoes on the way. They love me. EWald and I roomed together. The room was definitely tiny, and he (being from Austria) commented that it certainly was Swiss style. We got room #5. The guides specified a time to meet out front for dinner and sunk with them. They had bikes that they rode into town. We went the wrong way finding the path to town, but soon found the way. I’m not sure if the bikes are loaned to guides, or if RMI actually keeps bikes in town for the guides.

We met up for dinner at Denali Brewpub, a place next door to the famous Fairview Inn, where Denali climbers go to celebrate after their climbs. We grabbed beers at the bar and sat at a table inside. Other members of the team were already there when we arrived, having beers and food outside. I had pulled pork and their brownie, both were very good along with my usual Hefeweizen to wash it down.

After dinner we headed to the end of the road to see if Denali was out. It was partially, though mostly obscured by clouds. The river was very high, and flowing quickly. Parts of it moving close to 10 mph. It was an impressive river for sure. From here we headed back and to bed around 9.

Gear Check

Day 0:

The team started the day off with breakfast at the Inn. I’m pretty sure I had french toast, because why wouldn’t I. From there we headed to the hanger and did an extensive gear check. This took 4-6 hours, I don’t recall exactly. We did half before lunch and half after. We took out everything we had except food and verified each item by holding it up. Some items, like warm layers Hannah verified visually to ensure they were sufficient. Some items Hannah said not to bother bringing. Other items such as the solar panel’s we discussed amongst the team to determine who would bring them. We went with the lightest ones and left the rest. I was a bit frustrated about this as I reached out to the team before leaving to see if anyone had one and only got, if I recall, 1 response. This prompted me to get one. Here I was in the hanger looking at at least 5. Could have saved me some cash…

I enjoyed this hangar, we got to watch tour groups fill several flights and take off, excited for their visual adventure which I was to find out tomorrow, is stunning and something I recommend anyone visiting Alaska partake in.

For lunch EWald and I headed into town and checked out another place, right across from the Denali Brewpub called the Homestead Kitchen. It was fine. They unfortunately also had BBQ food, so I ordered…pulled pork and a Pacifico. With the warm day, the beer went down well. After lunch we headed down to the end of the road again to see if the mountain was out. It was a bit more visible, but still partially obscured.

From here we headed back for the final couple hours of gear check. This included what food to bring. I broke down my food in roughly 1900 calories per day of lunches and snacks. The guides said we shouldn’t go over 24 lbs or so. Mine was about 34. Now for the hard part, deciding what to cut. I cut stuff I didn’t want to cut and weighed again. Still close to 30 lbs. I cringed as I cut some items I really wanted to keep and if I recall, I ended up with 26 lbs, which the guides said was ok. In the end, I carried home half of that (spoilers).

Packs finalized and ready, we piled them in appropriate piles for balancing the loads of the planes. The team would take 2, 6 people per flight. We had to weigh ourselves, our packs and our duffels to ensure the pilot had proper records. My pack weighed 38 lbs and my duffel with 2 liters of water weighted in at 38 lbs. This is before adding fuel and team food which likely took me to around 100 lbs. We placed our bags in either the blue or red square on the ground, designating what plane we were on.

Finished with that, EWald and I headed back into town, me always walking quickly, or popping my legs up and down quick to avoid the mosquitoes. I wanted to check out the famous Fairview Inn, so we headed there for a beer. We sat in back in the shade where I itched a few of my new bites.

For dinner the team met back at the Denali Brewpub and I had the brisket and of course, a hefeweizen and again the brownie, which I was more successful at getting people to share with me. While here I ran into someone I knew! My lead guide from Mount Shuksan last year! That was crazy cool. Turns out he’s a Denali ranger in the spring, has been for 4 years now. I saw him a few times on the mountain as well at 11 camp, 14 camp and even at 17 camp briefly and got to chat with him a bit. He flew on the same day as us as I understand.

Bed time was at 9, and our final shower, our final real bed and our final proper night of sleep. Tomorrow we fly out.

Flying On

Day 1:

Alarm at 6am, breakfast at 7am at Conscious Coffee down the road in town. I had crepes which were pretty good. Everyone seems to like this place, it built a line quick, so we took Hannah’s suggestion and showed up 15 minutes before they opened.

We met at the hanger at 8 and waited around, each of us anxious and excited, some maybe nervous. I think we were supposed to fly out at 9am, so when that rolled past and turned towards 10, I was definitely anxious and excited to go. There was the chance we wouldn’t fly. The weather in Talkeetna AND on Denali has to be clear so the pilots can land. If one of them is cloudy, we stay another night. If we did, the rooms were booked, so the Swedish Inn said we could setup tents in the front yard.

10am hit and we were a go. We loaded our bags onto carts and rolled them across the tarmac towards the planes. The pilots took their time packing the plane, being sure everything was stable and fit. They take out the right side of the planes seats and setup a net system that our bags get strapped into. Then we took a group photo (in front of some random plane that was, I think, the same make) and once the pilots were ready, we loaded in. I called shotgun, which everyone said was ok and I got to ride co-pilot. I took tons of photo’s and a few videos, including of the takeoff and landing. I enjoyed the opportunity to tell the pilot about my grandpa and grandma. My gramps being a B-17 co-pilot in WW2 and my grandma being a W.A.S.P during WW2, one of only 2000 women selected to learn to fly WW2 planes and teach men to fly. The pilot was excited to hear about that.

We taxied down and took off to the south. The pilot commented “You are flying light!” as we hit the air. Apparently we didn’t over pack! We headed north, flying over the river, seeing the ponds and lakes, many of them very long and the trees. I love this stuff. The trees gave away to low foot hills, in front of us looming the huge glacier-capped peaks, partially obscured by clouds. Denali still hidden. Shortly after the mountains were under us. Two enormous glaciers extended towards us, the bottoms covered in dirt and rock, each 20 miles long. The pilot explained that they were heading for the pass ahead, 10,000 feet. The altimeter slowing ticking away. He had the angle of inclination perfect. Probably not his first flight.

I loved looking down at the peaks and glaciers as we flew over them. I’d never seen them from this angle before, and so close from above. As noted, I love this stuff. We finally hit the pass and headed right, flying up the Kahiltna glacier, a 30+ mile long monstrosity, up to 2 miles wide. We flew up valleys, huge peaks all around. The scale here was enormous. Pictures don’t do justice. Looking at these peaks, they all appear to be 3-4k in height from the glacier, but most are 6-7k or more. There are no points of reference. As Hannah would say, we should plant a 10 foot tree somewhere for reference. These mountains are huge.

The pilot flew over the landing strip. As he said he always flies over it to verify that it’s safe to land. Looking down we saw a few tents and maybe a plane. That was it, finally, that’s where we were heading. He heading down the glacier, turned around and flew up the hill and into a soft landing. Quite impressive for landing on snow!

Ready and excited, we all hop off the plane. It’s now 11am. The pilot hands us bags and we all line up, fireman style shuttling the bags back and forth to each other down the line where the final person threw them in a pile in the snow 150-200 feet from the plane. The snow is good, it’s warm, there’s no wind and most of the biggest peaks are hiding behind some clouds. But we’re excited, I’m cheerful and very motivated and focused. We setup our sleds and do a quick walk down the glacier to our camping location.

The view here was incredible. The biggest peak next to us was called Mt Hunter, a 14,500 foot sharp and steep peak. With us at 7,050 feet, it was the same as standing on Panorama point on Mt Rainier and looking up at the summit. It looked half the height, but again, the scale. It was a stunning mountain. Down the glacier lay Mt Foraker, a 17,300 foot peak, it was huge, clearly even. I knew we’d eventually be above that, and that was daunting.

The team works quickly to smoothen out an old campsite and setup the tents. We got that done by 12:30 and were resting by 1pm. Our pizza dinner came at 5pm. Yes, pizza. The guides bought us pizza in Talkeetna, threw it in the fridge overnight and brought it on the plane. That was awesome.

I camped with EWald and Erin, our two more experienced mountaineer clients and both super strong climbers. While hanging out at camp we were chatting with a couple guys who had just come off the mountain, one had a large black spot on his lip. Frostbite. I knew we were into something serious and certainly worried about what the conditions would be like up there.

We settled in at 6:45 after dinner and I played some Castlevania: Harmony of Dissonance on my Game Boy Advance. Yes, video games on Denali. It was perfect! We need to bring entertainment. Some brought physical books, some brought Kindles, some downloaded movies. The guides even downloaded the last 2 seasons of Stranger Things (the best show ever). I brought my GBA. Prior to leaving I took some time to figure out what device to bring. I’d originally thought my Switch. With my new game room setup, all my devices were there on the shelves. It took all of 10 seconds to decide. The Switch is heavy, my DS is heavy. The GBA is very very light, and it’s rechargeable. Done deal. From there games. I realized the GBA had 2 more Castlevania games I’d ever played. I’d gotten tired of them and didn’t buy they. So EBay showed my that there was a Double Pack, both of those games in 1 cartridge! Perfect. I brought that and my personal favorite, Final Fantasy 6. Spoilers, I completed both Castlevania’s by the time I was at 14 camp and got to start in on FF6.

7:15 was bed time for me and by 8:45 there were some light flurries of snow coming down.

As a quick note, every night on the mountain at 8pm is weather on the radio. The base camp manager reads it out for each elevation, temperatures and wind. After this is trivia. Usually trivia is about Denali based things, or the surrounding area. Sometimes it’s about any mountain in the world. Some are very obscure, some are easier. One of the questions asked early in the trip was “What is a flock of ravens called”. Our guide was likely the only one who knew. It’s an “unkindness” or a “conspiracy”. Most people who don’t know the answer have two answers. For words it’s one of the key climbers of Denali history (I think relatively recent), or if it’s a number, they answer 69. Of course.

Moving to 7800 Camp

Day 2:

Difficulty: 5/10

Wake up time was 12:20am. We knew it was an early wakeup. We had a large glacier, the Kahiltna to head up with a total distance of around 6-7 miles to travel. This day won’t be big on elevation. We were currently sitting at around 7050ft of elevation and would head down Heartbreak Hill to around 6600 feet, then slowly up the glacier, dodging crevasses as needed until we reach 7800 camp.

Breakfast was 2 bagels with salmon on them. I was definitely not very fond of the salmon, but I ate them anyway. We packed up camp and then loaded our sleds with weight we were willing to carry vs pull. I went weight about 40 lbs in my pack and 60 lbs in the sled. I’d rather drag more weight on flat terrain. The guides advised otherwise, but I was very happy with my choice. We were ready and headed out at 3am.

It’s worth noting, during this entire time it was bright out. Sure, the sun had set, and remained set until around 2am. Notably, by “set”, I mean, 3-4 hours of sunset. It never gets dark. With the snow reflecting the light as well, it was bright.

As any proper guide service would do, we took breaks every hour. The first at 5:15am at 6,650 feet just past the bottom of Heartbreak Hill. The second was at 6:30am at 7,000 feet. The third at 7:45am around 7,300 feet. Finally we reached our goal at 8:45am, 7800 camp at….wait for it….7,800 feet. At our first break Denali poked it’s head out. Whoa. That’s a big mountain. Along the way the weather got colder, maybe 20 degrees base temp. The sun stays behind Denali in the mornings until around 8:45, at least in our case with the clouds, that’s when we finally felt it’s warmth.

On the way, many many reminders of how hard Denali is. We passed 4-5 teams heading down, each looked beat. Absolutely destroyed. Exhausted, miserable and zombie-like. Most with clearly cold scarred cheeks and faces that had seen too much sun. One as he passed, with a half black nose. Yet another reminder of the cold. He was likely going to lose the last 1/4 inch of his nose and end up needing plastic surgery to repair it. This is a serious mountain.

Overall I found sled travel to be much easier than expected. It does a good job staying behind you on the flat Kahiltna, though as we find, the steeper slopes it tends to slide off to the side. Either way, it’s not terrible, until we descend later on. My boots hurt. They were plastic boot rentals from Whittaker Mountaineering. I didn’t have any hot spots, so the linger was excellent, despite the inner laces coming undone every hour. I had a sore spot, a deep bruise feeling on the front of my shin. I laid it in the snow to ice it a bit, which helped. I talked with the guides about it and they recommended leaving the inner boot more loose. I tried it on following days and it worked like a charm. The only other time I felt this was on the walk out along the Kahiltna. Others had hot spots and blisters and needed some treatment. At this point I was worried about the boots on the up-hills and a bit frustrated by them. I did try the down camp booties that I picked up. I was very relieved I brought them because it let me get out of my boots. They were also super nice, warm and when I put my feet on the snow, I couldn’t even tell.

Camp was ready about an hour and 15 minutes later and we took at nap at 10am. I did manage to sleep surprisingly, though very broken 30 minute bits at a time and needing to pee 3 times. Got up at 4:30 and played Castlevania until dinner at 6. Dinner were 2 burritos. I’m not fond of rice in them, but dealt with it because we need the calories and carbs. I did pass on the beans. They were very good, made by Dan today, but the later days were made by Hannah and I liked hers more (sorry Dan!).

One of our guides, Kiira wasn’t feeling well, she slept more and remained more isolated, staying out of the cook tent while we were all in it. We were worried that could impact the other guides. Can’t climb a mountain if all your guides are sick! As I recall, she later said she’d been feeling poorly since base camp. Impressive that she pushed through that long move day up the glacier!

We hit the sack at 8pm. Around this time I heard from my friend Mik who was up here as well, he made 17 camp where it was -25 degrees. I bathed (not sure why, it was only day 2!) and played Castlevania some more from 8:30-8:53 and was “lights out” by 8:55.

10,000 Cache Day

Day 3:

Difficulty 5/10

We woke at 3:30am, prepping to cache roughly 45 lbs of food and fuel each. We do cache and carry’s up the mountain from each camp to minimize how much weight we have to carry. Cache day is today, carry day is when we move to the next camp.

I was a bit stressed. I knew hitting 10,000 feet with a lot of weight is hard and altitude becomes a real thing. I also knew I’d done this before, but not with a sled. We threw on our snowshoes, ensured our sleds were ready, snacks were prepped in our bags and all our emergency gear was in our bags. First we had to head up Ski Hill, then we followed the endless hills after it, there were 3-4 of them. It was about 3 miles up, but it felt like 5.

We reached our first break at 7:30 and our second break at 8:45. We climbed in the clouds, so we only got minor views of the ridge to the right. Around 8300 feet this ridge had a stunning glacier fall coming out of a valley. I wanted a better view, but wouldn’t get one until the descent. It started cold and calm, but by the time we reached the cache at 8:45 it was very cold and windy. I had most of my layers on at this point.

Hannah probed around the area to ensure we weren’t digging into a crevasse. Dropping off your cache on top of a crevasse has a few obvious problems. Once she was sure it was safe, the guides started digging a 5 foot hole with a large open basin. Essentially, we like to build snow forts. However, instead of filling it with excited kids, we filled it with tons of food and fuel. They shoveled snow into it to fill it back in and various members of the team helped stomp it down. The guides set flags in it to mark the location, we strapped our sleds to our packs, threw them on and headed down the mountain.

We reached camp around 11:15 and I had 2 sandwiches made by noon. Camp was warm, with no logged breaks (maybe I forgot to log one), I imagine we stopped to delayer on the way down. My boots were fine on the way up, but hurt on the way down. Later in the journey I felt like it was the show shoes that was causing it. I found I needed to walk with my left foot tilting in a bit to prevent the left side from hitting the front as hard. The mosquito bites were still bugging me.

After lunch I kicked back to more Castlevania, 4 hours of it from 12:40 to 4:40, though I interspersed some of that time by stepping outside to enjoy the views. Around this time is when I heard of another “death” (I later learned was just a fall) up past 17 camp along with a report of someone on a summit attempt having frostbite on most of their toes. It seems to be normal to be up here and hear about a few events happening, often a death. It makes the trip harrowing, and is a constant reminder of the dangers and importance of following safety protocols. Hint, clip in at all fixed lines and auto-belays.

Dinner came at the usual time 6pm. I was back in my tent at 7:15. During this time there was a massive avalanche off the Kahiltna Dome a mile from camp. More reminders of the dangers of Denali, but we were able to enjoy it from afar. Bed time was 8:15. In the evening the guides hung out with some friends from other teams in their cook tent. Hours earlier they told us not to make any contact with people outside our team, Covid is all over Denali.

Needless to say, I was a bit frustrated by this lack of following their own rules at what could be our expense. It did degrade my confidence in their professionalism. At the same time, I wasn’t worried, we were outside. But the principal of the matter…

Move to 11 Camp

Day 4:

Difficulty: Start/Hills 3/10, final 1,000 feet 5/10. Overall very easy, done at very slow pace.

Today is move day. We’re heading to 11 camp which will be the highest I’ve ever stayed for an extended period of time. We’ll spend 3-4 days there, weather depending. I’m a bit nervous, but feeling ok from the cache day as today we carry less weight.

We’re up at 2:45am, break down camp and leave at 5am. The endless hills once again. They roll on and on. Over one hill, there’s another to go over. There’s no wind until we hit 8,500 feet, our first break at 7:00am. Second break comes at 9,500 feet at 8:30am. Our breaks were long, too long. Guides always say breaks are short. Take the pack off, grab a jacket, throw it on, grab water and food, sit on the pack, eat and drink. Be efficient. 15 mins tops. Most of our breaks averaged over 20, closer to 25. It was frustrating as we started getting cold and worse, our legs cooled off and took some time to warm back up.

We hit 10,000 feet where our cache was and kept moving. We’ll come back down tomorrow and pick it up. Here we start to ascend the right slope of the glacier, climbing 200 feet, snow shoes lumbering away, my leg feeling it more here due to the angle on the slope. Here the breeze picked up, a stead 15 mph or so, enough to be annoying. It was also enough to fully obscure the tracks of the guide ahead of me (Dan) due to a layer of 1-2 inches of snow filling in the marks. I found it satisfying to watch the rope move snow as it dragged along to cover some of the final snow shoe marks. It’s interesting how your mind keeps you occupied when you walk for such long distances.

We take a hard right and head straight up the glacier. 300 feet layer we hit 10,500 feet at 10:00am, our 3rd break. We are close, could have just finished it, but some people are showing signs of tiredness and we have no reason to push through, we have all day to rest, mine as well here!

We don’t like the steepness of the slope, but we push through it and reach 11 camp shortly after and have tents built by 11:30am. We took some more time setting up the base layer of snow for our tents, chopping away at the low wall at the tent site to flatten out the ground. We knew we’d be here a few days.

It was warm in the tent, too warm, but nice outside. At these camps I tended to try to minimize the time in the tent, but with the sun out, sometimes you don’t have a choice. I’d try to open the tent flaps as much as I felt I could, though often not enough to help provide cooling. I felt my other tent mates may feel it’s too cold. I should have just asked.

After setting up camp I mentioned something to Erin, I think it was where we would be sleeping and she seemed irritable and didn’t want to talk. She climbed in her sleeping bag as soon as she could. Worried I asked the guides what was going on. The said she may have a cold.

I helped the guides setup the cook tent, I had a lot of energy and was feeling really good. Super stoked, felt strong. It really helped boost my confidence. A key thing they always said “You need to roll into camp in good style, setup tents, eat, drink. If you roll in exhausted and not taking care of yourself, that’s as far as you’ll go”. I took that to heart.

11 camp was very different from 9800 camp. Lots more people and one nearby team would cheer every few minutes. I looked to see what was going on and they were tossing ice axes a short distance, almost like playing horseshoes and cheering based on some result. Looked fun.

I heard from Mik, he said it was -51 on the summit, seems like a bad day to try to summit. They of course didn’t try, and tomorrow is their final day to try. I didn’t feel optimistic for him as if I recall, the forecast wasn’t much better.

My lets were itching again. Mosquitoes are the worst. EWald said we hit 21k steps today, I believe it. I bet each move day is around that. I played Castlevania for an hour, slept for 2, then dinner was at 6. Chicken noodle soup, mac and cheese and Grandma’s cookies. I gave the soup an honest try, but barely managed it. The broth I was barely able to stomach. Definitely not for me. Around dinner Hannah told us lots of stories about Aconcagua (she told more at 14 camp), it sounds awesome. At this point I’m sold, gotta go, though by the time I hit 14 camp I was pretty sure I didn’t ever want to climb another big mountain again. 😛

I played more Castlevania at 8 and crashed out for the night at 9:40.

Day 5:

Today we pickup our cache at 10k. Our final day until the descent where we use snow shoes. I’m happy about that. Looking forward to crampons.

We’re up at 7:30am. It’s cold, very cold. Maybe 10 degrees out, so we throw on layers for breakfast. Breakfast is pancakes. Ya, you thought we’d be eating oatmeal and grits the entire time huh? This is the perk of hiring a guide service, they bring awesome stuff! It put a smile on my face.

Today’s EWalds birthday and at breakfast we sung him Happy Birthday.

The sun hit camp at 9:10am and we left camp at 9:30am, heading back down the slope. Erin and Kiira are staying behind so Erin can rest more. RMI can’t leave a client on the mountain alone, so Kiira has to stay with. Our rope team was about to head out, but got delayed due to rope length and snow shoes causing us problems. Took an hour to descend to the cache. Once dug out we stuffed the 2 food bags into our packs. My pack wasn’t able to fit more than the 2, so Nick took my parka for me (thanks man!). My pack weighed about 65 lbs. We slowly trudged back up the slope, taking one break again at 10,500 feet around noon and reached camp by 12:45.

Upon returning to camp and the tent again being hot I realized sitting in the cook tent would be nicer. it was! It’s got much more room, I can stretch out, there are big doors to help cool it and it’s a great place for people to hang out and chat. No one really joined me, but I didn’t put much effort into sharing how awesome it was. I hung out here until 2:40, hate a protein bar and a sheath of Ritz with pepperoni. I chatted with EWald for an hour and played a little Castlevania.

I was a bit paranoid I’d get sick with my tent mate feeling unwell. I had a slight sore throat, but maybe that was just from talking. I was talking more than I normally do. I also sneezed twice, which isn’t normal. This sore throat stuck with me for the next week or so, and the sneezing for most of the trip. I even theorized I was allergic to down (of course, that’s not true) because every time I would climb into the tent I’d end up sneezing shortly after.

I played more Castlevania in the tent and then napped at 3:30. Dinner at, of course, 6. It was burrito’s again, Hannah made them. They were gooey and delicious. The guides gave EWald a postcard of a mountain from Utah or Colorado (as I understood) signed by each of them. They also managed to successfully bring up (uncrushed) some Hostess cupcakes as a birthday surprise. Hannah told more poop stories, we get to hear a lot of them while up here. Seems climbers occasionally poo when they fart, so she said “never trust a fart”. She even told a story about a certain someone in her live who needed to poo bad at her school before practice, but there were no open bathrooms. The janitors closet was open though…

I enjoyed a long conversation with Nick about all kinds of topics from guns to bullies to politics. We chatted more at 14 camp as well. Good guy, I like Nick.

The clouds finally broke and gave us a stunning view of all of the nearby peaks. Mt Hunter was out, Mt Foraker was out. We even got an excellent sunset (though we never get to see WHERE it sets as it sets behind Denali). However, as always, when the sun sets, the temp drops fast.

No news today from Mik. He was certainly on my mind a lot as I knew today was his last day to attempt a summit. I was curious where he was, but knew it was windy up there even though our camp was calm and sheltered from the wind due to being protected from 3 sides. 11 camp is quite a nice camp.

Bed time was 10:30. I threw my eye mask on (my daughters pink one) and laid there until I fell asleep, which usually takes a while. I did note a team coming in at 11pm. I think this was IMG. Looking back on it, was also around the time that Nims, the 14 Peaks guy. I found out after the trip that he was in 11 camp around the day or day after we arrived. He used it as is main camp, going from the airstrip to 11, camping, summiting, back down, camping and then heading out. The guys a beast and soon to be a legend and multiple world record holder in mountaineering.

Day 6:

Rest day.

Guides woke us at 9am for breakfast burritos. Notably, Erin didn’t join us. After eating the guides announced that Erin had Covid. I was immediately worried, but in the back of my head, I knew I’d never yet been impacted negatively (enough) by my previous Covid exposures to really worry. I still worried, a lot. This was a very important climb to me.

The guides announced that Erin will remain isolated in the tent. We were all going to move into the other tents, 4 per tent which I later found out at 17 camp is VERY packed and very uncomfortable. The other climbers were worried about EWald and I being exposed and passing it to them. Rightly so. EWald and I offered to sleep in the cook tent. The nights aren’t THAT cold at 11k to worry, we have warm gear. The guides disagreed and said they would take the cook tent, EWald and I can have theirs. That was nice of them. They spent some time chopping the seats out of one part of the tent to make enough room for them to lay down, though barely. They were as cramped as we’d have been with 4 in a 4 person tent. Pretty much snuggling all night. 😛

EWald and I moved our gear into the guides tent and setup. 2 people in a 4 person tent is like a 3 star hotel. It was nice! We spread our stuff out and enjoyed the extra space!

Despite the bad news, it was a beautiful day out. Low clouds around 5k and very high Cirrus clouds. It was warm and sunny, base temps maybe 30 degrees or maybe even more. But everyone is still worried, some very worried. I felt like I had a sore throat still, and of course not maybe it feels worse. Still no word from Mik.

I played Castlevania for an hour until 2pm and then the team did a quick recap of crampon usage since tomorrow we are heading up to cache gear at 13,600 feet. I packed my bag, around 40 lbs total so I was ready in the morning. I went back to play Castlevania for another hour, but then decided I wanted to pull half the weight. Mik had mentioned pulling half of it to 13,600 and being happy he did. So I repacked the bag and moved half the weight to the sled.

Dinner was Miso soup and Indian Punjab Eggplant. I didn’t like this idea, but knew I needed to eat well. They dared me, especially Kiira, to try both. I tried the Miso, it was terrible. The tofu was the best part of it though it still sucked. It tasted like nothing. Weird and gross to me. I was worried about the Punjab Eggplant, certainly the first time I’d ever eaten anything like that as well. Dan, being awesome, hooked me up with some chicken of his and I mixed it in. I took a bite and despite wanting to go “gross”, it was actually pretty good. Quite tasty really. I’m still grumpy that I liked it. 😛

I chatted with Nick and Inga for a couple hours, we often talked about what Seattle climbs we wanted to do and worked on plans for ones to do when we got back. An IMG team rolled in last night and now they were doing self arrest training. I found it pretty strange that they were practicing that, a key and required skip to be very good at (per RMI) prior to being able to even come to Denali.

Finally heard from Mik, he’s at 14 camp for a few hours rest and it sounds like they are heading down. Damn. Denali decides if you get to attempt to summit. Then if you do get to, it’s up to your fitness and body to determine if you can. Lots of obstacles. This is a hard and unforgiving mountain.

I finally beat Castlevania Harmony of Dissonance with the middle ending. I was a bit undecided on what game to play next. Castlevania Aria of Sorrow or Final Fantasy 6.

I headed to bed at 10pm.

13,600 Cache Day

Day 7:

Difficulty: 7 or 8/10

Today has me nervous. I know my pack is heavy and we’re heading to 13,600 feet, nearly the height of Rainier. I’ve never carried more than 15 lbs or so that high. Will my lungs cooperate, will I be strong enough? The air is going to be thin. I’m also worried I may have a lung infection. I’m prone to them and I’m paranoid, daily, about being sick.

We wake at 6:30am. Breakfast is oatmeal in powdered milk. Not great, but good carbs. It’s chilly as usual, maybe 10 degrees, so layers go on around camp. We have to climb Motorcycle Hill, something called out as being very steep. Following that is Squirrel Hill, then across the Polo Fields to reach Windy Corner. Motorcycle Hill doesn’t look huge, maybe a few hundred feet, at it’s top is a small Bergschrund we have to cross.

Of course Motorcycle Hill isn’t a few hundred feet. It’s 700. Then Squirrel Hill after it looks 150 feet. It’s 500. Then the flats after it look pretty chill and short. They are 800 feet and 2/3rds of a mile long. The scale of everything on Denali is huge. Just need a 10 foot tree to tell.

Today though we have two things going for us. Clear skies, no wind, and crampons. No more snowshoes. They will be cached at 11 camp when we leave for 14.

We rope up and leave camp at 7:50am. My total pack weight is around 40 lbs, half of it’s in my sled, half in the pack. Our cache contained a couple bags of food, a large bag of personal snacks and all of our summit day/very cold weather gear. We head through camp, nice and flat and a good warmup. Motorcycle Hill quickly ramps up to being 30-35 degrees in steepness and quickly was felt in the calves. It didn’t seem to want to end. Each step slowly brought us further along, but the end stayed way ahead of us. My calves weren’t happy. We reached the Bergschrund, crossed it carefully and kept going, the hill bending to the right as it slowly leveled out. Break at the top around 9:30am. By this point I had a pretty strong headache. Altitude.

The view here was incredible. For the first time we can see to the North West. We could see down the valley, roughly 3,000 feet below us and the first sighting of the dark rocks. All of the rocks until now have been brown. These were almost black. Quite stark and incredible and it made me wonder how it was formed.

Squirrel Hill was now in sight, it looked short, but steeper. We slowly worked our way up, using much more of the crossover step. We don’t walk straight up, it stretches our calves too much, so we walk with toes wide, or sideways crossing our feet over. It wears at your hips, but makes it easier to take small steps. I prefer toes wide facing straight up hill, but I find myself taking too large of steps. I mixed it up a bit, switching sides, going straight. Much of Denali requires this kind of walking. I hated it, but after a few days started to accept it and get used to it. In the end, Squirrel Hill didn’t quite show it’s true height. At the “top”, the hill fell away a bit to not be as steep, but kept going. Damn.

We reached the top of Squirrel Hill and took another break. It was now around 10:45am. The Polo Fields ahead of us were a nice looking break from the steep hills. I recall at this point the peaks to the right looked amazing with some awesome crevasses, but they were partially obscured by fog or clouds. To our left was the end of the West Buttress. We saw groups ahead along the Polo Fields and also knew this was where our sleds were going to dangle to the right of us as we walked, something I worried would be pretty annoying on the trip, but I found it to be reasonable. Windy Corner lay at the top of the saddle. To our left was the start of the West Buttress, the ridgeline we would be walking along after 14 camp in a few days.

Overall, I was feeling confident. My breathing was good and I felt strong. On the way up 3 or 4 teams passed us heading down. This was a relatively busy section of the mountain.

We made for Windy Corner, cruising along at a pretty good pace due to the flats. Third break was at 11:45 just under the lip of Windy Corner, allowing us to remain protected from the wind, though there was very little today. We sat on the rocks along this ridge and looked back down the slopes, watching other teams slowly work their way up and marveling at how far it really was, despite how short it looked.

Break time over, we made for the cache. Past Windy Corner we curved left and could see the hill to 14 camp. It was much further up for sure. This area was more crevassed, and the 2300 foot tall face of the West Buttress to our left loomed rocky and threatening to drop rocks on us. I was thrilled when we finally stopped at our cache. It was just after noon, around 12:15 and Hannah was already digging at a pre-used cache location. We were at 13,500 feet. We plopped down in the slow, had more snacks and water, pulled out our personal bag of food and both team bags and threw on sunscreen. The view here was incredible. The large cracks below were beautiful and a reminder that we’ve been sitting on a glacier for a week now. The guides dug quite a deep hole, threw the cache in, filled it up, flagged it and we headed down.

If I recall we took 1 break around Squirrel Hill, then all the way down to camp, reaching it around 1:30. The hike down was entirely in clouds below Squirrel Hill. My headache was still there and had worsened as we went up in altitude. Ibuprofen it is, that helped. I also asked the guides if I should start on Diamox. They said go ahead, with the low dose around 62 mg. I did that and once started, stayed on it until we summited. I did increase my dose to the 125mg upon reaching 17 camp, or when we left 14. I also had a sore throat, a clear warning that I could be sick. It kept me paranoid for nearly a week.

By 2pm I was resting in the tent and it started snowing again. It was light snow, more sleet-like and very hit or miss. Never more than a half inch of accumulation. I also found myself peeing a lot today. I rarely pee while travelling on a rope team, I prefer privacy. I had to pee 3 times. That at least helped me get used to it.

My heart rate felt really high, I did a quick check, my resting heart rate was 84. It was 75 before. I napped till 5:30 and by then it was super hot in the tent, but it was time for dinner so I got up. Soup. Great. It was also super hot outside, it felt like 70.

My pickiness was an entertaining thing for the team, especially Kiira. She enjoyed making me try new things and seeing how I reacted. The team said I should make a YouTube channel called “CJ tries mundane foods”. Dinner today however was better because we had Ravioli style noodles (though not filled with cheese) and a Grandma’s cookie. It was a bit plain, but still good for mountain food.

However, not we don’t have a cook tent. We’d made a new location nearby and sat at it, but no one seemed to want to come over, so we largely abandoned it from then on and just stood around outside the cook tent or ate in our tents.

The weather cleared a bit and big billowy clouds showed their faces. Incredible views. But, after dinner it socked in again and the temperatures dropped quick. Tomorrow is a rest day instead of a move day because another RMI guide who’s up at 17 (Avery) is planning a summit attempt and will be down on Saturday, 2 days from now and he can pick up Erin to bring her down the mountain. This allows us to stay on schedule despite the plans to try to cut a day from the schedule and move tomorrow. The forecast suggests good weather for the weekend, so we could move to 14 and pick up the cache. This would have us all set to hang out for weather windows for a summit attempt.

I played Castlevania Aria of Sorrow for 2 hours or so and headed to bed at 9:20.

Day 8: Rest Day

I barely slept last night, didn’t fall asleep till 3am. 6 hours tossing and turning, worried about being sick. Diamox is supposed to help you sleep as well. Hots were at 8:30, bagels and bacon at 9. Breakfast if I recall was given to us, so we officially got breakfast in bed. I didn’t tip the waiter though, I didn’t have any money.

There were a couple avalanches last night, one of the two big blocks on the side of the nearby hill fell and is now laid scattered across the fall zone.

Today is sunny and clearly is going to become warm. Great views as you can imagine.

I spent some time chatting with EWald about kids, politics, taxes, work and more. Found out the income tax in Austria is 50% and sales tax is 20%. Pretty insane, but at the same time, that’s what free health care and free universities cost. Worth it I bet.

I played Castlevania until around 3 and then laid down for a few. I got up for a snack at 3:45 and laid down again till 5:30 when the early dinner call came. I didn’t fall asleep. Dinner was soup and mac and cheese then I was back in the tent at 6:35. It got very cold and had a breeze. I presume the temperature is negative with the wind-chill. Base temp is probably 15.

The guides got in contact with Avery up at 17. They reached the summit and will swap tents with us. This lets us leave our tents at 11 and not have to carry them to 14. They will leave their tents at 14 setup (where they are resting after the summit) and we can just break down our tents when we leave 11 and leave them at their cache. Then they just pick ours up and carry them down. The perks of guided teams. We can climb to 14 and don’t have to setup our tents!

I headed to bed right away and slept really well.

Move to 14 Camp

Day 9:

Difficulty 10/10

I woke feeling more confident. Having been to 13,600 I felt going to 14,000 would be easy enough. We were up at 8:15am with the plan to leave at 11am. It’s about 20 degrees this morning and snowing. Avery’s team arrived at 10:15 just as we finished breaking down the tents. We asked how the summit push was, they said it wasn’t too hard. We all signed a book for Erin, one that one of the Nice brought with and said our goodbyes. She was joining Avery’s team and heading down, despite feeling a bit better.

We headed out, my pack balanced with 20 lbs in the pack and 15 or so in the sled. We weren’t sure if we’d pick up the cache or not, depends upon how the team is feeling when we arrive. I hoped we’d pick it up.

With the fresh snow, moving was a bit harder. Some places had 8 inches or more, some places that were more windswept had none. But I struggled hard. My lungs didn’t want to give me anything. Easily twice as hard as two days before, and I had a heavier pack then! I mentioned to Hannah that I couldn’t catch my breath and she said “We’re allowed to have bad days”. It was comforting that she wasn’t judging me, but I worried it could happen again. For me it was just like the first time I climbed Rainier, nearly impossible. Each break I could barely catch my breath before it was over. Each step required heavy breathing, lots of pressure breaths and just trying to keep up. I really felt like I must have Covid, why else could I not breathe? Was this the end of my trip? Would I have to stop at 14 camp? Would I not make a new high point? That all weighed on me. Worse was thinking about work, friends and most importantly, Claudia who were all watching me climb on my Garmin. I didn’t want to fail them.

We took breaks in the same locations as last time. It was cold on the Polo Fields and at Windy Corner. Then later when the sun came out it shot up to around 80+ degrees and we stripped most of our layers off to avoid sweating too badly. At our last break my Whatchamacallit bar was melted. Fortunately, a bit later the clouds came in and the temps dropped to around 50. A perfect temp for a base layer while doing hard work.

The team was wiped, so we skipped the cache. We reached camp around 5pm. With camp already setup by Avery, we were thrilled. I needed to lay down. I was beat. Dropping my pack onto the ground where the team stopped and walking to the tent required a break to catch my breath. Then walking the 20 feet back to my pack to get something required a break to catch my breath. It was rough.

The base of the tent was lumpy, as we expected. Avery’s team descended from 17 to sleep overnight and when you are as tired as they were, you just put up tents and go to sleep. No worries if the ground is lumpy. Fortunately for us, it was hot out. Once we rested enough to breath, we picked up the tents and stomped the snow to smooth it out. We re-anchored the tents and threw our sleeping gear in. For me it took a full hour to get my things into the tent and my inflatables setup. Each item required a break, making for very slow progress.

I asked people if they had cold meds in case what was going on was related. Yvonne had some cold pills that she hooked me up with. Thank you Yvonne! I took 2 and drank plenty of water. Of course, I ended up spending 2 days blowing my nose every 10 minutes. Maybe that helped though, I started feeling better 2 days later. Also, glad I brought 3 rolls of TP. Hannah kept telling me 1 was enough, but when you blow your nose on an entire roll, you are grateful. When you run out of TP, it’s a really shitty situation, when you bring too much, no worries. It weighs nothing, and we have room.

Dinner was at 6, ramen. It was my second time having it. Claudia made me ramen before I left since I knew I’d have to have some on the trip. I wanted to ensure I could eat it. Ya. I’m picky. It was fine, boring and I was slow to eat it, due to the elevation. It took twice as long to eat my meal here than it did at 11. I could definitely feel the altitude. Hannah said everyone feels crappy at this altitude, so it’s just something we have to accept. Kiira on the way to 14 said she *loves* this camp. It’s her favorite. A few days from now, I agreed. 14 camp rocks.

Naturally, I’m really excited to get to bed. My back and neck both hurt a lot from the climb, which happened on our first couple moves as well. Lights out at 7:50. I’m totally wiped.

Day 10:

Back-carry to cache

Difficulty 5/10

Today is our back-carry. We head back down to 13,600, dig up our cache and bring it back up to camp. I slept well, though around 11pm it too a while to get back to sleep. I felt really uncomfortable. We got about 4 inches of snow overnight.

We’re up at 8:10am with breakfast at 8:30. Captain crunch. Pretty good breakfast! Dan said during breakfast while trying to get some milk “will you milk me” to Kiira. There was a brief pause and they both started laughing. Quite a good slip there!

We left for the cache at 9:45 and arrived around 10:05. At 10:45 we headed back up and took a break after the large crevasse. I felt super strong, my breathing was back to 100% and my confidence restored.

When we got back we fixed the tents flooring more to flatten them out and more securely tie them down. I then took my first full bath (with a few baby wipes), changed my underware (yes, for the first time) and switched to my grey base layer shirt. I hate this shirt. First time wearing it. It’s too short on my arms and the thumb holes are uncomfortable. This shirt was only going to last me a couple days before I switched back to the stinky, but comfortable blue one.

I got the tent organized by 1pm. I’m starting to worry that my mat has a slow leak.

It’s snowing again, the clear skies have turned to overcast and the temps are comfortable. I’ve been sneezing for the last few days as well. My nose is raw, typical for everyone else from being burnt, but a bit worse for me since I’m blowing my nose so much.

I spent an hour and a half or so chatting with EWald about homes, old age and family. Snack time was around 3, more salami and Ritz. The snow is still coming down hard. I played some Castlevania from 4-6 and got to what is likely the end boss. Dinner came at 6, Hannah made burrito’s so they were amazing. She chatted about Minnesota (her home state) for a while.

Snow is still dumping. 8 more inches have fallen, enough to nearby bury my camp booties as I was walking around. Guides said that tonight whenever someone gets up to go to the bathroom, they should knock the snow off other people’s tents as well as their own. Around the edges of the tents we need to shovel snow away so that air can get in and out from the underside of the cover. Otherwise we could suffocate. It was a noisy night. 😛

At 7:30 I beat Castlevania Aria of Sorrow with the good ending, not the best ending. Then I loaded up one of my personal favorite games, Final Fantasy 6. I was definitely itching to play that one! While doing this I noticed a sticker on my Game Boy. You know those ones they put over logo’s and metal parts to protect them that you take off after buying the hardware? I missed one! So here I am 19 years after the release of the system, peeling off one of those stickers.

Bed time was at 9 and it was a very cold night due to the snow. It kept snowing overnight, but seemed to slow. I had a hard time sleeping and was up at 1. I played more Final Fantasy, but with the code and my breath (being snuggled into a sleeping bag with the hood up), the screen kept fogging up. I finally fell asleep around 2am. I figure it was 5 degrees overnight.

Day 11:

Today is our first rest day at 14 camp. We get to do nothing. 🙂

Pancake breakfast at 9:45, back in the tent by 11:15. I did some maintenance around the tents as we got about 1 foot of snow overnight. Later we watched a skier skin about half way up the headwall (towards the summit) and then ski, expertly, down. They did about 48 turns, which I got on video. It was a stunning, sunny day. Later on 5 more skiers were on the headwall, one I watched come down made 26 cuts.

Today at 2 we did need to do some quick training on fixed lines and running belays, so we were ready for tomorrow’s carry to 16,000 ft along the ridge. We got our harnesses on and had to setup our ice axes with some foam around the handle as above 17,000 feet the metal can get really cold and even with gloves on, get our hands cold. Nathan had some foam that he hooked me up with and I wrapped it around the handle and used my duct tape to secure it down.

The snow started coming down again, heavily.

We started training after the guides had a rope course setup. We attached our mechanical ascenders and Dan showed me how to put on hardshell pants while having a harness on. Turns out it’s easy, you unzip the hard-shell pants, keep them buttoned up and stick the carabiner out the front. Dan calls it a “caraweiner”. This guys hilarious!

We went down the training course, learning how to attach and detatch the ascender quickly and efficiently, with each hand. We refreshed ourselves on running belays, being reminded how to quickly unclip using the rope. Handy training.

Around this time a team was heading up the alternate right hand ridge route that takes you to 17. It’s steeper and also a good ski route. EWalds temperature gauge read 77 degrees in the tent, but with both flaps open it was nice and cool. I started pulling together food for the cache day and summit day. I wanted to be prepared. My cache contained my mits, down pants, overboots, 3 days of food and my 12 summit snacks. My cache I’d leave at 14k would have socks, hat, booties, helmet, dirty clothes, the grey base layer (that I quickly didn’t like wearing), my unopened Keebler cookies, bottles of meds (I took a few pills out of each and stuck them in a bag), my crampon bag, some leftover food, my charger and the Werther’s Originals I wasn’t bringing with.

I played Final Fantasy for an hour then rested till dinner. Dinner was Gumbo, rice and beef with cheese. It wasn’t terrible, but the rice on it’s own wasn’t good.

After that the clouds parted and everything became visible. Absolutely stunning. The temps were wonderful, the snow was fluffy and dry. Literally a winter wonderland on one of the most beautiful places on Earth. Many of us took this opportunity to go for a walk around the camp, I did too and enjoyed some peace and quiet while exploring the camp’s trails.

At 8:40 the sun dropped behind the West Buttress ridge and the temps dropped 30 degrees. Layers went on quickly, multiple layers. The water on the bottom of my overturned sled started cracking and popping within a minute. Each droplet instantly freezing. I made a video of it, really cool!

I was quickly back in the tent and after enjoying that “sunset”. I was too cold to play Final Fantasy and just headed straight to bed at 9. I just needed to find a way to warm my hands. They were freezing!

16k Cache

Day 12:

Difficulty: 9/10

Another nervous day. Will my lungs cooperate, will I be able to manage being at 16,000 feet? Today will be a new high point, so the unknowns weigh on me. Today we head up to the West Buttress, then along the ridge some distance to a cache location. Our goal is Washburns Thumb.

We’re up at 7:30am, it’s very cold, around 5 degrees. No wind. Breakfast was Cheerios and I ate a lot. I was fully packed and they woke us early in case we needed to finish stuff up. So I climbed back in the tent for 30 minutes with EWald because it was so cold and my toes were freezing.

9am we left camp, all roped up, cache in our bag, crampons on and layers on. I don’t recall my pack weight, but I bet it was around 25 lbs. Our first break was probably around 11am around 14,800 feet. Our second was at the bottom of the fixed lines at 12:45 and 15,600 feet. I felt good going through this, my breathing was good despite the steep approach that never really let up. It remained very cold until this fixed line break, right at the Bergschrund and right at a pretty steep spot. We threw on a clove hitch on our ice axe and drove the handle deep into the snow. Then the sun came out and it was 80 degrees. My candy bar was fully melted. Layers came off. Crazy how Denali is!

Looking down at 14 camp was surreal. We were so far above it. You could see the tents, the empty camp locations and all the trails around camp, even one one to the “Poop Crevasse”. It looked like a little city as seen from an airplane. I loved seeing it from here, so cool looking!

The fixed lines were next. They comprise of about 600-800 feet of ropes, each around 100 feet long or so. Each rope is fixed on each end, with pickets in intervals as needed. This is where our mechanical ascenders shine and prove that they are worth the cost. We each call out “anchor” as we reach one, open the ascender (with our mid-weight gloves on), hook it on the rope, close it, then throw the non-locking carabiner back on the end of it to prevent the rope from being able to pop out. Then we step, slide the ascender, step, slide, step.

This goes on for the next 2 hours as we slowly work our way up what is a 50-60 degree slope. Each step is a wide step with our toes out, each one with a strong breath accompanying it. It’s slow moving, and hard. This is easily the hardest part of the climb, and maybe the hardest on Denali. It is fun, as my friend said it was. Some of the parts are snowy, some are icy and hard to get the crampons to dig into. Slips pull at the person ahead and with Dan leading, we kept up with the “hurry up and wait” pattern he likes to follow. Several times he was less than 5 feet from the person in front of him, a single slip of their feet could have a crampon going into his face. I was pretty frustrated about this and didn’t appreciate his lack of carefulness. Fortunately, things went ok. I just got exhausted from constantly catching back up to them, so we could stand there and wait for the team ahead of us.

My breathing on the fixed lines was tough, but I realize I was likely using my arms too much to pull me up. The fixed lines require some practice to prevent that habit, which is easy for me to do wrong being a climber. The lines seemed to never end. I could see the top, then got there and realized the hill just tapered off, more lines going. Then I saw the top at the ridge, but they kept going, bending right. Fortunately that was the last one, and we were finally there, at the top, on the West Buttress for the first time.

The ridge was narrow and we were hungry. We plopped down and pulled out our snacks. It was 2pm, it felt like longer. Lots of pictures were taken. The skies were perfectly clear and afforded incredible views of Mt Hunter, Mt Foraker, 14 camp and the valleys to the north. It was still warm here, but pretty windy so we threw on our hard-shells. While on break, the rangers showed up, a team of 5 or 6 of them arriving and they started digging a cache just above where we sat under the rocks. They seemed pretty wiped.

With our goal of Washburns Thumb, we continued up. The wind tore at us as we pushed upwards. There was another set of running belays that we had to follow. 3 or so anchors that curved around a large rock. It was pretty steep and very exposed. Working around the rock was a bit more work, but not hard.

Due to the wind reaching up to 30mph, we stopped just above that at 3pm and dug our cache at 16,400 feet. We huddled facing away from the wind, eating snacks, drinking water and putting on sunscreen. Even with the heat, it felt like 30 degrees due tot he wind. We kept our layers on as we descended.

When we reached the fixed lines we reversed the team order as well as shortening our rope intervals from 5 arm lengths to 4. We wouldn’t use our ascenders here, only the guide will, taking up the rear and using an awkward hand position to hold the ascender so that he can safely lower it as he steps, but if he falls, it will auto-lock onto the rope. I can’t imagine that was fun for the guides. For us, we were told to put on our least favorite jacket. In my case, I went with my hard-shell as it was already on, it’s my oldest and despite the fact that I love it, my other jackets were all new and easy to damage. The reason, we wrap our arm around the rope and use the friction of the rope to slow our descent as we head back down the 50-60 degree slope. It’s steep, but the arm wrap is solid and easy to feel safe with. For me though it really started feeling like it could break my arm. Not literally, but it was a lot of force on it. I liked the security, but after 600 feet I was pretty much done with it! I found holding my arm straight ahead instead of letting it bend 90 degrees to my side helped with this pressure. If I recall, I did slip once. EWald I should note, for a guy in his mid-50’s is very sure footed and quick. He hauled ass down the fixed lines! We were quick and efficient at each anchor. Much easier for us than it was for Dan, but his experience helped.

The clouds came in as we went down and it became a whiteout. We took our next break at the bottom of the fixed lines around 4:15pm. Once again we plunged our ice axes into the snow and carefully took our packs off so we could find a place to set them. It’s steep here if you recall! Kiira’s team comprised of Nick, Nathan and Yvonne were behind us and overall moved much slower and more carefully. When we reached the bottom of the fixed lines, we had to wait 15 minutes or so for them to arrive. With the whiteout, we needed them in sight before we could continue. It felt like 70 degrees at the break, layers came off, but I think I still had left my hard-shells on for a while. I recall later on (or days later on this same section) Hannah saying “who is regretting their layers” when we were left the next break and everyone raised their hand. We took an extra stop to lose layers quick that time. 😛

We descended the headwall quickly, plunge stepping or side stepping our way down The snow was soft with the heat and it felt like any other descent in Washington state. Mashed potatoes snow and tons of heat. Terrible and never fun. I also really had to pee. We reached camp around 5pm, though my notes say we returned around 4, so the times above may be slightly off.

I was wiped, it was a very very hard day. But, it was a new high point for me! About 16,200 feet! Checking the temperature gauge EWald had in the tent showed 118 degrees. Not a great place to rest. My chocolate bars surprisingly weren’t melted! They were in a bag on the floor of the tent, so the snow under them kept them cool enough! I opened the tent flaps and hoped for a breeze. 20 minutes later we got one and more flurries.

I overheard one of our climbers being given “the talk” by Kiira. The climber was told they needed to be taking care of themselves better, something I’d been hearing repeatedly during the trip and now that we were at serious altitude, the guide wouldn’t accept anymore. To go to 17 camp you have to EARN it as the guides always told us. You have to show up in good form, able to make camp, eat, drink and help as needed. This climber was close to not earning a chance to continue. The same rules apply for a summit day. You have to earn it. Each break you take your pack off, pull out food and water, a layer, sit down, eat, drink, put on sunscreen. Not enough of this was going on. They said they were still full from the previous break, but the guides wouldn’t have it. I agree. We churn through calories up here, I’m still full too, but skipping food at a break always ends badly. They had eaten 400 calories, total at the climb breaks. The expectation was 800-900 to help compensate for the 2,000 we burnt. The climber will hopefully fix these issues, but they talked about the summit not being important and another climber said that they came all this way, they need to take it seriously. I felt at that point that they would not summit. Denali is hard, you have to really dig deep.

With the breeze, I’m finally cooling off. It feels nice. Tomorrow’s rest day will be amazing, I’m looking forward to doing nothing at all! I checked my resting heart rate, 90. Altitude is crazy. I rested for 2 hours, nodding off a bit. Dinner came at 6:30, Indian Lentils Madras. Another food Kiira said I should try. I did, it was ok, though I was full enough at the end of it that the last bite triggered my gag reflex. I don’t plan to eat that again.

Then the guides gave “the speech”. From here on out, everyone needs to perform and it will get harder. I was paranoid and after everyone left I asked how I was doing. The guides had no concerns. I mentioned to Hannah in private Dan’s inconsistent pace, the “hurry up and wait” and she said that she’ll mention it to him, he’s new and has some learning to do. I think he did improve, but there was still plenty of it happening after this, but less. Habits can be hard to break. I didn’t hold it against him, I’m the same way, I love rushing.

I was back in the tent by 8 and while I wanted to go to bed, I wanted to first finish the Phantom Train in Final Fantasy. I ended up playing until I got Gau and headed to bed at 9.

Day 13:

Rest day.

I slept well, waking at 9:30. I heard Hannah talking and realized it wasn’t cold and my back was sore. Time to get up. Breakfast burritos were on the menu, with bacon. I got super full and then waited in a long queue for the bathroom. Speaking of bathrooms, camps have a lot of them, there is always one near camp. We had claimed an existing one and had our CMC setup and the two pee holes nearby, one for men, one for women. Separate ones we found to be necessary to ensure everyone has a good place to stand. Pee spreads.

Today we get to go to the “edge of the world”. It’s a cliff to the South past the research tents about a 10 minute walk from camp with stunning views of the valley 4,000 feet below us. We threw our harnesses on and grabbed our ice axes and headed out. Clouds came in part way through the photo shoots, but I got to go first and got a mostly unobstructed view and good pictures. This cliff overlooks the “valley of death”, called this due tot he crevasses and rockfall danger for those climbers heading along that route.

Each person, including the guides took their turns on the edge. A helicopter circled overhead, looking to land at the research tents, but the clouds just dense enough to make it unsafe. It finally flew away. If I recall, it returned later and landed.

We headed back and it was time to chill. The temps are nice, we’re in clouds/overcast with light flurries. As always, this keeps the tent below 70 degrees. I still have a headache, sneeze and a runny nose, but most people seem to have runny noses due to the cold. I feel stressed through, tomorrow is the start of the hardest days on the mountain and I need to ensure my lungs don’t fail me.

Two more days of uphill and we’re at the summit. Then two hard downhill days.

Naturally, I spent some time today packing for the move. I took meds out of containers and put them into one, just as many as I’d need. I jotted down notes on what the numbers on the meds were, so I didn’t have to remember. I ensured I had a face mask in an accessible location and my snacks in the brain of my pack. I had this packing list figured out by 1:30.

Snack came after this, the rest of my cheese was eaten to finish up my opened Ritz packages. This took an hour to finish.

Shortly after this the RMI group that started 1 week after us arrived at 14 to cache their gear. One of their guides was my guide on Rainier last year, via the Emmons Glacier, Ben Luedtke! Small world! He was there when I was accepted for Denali and he was thrilled to see me. It was surprising actually, I’d expected him to be like “oh hey man, glad you made it”. Instead he embraced me in a strong hug and was genuinely excited I was there. That felt nice. It was good to see a familiar face, it felt more like home.

After the reunion I played some Final Fantasy until 4:30, took a quick pee break and then played more. I got to Figaro, moving under the mountains with room in my party for Shadow (a decision I later regretted when he….left, I forgot about that).

Dinner was at 6, ravioli style noodles (no cheese though…). Kiira found the other teams hoola-hoop and was satisfied with 2.5 minutes without stopping. I hung out with the team outside for an hour enjoying the last of the pretty nice weather, though clouds obscured the far mountains, they still looked amazing.

Dan changed our CMC for us, and as such, everyone lined up for the bathroom. A total of 6-7 people used it. Total madhouse. I settled into the tent and finalized my gear for 17 vs. what I will cache. EWald said he’d bring his battery pack, so I left mine. In the end, neither of us even used it. I played more Final Fantasy to reach Jidoor north of the Opera House, on my way to Zozo. I headed to bed at 8:30.

Move to 17

Day 14:

Difficulty 9/10

Up at 6:30am and left camp after 9. Reached the fixed lines around 11:45 for our first break and the ridge by 1:15. It was a nice day out, clear, low winds and warm. I climbed all the way to 17 in just a base layer and a light jacket. By far not what I expected up here!

We hit the top of Washburns Thumb at 2:15 where we took our second break. The move around Washburns Thumb was another set of fixed lines. Just 4 clips, but most of them short, so the 50 or so foot climb was quick. It was a bit awkward working our way around the rock, but still just fine.

I enjoyed this ridge route. The trail was narrow, with a few running belays here and there for safety. To our left was a valley, not far below, sliding away 1,000 feet and down to the West behind us. To our right was the drop to 14 camp over 2,000 feet below us, and great views of the camp the entire way.

I’m pretty sure there was one more break along the ridge, then reaching camp by 4:45. Up here it was getting windy and colder so I threw on my Bight jacket, a mid/heavy layer. RMI caches, year round, tents at 17 camp so climbers can leave their tents setup at 14 and not have to carry them up. Very nice! I saw the rangers as we arrived at 17 resting near a permanent structure, looked like one of those green electrical boxes you see in subdivisions. They didn’t stay the night, but were there to acclimatize.

However, they told us 3 person tents were cached, but they were actually 4 person tents. The team wanted to stick with the existing ratio, 2 or 3 people per tent. The guides said no, we needed 4 in each to help stay warm. We were pretty angry about that (and it wasn’t really that cold anyway), but I understood. At 17, anything can happen. We camped with the Jasons.

We setup our tents quickly, finding a couple of already made homes with walls built that fit our tents. The ground was pretty terrible, so we chopped some snow to try to flatten it out. Didn’t work very well, I ended up with a slope under me that was unable to be smoothed out in a reasonable timeframe.

You’d think at 17,000 feet that the tents would be cooler. Nope, we’re even closer to the sun with even less air density. It was even hotter in the tent.

Overall, the climb went really well. It was hard of course, but my breathing was strong and I felt good coming into camp. For sure made camp “in style” as the guides would say.

While we setup camp and rested, the guides chatted with IMG, who hit 17 camp just before we did. They worked out carabiners for everyone, pickets, etc. IMG said they’d head up first, so Hannah came around and asked all of us for our non-locking carabiners. She needed 20 and she gave all of them to IMG with the promise that they’ll use them on the running belays on the way up, and we’d clean them on the way down. In the end I only got 2 back.

Diner was at 6. No more proper hots, and everyone has to get it quickly. It takes too long to boil water up here, so our water was warm towards hot No more cooked meals, not it’s just Mountain House meals, and of course, not ones I really liked. The water wasn’t hot enough to really cook the meal, so even after waiting an extra 5 minutes to eat, most of the noodles were still crunchy. But, carbs are carbs.

The guides confirmed that tomorrow is our best window to summit. After that it’s 3-4 days before another opportunity and no one wants to spend more than one night at 17. Looking up at our route the start looked easy enough. Up a slow hill then traverse along to Denali Pass. It didn’t look like more than 500 feet or so, but this is Denali, everything is bigger here. The pass is 1,300 feet above us.

I got all my stuff setup for tomorrow, including getting my overboots put onto my boots to keep me from getting frostbitten toes tomorrow. Those are always really tough to get on. I had my pack with the appropriate gear, my crampons and ice axe laid out for tomorrow and my 12 summit snacks, pretty much just a bunch of candy bars. I was back in the tent by 7:45, though climbing in was difficult with it being so cramped.

It was ridiculously cramped, shoulder to shoulder and very uncomfortable. It was almost impossible to get into the tent without kicking someone. I slept on the right side and unfortunately got stuck with a slope under me, so I rolled into EWald all night. I propped some clothes under the edge of my mat so I rolled a bit less forcefully. I figured I’ll just lay down and hope to sleep. Wakeup was 7:15, so that we could have some warmth when we start up. This for me was almost impossible. The tent was hell for me. I nearly had a panic attack due to being so confined. My face was nearly up against the side of the tent, the pockets above me were full so my head was just barely under them. I felt like I was being smothered. Rolling into EWald was frustrating. In the middle of the night I got up and swapped my sleeping pad and inflatable so that I woldn’t slide as much, it helped for sure. That was a pro tip from Kiira, put your inflatable on the bottom since your sleeping bag won’t slide as well on the foam pad. I somehow fell asleep by 1am and got 5-6 hours of very broken sleep. I don’t know how I made it through the night.

In the morning I told the team, if we are sleeping another night here, I’m sleeping in another spot tomorrow. Spoilers, EWald took that and seemed to sleep fine. I was much better where he was tonight.

Summit Day

Day 15:

Difficulty: 5 to 10 depending upon where we were

I woke “naturally” at 7:15 am. The rest of the team got up around 7:30 and the guides came by at 8. I mentioned how terrible it was sleeping there and EWald smiled and called it a “5 star hotel”. He and I had made many such jokes at various camps over the last 2 weeks.

It was a beautiful morning, warm and sunny and no wind. I could tell it was going to be a great day! I can’t imagine being up here with “typical” Denali weather, cold, windy, cloudy. I’d have hated this day more than almost any.

Breakfast was at 8:30. Oatmeal. Bleh. But carbs are carbs. I was packed and ready to go at 10am. Like I had last night, I headed over to the edge of 17 camp to enjoy the view. The team was roped up and ready to roll out at 10:30. My pack weight was 15-20 lbs and I was carrying a shovel and an emergency sleeping bag.

IMG headed out first, as promised. We waited about an hour and started up after them. The first section up to Denali Pass was called the Autobahn. It sucked. I estimate about 40 clips. We walk 10-15 feet and someone on the rope team reaches a clip and calls “anchor”. We all stop, they unclip if needed, then clip if needed. We’re wearing our heavy gloves, so this is more awkward and for the most part, each clip is problematic on the way up, which takes longer. They finish clipping and call “climbing” and we all start walking again. This goes on for 2 hours.

Since Dan, EWald and I are on one rope, we have less people (as the other ropes have 4) and we have to always have one member of the team on a picket so if one person falls and the other can’t catch them, we’re still safe. As such, Dan has a 3 extra pickets (he uses them all) so that when we reach the end of the rope, he has to stop and put in a picket and then we can continue. On top of this, he had to extend his interval from me 5-6 times to be able to reach other pickets without having to use more. In the end his interval was 80+ feet. Naturally, Dan likes to go fast and he hurries to catch up tot he team ahead of us (Hannah’s team). This is exhausting, especially at 18,000 feet.

Most of this trail is about 1 foot wide (as in, your physical boot width) and about a 30-35 degree slope down to the left. Some spots are wider, about 2 boot widths. It’s hard snow, a fall would make it hard to catch yourself as you would accelerate quickly. This is one place were climbers die, they may decide to be lazy and not clip in. Bad idea. Clipping is slow and annoying, but those pickets are there to keep us alive. After 2 weeks, we can take the time to clip.

Our first break came at Denali Pass at 1:45 pm, 18,300 feet. This was quite a bit from our previous drink and food break so we were hungry. Unfortunately, big Jason (Jason M) wasn’t feeling well. The guides talked with him about what he was feeling, and him being a doctor was also able to self access. HAPE. His trip is over. HAPE is “High-Altitude Pulmonary Edema”, which is a condition that happens at high altitude where your lungs fill with fluid. If you don’t turn back immediately, you could die. This can happen to anyone, it’s a roll of the dice. Both Jason’s had summited Aconcagua together years before, 4,000 feet higher than Denali, so physically they knew they were able to do this. However, HAPE comes when it wants to.

Jason N said that Jason M was talking about how he wasn’t feeling well on the way to 17 camp, but felt better at camp. He noticed on the way to Denali Pass that Jason M wasn’t feeling himself. He was making mistakes he normally wouldn’t. Now that he was at 18,300 feet he knew he couldn’t continue. Normally RMI will turn back whichever guide has the least experience. In this case that would be Dan. However, HAPE is life threatening and Kiira had more medical training, so she turned back with him.

The teams were reshuffled. Hannah became my guide with Inga, me and then EWald on the rope. Dan took Jason N, Nathan, Yvonne and Nick. I was happy to be on Hannah’s team, she’s much more consistent with her pace and we got the lead.

Next up was a more gentle slope heading up along the ridge and around a rock. The next destination to pass is Archdeacons Tower at 19,500 feet. This section remained nice, the weather is excellent and it felt warm. Some wind did kick up and we climbed with our hard-shells on. During this time one of the climbers started to doubt themselves. Wondering why they like to challenge themselves.

We took a break around 2:45 pm at 18,900 feet and that climber had a really hard time catching their breath. I was grateful that my breathing was 100% today. I did notice the climber’s pressure breaths, which they said they were taking I couldn’t hear. That suggested to me that they weren’t strong enough to be pushing enough air out of their lungs. I’m not experienced really, but all the guides taught pressure breaths as being a strong forceful push out of air. It should be loud. I never heard them. The guide offered words of encouragement, as did I to help them along. I knew they could make it. The air is thin here, we were all struggling.

We got an update somewhere along here from Kiira saying she had reached 17 camp with Jason and they were resting then moving to 14 camp. The rangers were alerted and helping.

The mountain seemed to never end. The hills just kept coming, and took forever to get over. But we eventually passed Archdeacons Tower and looked down a small slope to the Football Fields. We stopped in the middle around 4:30 pm for our final snack break before the summit push. The Football Fields are a short section a few hundred yards long of totally flat terrain. A nice break before the summit push. The final hill to the summit ridge was tough and steep. More running belays up the hill. Reaching the top afforded us a great view of the summit, or at least, the cloud cap on it. A cloud cap. Really!? All this way to have a 100 foot tall cloud sitting right on the summit.

Ahead of us we could see the final ridgeline, a sharp edge of snow with a 1,000 foot slope to the left and an 8,000 foot drop to the right. That was quite the view! Ahead of us were the final running belays, and IMG. We could see them starting down from the summit and Hannah said we have to wait for them to come down. Running belays are a 1-way street.

Just as their 3rd team passed us, the cloud on the summit cleared. I was in shock, how rare is it to stand on Denali with such clear views!? We headed up with excitement while carefully working our way along the running belays, switching sides as we crossed the ridge to avoid exposure to the large drop. It was windy, maybe 20 mph winds, so we kept our faces protected.

And then, at around 7:15 pm, finally, after 15 days and 13,000 feet I stepped onto the top of Denali, standing at 20,310 feet. Years of dreaming to be here, thinking it would never happen and here I was. I had reached the top of North America’s tallest peak and to top it all off, the skies were clear. It was incredible. Beauty and heights I’d never been able to imagine. I looked down at Mt Hunter, the 14,500 foot “Rainier” our plane landed next to and it was a tiny hill. It looked like Little Si when standing on top of Mt Si. I never thought I’d be here, but here I was. It was hard to imagine that this was really happening.

But, as much as I wanted to stand there and take it all in, we were in rush mode. I pulled out my phone and took tons of pictures. EWald took a few of me, I took a few of him. I pulled out my New World flag (the game I’m developing at Amazon) that I brought with and got pictures with that. Then the team did a quick photo set from a group of 3 that reached the summit a few minutes after us. Hannah took her usual picture with a photo of her Mom. Then Hannah and Inga held up a trans pride flag, I believe in honor of Erin. I didn’t know what it was at the time, EWald theorized it was the Italian flag, but the colors looked wrong. When I got home and showed it to a friend, they confirmed what it was. Very cool!

Crazy as it may sound, I never needed my puffy pants, never put on my parka and never needed my mits (though those are more rarely needed). It was pretty comfortable on the summit!

There was a rumor going on that one of the climbers in this group of 3 was 13 years old. However, others later refuted that claim and said the person was just short. We never found out.

We stood on top of Denali for 20 minutes. It’s always weird to think that you spend 2 weeks on a mountain to spend 20 minutes on top. I realized it costs about $750 a minute, 6 months of 2 hours a day training and a month of your vacation time to stand there. A very unique opportunity on a very remote mountain.

After a quick snack and water break we headed down. We moved quickly and efficiently, though some of the climbers were really slow with clipping and unclipping running belays. Likely exhaustion was kicking in. We took another break on the Football Fields, and if I recall only one more at the top of Denali Pass around 9:30 pm. The final move down the Autobahn was very slow due to the slow clipping and it felt like it would never end. It took about 1.5 hours to get down just that section. We reached camp at 10:54 pm.

Dinner came shortly after, more Mountain House meals that didn’t cook fully. I had mac and cheese. Most everyone was totally wiped, but I never seem to run out of energy so I went around camp helping to gather food and dinners for people. It took a while for me to get into the tent and it was very cold by the time I did. I did get a great sunset picture though from the edge of camp!

The guides finally got an update on Jason. Kiira managed to get him to 14 camp. It required some sections of lowering him as I recall hearing to get him safely down the fixed lines, and they only just reached 14 camp when we got back to 17. That must have been brutal. Kudo’s to Kiira for safely getting him down. As I recall, the next morning is when he was picked up by helicopter and flown out.

EWald had a flask of Zaya rum, which he broke out at 14 camp, only to find it leaking. He tried to fix it, but here at 17 camp it showed more leaking. He’d planned to bring it to the summit, but I don’t think he ended up doing it. I enjoyed a small pour, thanks EWald!

Fortunately, as mentioned earlier, EWald was willing to swap spots in the tent with me. I’m still grateful for that, thanks EWald! He slept well, I slept much better than last night. Still cramped, but I wasn’t having panic attacks since I was in the middle and had room above my head. Bed time was at 11:50 pm for me.

In total it took us 8 hours to reach the summit and 3.5 hours to get back to camp. Normally teams would summit, pack the tents and head back to 14 to rest, then the following day head to basecamp. However, the team was wiped and we had moved slower than normal, so we opted to camp at 17 and tomorrow go down to 14, rest of “a night” and then go the rest of the way down to basecamp.

Overall, it took a lot for me to be willing to keep walking. Each step was hard, each step told me, “just give up man”. But I refused. I’d come all this way, we had good weather, lets summit. While I did climb this for myself, the true reason was for Claudia. I hope this climb helps inspire her to overcome obstacles in her path.

Return to 14 Camp

Day 16:

We’re up at 7:45, hots at 8:15 and I’m packed by 9. It’s cold and windy out, so we waited in our tent until the guides told us to break them down. The plan is to leave at 10:30. I played Final Fantasy for 30 minutes to kill the time. It’s another nice day.

My toes were cold, very cold, so I stuffed them in my heavy weight gloves. Quite hilarious to see feet with gloves on. Then, as the tent warms, it starts snowing in the tent because the frost inside the tent (a nightly thing on Denali) starts falling off.

Once the guides called for the tents to be broken down, we packed it away and got our stuff finalized to head down. As always, we split up the food to carry down. Someone didn’t seem to grab one, but everyone said they did, so I took the extra. Turns out there were two small bags or one big bag to take. Someone took a small bag without taking the second. We were ready to leave by 10:35.

I counted the pickets along the ridgeline on the way down, roughly 20. Then we went down Washburns Thumb, which was steep and I found harder to go down than up. We reached the fixed lines and it was very hot and below the fixed lines, very very hot. Easily 70+ degrees.

Fortunately, the other RMI team behind us had reached 14 camp and were notified we were coming. They had taken over our tents, but setup other ones for us so when we arrived, we could just climb in and sleep. Thank you everyone!

We arrived around 3:30 pm and found that they had also dug up our cache and have water ready for us to fill our bottles with! I passed around my bag of Brussels cookies (which I’d planned to share at the summit) and everyone was very happy to have some! Got to give back you know. Now here we are, on the downhill, our trip is done. We have a lot of extra fuel and food. The guides assess what is up at 17, what the next RMI team has and determine what to give to them and what to take down with us. I did some digging and finally found my lip balm and light gloves. It’s easy to lose track of this stuff after a climb and when it’s cold in the morning.

The other RMI team had a pretty good hangout spot. They were kicked back, listening to music in the sun on their mats. Their hoola-hoop was standing behind them in the snow, ready for it’s summit attempt and successful 30 second hoola-hoop at 20,310 feet a few days later, a possible world record. I called it “beach weather”. It was nice out. This whole moment felt like a community. Everyone chatting, friends on different teams saying hi, happy to see each other, embracing each other. It felt wonderful and has been etched into my memories.

Someone from another group came around camp asking if anyone had extra toilet paper. I couldn’t help but think of Hannah saying not to bring more than 1. I still had 2 rolls unused. As always, when you need TP, you need it. I waved my arm out the tent and called out that I have some. I gave him a roll and he was very grateful.

Dinner came at 5:15, breakfast burritos because why not! After that I was back in the tent where I played Final Fantasy for a bit and got to the opera house. I wanted to play that scene, but needed to rest. Thinking back, I should have played it, what better place to see the best scene in video game history!

The other RMI team finally ended their party around 8, they sounded like drunks at a bar. Definitely sounded like a fun team!

We are at 14 camp to rest, then we get up at 10pm….today. It’s hot in the tents of course and we’re just here for a few hours, so I left the sleeping bag packed. I pulled out just the foam mat and inflatable and over the next few hours I threw a few layers on as the sun dropped behind the mountain. I finally put on my parka, which I think I just stuck my legs through the sleeves of. I was a bit chilly by the time we got up, but it was fine.

Descent Day

Day “17”:

Today was a whirlwind, and much more interesting than it should have been. Read on.

We are up at 9:50 pm, on day 16 technically. I finished my crackers and salami by 10:30. We had sleds ready by 11:40. We knew the downhill was going to be tough with the sleds, especially Squirrel Hill and Motorcycle Hill. It was worse than expected, especially for EWald since he was in back.

The initial downhill towards the 13,600 cache site was easy enough, then along to Windy Corner. Going down the Polo Fields was easy enough, then across the end to Squirrel Hill had us losing track of where the path was due to fresh snow and winds. Then we hit Squirrel Hill, the first true obstacle, and one that was far harder than everyone on the team expected. Our sleds were heavy and they slide down the hill. Generally the person in the back has to control the sled ahead of them, so I’m pulling on Dan’s sled to keep it from sliding down past him and EWald is pulling on mine. But EWald has a sled too, and his is also heavy. I bet EWald had a pack of 20 lbs, a sled of 30 lbs with my sled of 30 lbs. All this while trying not to fall downhill on sketchy steep terrain. He barely made it and was quite cranky we were doing this kind of sled setup. I agreed. He recommended doing what he did on Vinson Masiff, attach your own sled ahead of you so it’s going downhill.

We heard yelling ahead of us, lots of it. Kiira’s team was also struggling and getting angry with each other. My sled kept rolling on us, it was too top heavy. We stopped a couple times to reorganize my pack a bit, but in the end as we neared the bottom of the hill, we gave up and dragged it through the snow. With the steep hill, this acted as a perfect anchor and EWald didn’t have to control my sled anymore.

At the bottom of Squirrel Hill we redid our sled hookups so the sled was no longer attached to the rope behind us. This let us “walk the dog” down the Motorcycle Hill, which was vastly superior and allowed us to only have to deal with our own sleds.

Kiira’s team quickly dug the cache at 11 camp and it was ready by the time we arrived around 2:45 am. We also packed away the tents the other RMI team left for us (since we swapped with them). We were here for 45 minutes or so as we needed bathroom breaks, switching to snowshoes, getting our poles, putting away crampons and packing the stuff from the cache. The guides also chatted with the RMI team here at 11. But, we found a treasure in the cache! Jason M brought homemade cookies, chocolate chip! They were almost identical to my Mom’s, delicious! Thank you Jason, sorry you weren’t able to be there with us to enjoy them. 😦

Just as we were ready to leave IMG headed out, always just a step ahead of us. We waited a bit to give them some space and then we headed out, back on snowshoes, working our way down to 7,800 camp. The initial big hill wasn’t as bad as expected and the sleds acted better. As we reached the bottom of the first hill, we heard there was a possible crevasse fall on the IMG team. We kept walking and eventually ran into them. A rope team worth of them was sitting on the side of the trail, one person laying down. The rest of the team it seems had kept going. It was strange to me that they’d split up, and it didn’t look like there was a crevasse fall. We found out later that one of the clients had tripped over their snowshoes and broken their wrist. On Denali, that means you walk out as it’s not “life, limb or eyesight”. That sucks. I heard they hung out here for a few hours while someone on the team helped to splint the wrist before they could continue on.

We reached 7800 camp around 6 am. The climb to reach this took longer than I remembered. Once again, the hills rolled on and on. We had to zig zag a few times to get through camp due to crevasses that had opened up. The guides spent quite a bit of time searching for the cache, it was finally found as the LAST set of flags marking caches. Of course it was. It was surreal being here, it was a total ghost town. Nothing but empty campsites and flags marking caches, almost like a graveyard. Not a soul in sight. I even passed a bird, dead in the snow and melted part way down into it. That was a reminder of the stories the guides told of birds making it up here (lost in the clouds) and not being able to make it back down.

Cache dug up and packed, largely trash and poo, we headed down the Kahiltna Glacier for our final long downhill of the climb. I thought we’d have to zig zag a lot to get around the crevasses, but it wasn’t as bad as I thought. Most of it seemed to be closer to 7800 camp. I think we took a total of 3 breaks along this stretch before reaching Heartbreak Hill, our final climb before of the trip.

At this point it was hot, we were in base layers and the snow was soft.

The Fall

That’s when it happened.

We had just left our final break, for the final 1.5 hours or so up Heartbreak Hill. We started hearing members of Hannah’s team call out. We stopped to listen and heard “Hannah fell in a crevasse!”. Dan rushed ahead, EWald and I double timing it up the 100 yards to catch up to their team. We arrived and Dan had us drop to the snow as he approached the crevasse edge. We slid along as needed to give him a taunt rope as he got close enough to call out to Hannah. I could hear Hannah crying out.

Fairly quickly he started rushing about, digging at the snow, calling down to her that he’s going to get her out. Hannah’s team was lined up with mine next to me, laying on the ground as well. Shortly after Kiira arrived and they did the same. We lay there, nervous about what was going on. The guides rushing frantically, setting up anchor’s and digging at the snow. Every one in a while Dan would tell Hannah’s team to pull. They’d get on all 4’s and pull as hard as they could. It was nearly impossible though, with snowshoes on they couldn’t get much traction on the soft snow. I was in front, and lay there curious, why weren’t they setting up a pulley system?

Hannah’s team was visibly getting exhausted and they were nervous as well. Eventually Dan extended his rope to lower it to Hannah in order to hook our team up to her as well. Then when he called out, both teams would pull. Still no luck. At this point we were 20+ minutes in. While the guides did their thing, we prepared as well. We pulled our snowshoes off and setup a prussik to our rope so the 3rd team could also help and we could ensure we didn’t lose ground. We then pulled out extra layers, knowing Hannah would come out cold.

We did a few more hard pulls and eventually, she made it to the surface. I looked up and saw her standing there, shivering badly. Only then did we find out that she’d fallen into a pool of water, up to her neck. The guides helped her out of her layers and we threw dry layers to her. I threw my puffy pants, which I’d yet to wear, tag on it even, someone else threw her a parka. She got all of those layers on and the guides spent another 20 minutes or so getting her stabilized.

They split up Hannah’s gear amongst us to lighten hear load for Heartbreak Hill. I took several items and stuffed them in my sled.

Another 20 minutes later, Kiira lead the team up Heartbreak Hill (because Hannah didn’t want to fall in again), Hannah following with her team. If you are curious how badass mountain guides are, now you know. Hannah brought her team up to base camp after falling in a crevasse!

We reached our base camp cache at noon. Heartbreak Hill was certainly a slog! Even worse with the heat. It was another 70+ degree day. I took 4 bags of 50 count hot chocolate and tea as Dan is wiped from the rescue. I could tell he was, as he took no effort catching back up to Hannah and ended up falling behind. Each bag was around 4 lbs. Once finished, I estimated my sled at 80 lbs and my pack at 50. With that we made our final climb to reach the new airport, about another 100 feet up the glacier from where we landed before due to the melting snow causing crevasses to start showing up. By this point I noticed my ankle was hurting, just like it did on the approach. These plastic boots are great uphill, but not so good on the flats.

We arrived at the “air strip” by 1:10 pm and threw our gear in a pile. It was unknown how long it would take for planes to arrive. The guides talked with the basecamp manager to let Talkeetna know we were there waiting. They found out Talkeetna is under clouds, and our planes don’t have instrument flight. Great. 3 other planes arrived to pick up IMG around 3pm. Dammit, they are always just ahead of us. The guides said if the weather doesn’t improve, we’ll have to stay the night.

It was hot, we had base layers on and with the direct sun, light gloves and buffs to prevent ourselves from getting burnt. The guides cached some cans of beers at basecamp, Alaska Kolsch, perfect for a sunny warm day. I cached 2 airplane flights of Rum and a bottle of Fireball (not a big fan, but it was the most sharable stuff in the liquor store in Talkeetna). Erin had a bottle of, I think a Rye that EWald opened and brought home. I enjoyed two of the beers and then most of my water with some snacks.

Hannah told her story of what happened in the crevasse. When she fell in she was up to her shoulders in the water. The rope was dug deeply into the snow above her. She grasped for anything she could hold onto. She could feel something under her that she was standing on, but putting her foot out towards the middle of the crevasse she couldn’t feel anything and was scared to try putting her foot down. It was likely 50-100 feet deep. If she’d fallen off her ledge, she could have drowned with the heavy pack on and gear.

Within 10 minutes or so she was able to get herself on a small snow ledge which got her most of the way out of the water. She was still soaked though, her layers providing no warmth. As we were trying to pull her, she was just being pressed up against the snow overhang of the crevasse. She got her prussiks setup, but still couldn’t climb out. She dug a trench in the snow and got carabiners setup, but her hands were cold. Her pack was creating a choke point for being able to get out and it was sideways. She managed to get her pack removed and the whole pack was in the water. Her hands were losing feeling and her feet were going numb.

She said it was light in there, and 5-7 feet wide, but the snow was super crumbly. She wasn’t able to get much purchase with the snowshoes either. As I understand, more digging from the guides was able to get enough snow away that we were able to pull her out.

She said that crevasse rescues are much harder with 70 lb packs and, the best quote was “they teach you about crevasse rescue, but they don’t tell you about the pool of water at the bottom”. Of course, normally there aren’t pools of water at the bottom because crevasses are on the side of a steep mountain. In this case, we’re literally standing on a river of ice that is flowing for the next 30 miles. It made sense that there would be water there, especially with this heat. The glacier is dense enough that it makes it really hard for the water to flow anywhere downhill. She thought to herself after falling in, “is this how I’m going to die”? Her team, laying in the snow trying to pull her out was absolutely not ok with that.

She said that after climbing to the airstrip, 2 hours or so after getting out of the crevasse, with the down layers on and the 70+ degree heat that she didn’t start feeling warm until 10 minutes from the airstrip. She left the heavy gear on the entire time we sat at the airstrip. 3 hours.

She lost a helmet, a trekking pole, a carabiner, Dan’s cheap sunglasses and a few other things. Much of her cold gear may also have to be tossed.

I napped a bit, laying out my foam mat to try to get some rest and find some way to shield myself from the sun. I generally left my gloves off, but kept my hands hidden away from the sun and reflection off the snow. I didn’t sleep of course, but laying down felt nice. I hadn’t slept for 36 hours or so. Around this time the final rope team from IMG arrived with the guy who had the broken wrist. We saw them coming up Heartbreak Hill, his arm wrapped in a sling. I bet he’s in some pain. That must have been a terrible 6-7 miles out.

Finally, around 5pm a plane arrived to pick up 5 of our crew. I got a video of the landing and takeoff. The pilot said there is either another plane on the way, or he’ll return in an hour or so to pick us up. Fortunately, another plane landed 20 minutes later! I was sooo thrilled to see it! Finally we could escape this heat, get back and enjoy some more beer and real food! I was very excited to get off the mountain.

We packed up the plane and climbed in. The flight out was equally stunning as the flight in, and landing in Talkeetna was sweet and wonderful. We unpacked the plane and put our stuff in the hangar. We pulled out all group gear and threw it in a pile, then grabbed our duffle from the locked storage container and put that in the hanger with the rest of our gear. I took my backpack out of the duffle and grabbed anything I could think of that I needed in it. Of course I missed my hair brush.

Hannah borrowed the shuttle from the motel (because they are cool with RMI guides) and dropped us off at the motel to save us the walk. We checked into our room around 6:30. I don’t recall the number, but it was a two story building on the left, second floor in the back left corner of the building. I checked Facebook to see what people said and cried when I saw my wife’s post showing me at the top on the Garmin map and saying congratulations. It finally hit. I did it. I climbed Denali. I broke down in tears.

I called her at 7 to say hi and showered at 7:20. My first shower in 17 days. It felt amazing. I washed my hair really well. Of course, then I realized I left the hair brush in the duffle, but whatever. I dried my hair and pulled it back and headed into town, thrilled to be walking in shoes instead of plastic boots finally!

We met at Denali Brew Pub at 8pm and got a table outside. Naturally I got bit twice on the way there, then 6 or more times on the way back. We ordered beers inside and did some rounds of cheers. I ordered a 1/2 pulled port with curly fries and the brownie and 2 beers along with a raspberry or blueberry mojito. Inga had ordered one and it looked delicious, I had to try it. It was good! Nick and Inga said their feet were messed up. Mine fortunately turned out ok, a bit red on the big toes and the soreness on my ankle was a swollen bump (mildly) that is strangely enough, still there 1 month later as I write this. I did burn my tongue. I was curious why it seemed to hurt when I was eating, I figured it was split from dehydration or something. I noticed it in the mirror after my shower, white specks of burnt taste buds. In general, everything hurts.

After finishing dinner the guides handed out our summit certificates. That felt great. I had my two from Rainier, but one from Denali, that’s an achievement that I’ll treasure forever. It was awesome to see my name on it, signed by the guides and the word “Denali”. Writing this even now, weeks later, I treasure this trip far more than any climb I’ve ever done.

Some people headed back to their rooms to sleep, the rest of us headed to The Fairview Inn, the traditional place that Denali climbers go after their climb. I’ve been told you can’t take photo’s in here, though I saw no rules stating this. It was packed, people everywhere. We ordered drinks at the bar, I got a rye. While hanging out and chatting with the team someone ran the bell. Ringing the bell here means a free round for everyone, paid for by the bell ringer! Naturally, we ordered more drinks! The team ordered shots of Jameson and did more cheers to Denali. Nick I think was talking to the bell ringer. Turns out he’s from Leavenworth Washington! My Mom lives there! I chatted with him for a while, super cool guy. He climbed Denali in the 80’s.

Naturally, at this point I was pretty toasted. 5 drinks plus 2 on the glacier on top of almost 48 hours without sleep and something like 11-13 miles and 7,000 feet downhill with up to 130 lbs of gear. It was a perfect way to celebrate after doing the most physically challenging thing of my life.

EWald and I headed back to the motel and I was in bed by 11:30.

Heading Home

Day 18:

I set my alarm for 8 am and was ready at 8:15. EWald had already left, so I turned in the key. The shuttle was 9am at the hanger. I had forgotten one key bit, the shuttle was LEAVING at 9 am, not us meeting there at 9 am. I did go first to the airport, but the door was closed and locked. I found that strange as it was around 8:30 and I expected people to be there. So, I jogged into town to go to the coffee shop. While waiting in line, I got a phone call. I was surprised as my phone doesn’t have service in Alaska! I answered it and it was Dan. He said “where are you, we’re leaving!”. I quickly ran back to the airstrip and it turns out a side door we never used before was the one that was unlocked. I wasn’t aware of that! I finished pulling together my things and we threw them in the van’s trailer and headed out.

Fortunately, they wanted to stop for coffee on the way back, so I didn’t go too long while starving. The coffee shop didn’t have food, so I headed over to the Starbucks in a grocery store. Of course, it didn’t click when I walked into a….grocery store…that there was other food. I picked up a Starbucks sandwich (it was fine), but then realized where I was. I found a donut as well. Perfect.

3 hours after leaving Talkeetna we arrived at the airport. Jason and I were heading into town to stay in Anchorage a bit longer, so we said our goodbye’s to everyone else. Inga and Nick said all three of us will have to climb a mountain in Washington sometime. Nathan and Yvonne said they’d love to climb something in Washington with us too if they come to visit. The shuttle dropped me off at the Hilton and Jason down the road at the Sheraton. He was going to grab dinner with Jason M who was just finishing recovering in Anchorage. They fly home tomorrow.

Conclusion

So that’s it. That’s my story. That’s my adventure of a lifetime. It was incredible. While there I certainly said I never want to do this again, but now, of course, looking back on it a month later, finally finishing this story I really do want to climb another one, or even return. Denali was incredible. The views were way above anything I expected. The scale of everything there was unimaginable. The heat and cold cycles were intense and unrelenting. But the climb itself, while physically difficult, wasn’t as hard as I’d expected. One foot in front of the other. That’s what it takes. Persistence. The refusal to stop and refusal to turn around until you reach the summit. It’s far more mentally difficult than physically. You have to want the summit to get it and clearly I wanted it.

I climbed this for three reasons. For my team at work, New World. That team works hard to build an incredibly fun game for millions of players around the world. They poured their heart and soul into it for nearly a decade now.

I climbed it for myself, to experience something totally unique and absolutely incredible. It met that expected bar and then exceeded it. This was a true test of what I can do. It felt incredible to be able to be one of the very few people in all of time to have been able to climb Denali.

But the primary reason to climb it, the primary hope is that this climb helps inspire Claudia. I hope she looks back at this through life and remembers what I did and uses that memory to push herself to find her own limits. I hope she uses this to make herself the best version of Claudia that she can possibly be. I love you Claudia. This is for you.

Building a Flexible Vision Framework for Video Games

During my time working for a previous game studio I had the privilege of building a number of core gameplay systems from the ground up.  We had a need for a server authoritative vision system that would allow objects in the world to be in certain vision state such as hidden, spotted and fully visible.  If the object was hidden the server would stop networking it’s position and state data and the client would not render it.  This ensured that clients cannot hack the executable to display the enemy position.  I referred to this system as the Intel system.

Another developer had written the first version of this system and he successfully built it to meet the requirements set by design.  However, design later changed these requirements and made a request that the system was almost entirely incapable of meeting.  With much work another developer was able to make it meet those requirements.  However, at this point the systems performance was a serious concern.  To make matters worse, design requested another change.  A 3rd developer similarly wrangled that one in.  At this point the system was considered on it’s 3rd re-write.

My boss originally accepted the task of rewriting the system.  Within a week he realized he would never have time to get the work done and asked me to take it on.  I accepted and proceeded to dive into the design documents.  Over the following two weeks I put together an engineering proposal on how the system would be architected.  I learned what went right and wrong with the original system and while I scrapped the original system, one or two concepts (I’ve forgotten the specifics at this point) did come over.

The key requirements that I needed to consider were as follows:

  1. Design will flip flop on the rules that determine if an object is visible.  Perhaps the objects become visible automatically within a certain distance, perhaps objects are visible if an ally can see it or maybe objects become invisible if the player is damaged by a certain type of attack.  Second
  2. The system must be flexible in the types of queries it makes.  It should be easy to swap out a query for another one if performance becomes a problem.
  3. Objects should become visible as soon as possible when the rules for being visible would return true.  This requires the system to be very performant and with this, clever in how it manages it’s queries.
  4. Players should not have a random advantage over each other.  If one player processes Intel against another, the other must process Intel that frame as well.

System Architecture Ideas

The Intel system was a large and complex beast.  There were a number of moving parts, a large number of classes and components and an architecture for putting all of this together in a way that would maintain flexibility and minimize the performance cost was critical to it’s success.

The “Pipes” Concept

One of the pieces my boss originally recommended looking at was the logic that determined if an object is spotted.  He recommended viewing this logic as a bunch of pipes.  These pipes may have one entrance but each pipe splits into one or more additional pipes.  Each pipe has a set of requirements in order to enter it and a set of output data.  Perhaps the first pipe is distance based.  If the player is within a maximum spotting distance, then we will flow through to the next pipe.  That pipe may check if the player is very close and if so, immediately resolve to a spotted state.  If that pipe fails it may lead to a raycast pipe who performs it’s check and returns it’s results.  The next pipe processes those results and if it returns success, passes along to yet another pipe who does even more processing.  All of these pipes and requirements would be able to be customized by design.  This would allow them, in the tool, to redefine the flow of logic for determining the Intel state of an object.

I wrote a quick 2 day rough draft of this system to determine it’s feasibility.  I felt ok about it, but I had concerns about the flexibility they system provided and how much engineering time would need to be spent on it.  I wasn’t convinced that amount of flexibility was necessary and I was worried about designer changes creating bugs and performance issues.

I discussed this architectural proposal and my concerns at length with my boss and our tech director.  In the end they agreed to let me make the call.  I abandoned that direction.

My Resulting Concept

I preferred to simply hard-code the spotted logic.  I recognized that design will want changes to this code flow, but after writing the logic for their initial design as a rough draft it was no more than 100 lines of very easy to follow code.  If design wanted to add a new piece to that flow, it would be simple.  I also recognized that it was unlikely that design would want to make consistent changes to this flow due to design being very well vetted.  I was right.  Over the course of the next year design requested edits to that flow maybe 3 times.  Building out the pipe architecture would have been a waste of time, all at the expense of much more complicated code that would be vastly more difficult to debug.

 

Final Architecture

So, without further ado, my next steps were to define the remaining system architecture.  The spotted code discussed above was a very small piece of this pie.

Here is a very rough flow diagram of the system architecture:

IntelSystem

Classes

  • IntelMgr:  Responsible for maintaining all permutations of Intel objects in the world.
  • IntelQueryGroup:  Responisible for maintaining all permutations of IntelQueries from object A to object B.
  • IntelQuery:  Responsible for executing a single IntelQuery.  There are roughly 3 different types of queries:  Distance, raycast and occlusion.
  • IntelComponent:  Responsible for processing the IntelQuery results and determining the Intel state of the target object.
  • IntelStateComponent:  Maintains the objects current Intel state of all other Intel objects in the world.
  • IntelStateCalculator:  Fed the current Intel state and determines the current visibility state.
  • IntelVisionTargets:  Feeds the IntelQueryGroup to determine what designer specified points on the target object to raycast to.
  • IntelViewports:  Feeds the IntelQueryGroup to determine what designer specified points on the source object to raycast from.
  • IntelSettings:  Global settings provides settings that individual objects don’t need.  Individual objects contain overridden settings or custom ones of their own.  These settings feed the IntelStateComponent’s IntelQuery processing.

High Level Flow

IntelMgr is the root of everything.  The client and server both call Update() on this and it processes everything it needs to.  It is directly responsible for tracking the Intel based objects in the world.  Each object has an entry added into it’s priority queue for each other Intel object in the world.  During the update it will allow a certain “cost” of these pairs of objects to be processed.

These are processed through IntelQueryGroups.  This class maintains a lost of all possible IntelQueries object A could make against object B.  This includes raycasts from each viewport to each designer specified point on the other object.  A certain number of IntelQueries are then run against both objects.

An IntelQuery can be raycast, distance, occlusion, etc based queries.  An IntelQuery must run at least a few of these in order to fully process the query.

Next the IntelComponent’s Update() is called.  This processes the query results and runs the logic I discussed in the “My Resulting Concept” section above.  The resulting state of the queried object is stored in the IntelStateComponent.

The IntelStateComponent’s job is to track the current Intel state of the object.  However, it doesn’t do this on it’s own.  It relies on an IntelStateCalculator to track the interpolated state.  This allows objects to fade out over time or even linger before disappearing entirely.  Without that players would have a jarring experience with enemies popping in and out of view.

Server/Client Differences

The whole system can be thought of as a Model View Controller.  The client is the view, the Intel objects are the models and the server is the controller.  The server naturally processes the above-mentioned “High Level Flow”.  The IntelStateCalculator will call a supplied callback when the state changes.  This will send the updated Intel state down to the client.  The networking systems ask the IntelMgr if the object is visible to a particular client and if so, they will send the primary object update packet down, otherwise they do not.

The client has a number of differences to the above flow.  It is overall much simpler.  It is notified of Intel state changes and sets these on the IntelStateCalculator.  The calculator processes this state just like it does on the server.  The difference here is that the listener for the callback is instead UI or audio.  The IntelMgr simply ensures that all the IntelStateCalculators are updated.

Performance Considerations

IntelMgr

Since IntelMgr is the root of everything, it has the ability to have the greatest impact on performance.  If the game world has 20 Intel based objects on each team then IntelMgr has 400 permutations of IntelQueryGroups and each IntelQueryGroup could have as many as a dozen raycast targets.  It originally had on average 23!

IntelMgr therefore needs to be able to minimize how many IntelQueryGroup updates are ran.  It also needs to track the cost of the group running in case the IntelQueryGroup needs to make an expensive update.  IntelMgr was built on a priority queue to solve for this.  Intel object nearer to each other would update more often.

IntelQueryGroup

This class also turned out to be critical to the performance of the system.  It also saw the most change during it’s development and in the end, was a bit more messy than I would have liked it to be.  It was turned into a threaded operation in order to provide more throughput and leverage our idle physics threads.  Additional complexities were added to minimize the number of IntelQueries it ran.  It was discovered late in development that the time that an object would become invisible again after being spotted was variable.  In order to resolve this I split it up into 3 phases:

  1. Target is invisible:  Round robin through IntelQueries.
  2. Target is visible:  Run only the last successful IntelQuery.
  3. Target just lost:  Run a BatchedRaycastAll.

If the target is not currently visible, IntelQueryGroup would run a few IntelQueries per frame, not all of them, but it would run through it’s own internal priority queue of IntelQueries so that higher priority (more likely points to spot) are ran more often.  Once the target is spotted, the successful IntelQuery is stored off and each frame after that is the only query tested against.  This allows performance to be dramatically less for visible targets.  However, in order to ensure that targets fade out after a consistent time when they become obscured by the world I ran a BatchedRaycastAll the frame the stored IntelQuery failed.  This query would run all possible queries against the target.  If successful, it would cache off the first successful query in the batch for the following frames.  Naturally this BatchedRaycastAll is quite expensive.  It too was multi-threaded and we managed to take it down to a very manageable cost.  IntelMgr will receive information from IntelQueryGroup so it can determine how many more queries it can run that frame.

IntelQueries

These encompass a set of derived classes.  A query by itself just as a success flag to allow IntelComponent to process it’s logic properly.  I designed this to allow us to develop new queries easily and swap out queries that we felt weren’t performant or to allow us to try new query concepts.  While we stuck with distance, multi-threaded raycasts and occlusion queries we discussed other high level rejection queries such as bounding volumes, voxel data and Umbra.  Each of these have possible application here that could have allow trivial rejection of the remaining queries, saving time on raycasts.

Results

The system took approximately 4 months to build and 4 months to feed design requests for additional changes.  These changes proved easy to add in.  Adding multi-threading support took myself and another developer about 2-3 weeks of time.  The system worked well from the start.  The first check in proved to be bug free according to QA and as far as my own heavy testing of it found.  It proved to meet designs requirements, be performant, scale-able and easy to maintain.

Key Takeaways

  • Adding multi-threading support was much more challenging than expected.  This wasn’t multi-threading itself, but the desire to leverage that to provide guaranteed fade out timings for Intel objects.  This code was starting to become challenging to follow and contained a few bugs that were difficult to track down.
  • Needed more focused QA passes.  Much of the QA passes that were done were individuals on a single machine rather than a full server with all clients.  An 11th hour bug due to the multi-threading support was found and while relatively easy to fix, would have been caught weeks before with a proper QA pass.
  • Testing the system thoroughly is relatively time consuming for an engineer.  However, it’s also very important to ensure bugs are not introduced.  I setup a test level that allowed me to run through some extreme examples to see the worst of the system and this helped speed this process up.
  • Debugging state transitions through IntelStateComponent and IntelStateCalculator was tricky.  Finding a way to improve this interaction would have been a benefit.  I did send down the server state information to the client for debug rendering and this did greatly help.
  • Don’t let design define how many IntelQueryGroups to execute on a given frame.  I originally put this in to allow them to tune the numbers until they got the responsiveness they wanted.  This caused our frame times to spike to 10ms from what should have been 2.  By adding in the BatchedRaycastAll query, this reduced the need for them to concern themselves with responsiveness.
  • Be cautious about the number of IntelVisionTargets design can specify.  Lay out serious ground rules and ensure they stick to them.  Our original IntelQueryGroups had 23 raycasts, we reduced this to roughly 8 with a few exceptions allowing for more.  Doing this increased responsiveness for initially spotting a target and allowed IntelMgr to run more IntelQueryGroups per frame.
    • The goal had been to allow strangely shaped world geometry to ensure that an Intel object would still be spotted.  However, in the end our game found this to not be a concern.
  • Keep accurate documentation.  This includes engineering and design “how-to” documentation.  This made it easy for other engineers to understand the system and was a handy reference for myself.  The design facing documentation proved to be extremely handy in allowing design to work with the system without needing to ask an engineer.

Super Mario Brothers in 2.5D

Introduction

A year or so ago I wrote a 2.5D Super Mario Brothers game in Unity 3D.  This has been done by countless developers who are inspired to recreate legendary games in a fun environment.  I did it for just that reason, fun.  I of course had grandiose plans to develop it as many different games.  Level 1 would be Mario, level 2 would be Mega Man, level 3 something else, etc.  Of course, as life always does, bigger things came up that I decided to work on.SMB1

Features

The game featured the following components of the original title:

  • Jumping and bonking of ? blocks
  • Squish-able Goombas
  • Stompable and kickable turtles
  • Mushrooms and Fire Flowers
  • Fireballs

Development

This process went smoothly, and horribly.

What Went Right

Much of the project came along smoothly.  The first thing I needed to do was create prefabs to duplicate around the level based on some data.  Then I needed to supply that data.  Doing some quick searches on map images turned up a great website resource such as Mario Brothers Maps.  This had pixel perfect maps, including showing where item blocks are, enemies, and more.

I took these images, ensured their pixels lined up in 16×16 increments and pulled out all the unique tiles.  From here I wrote a C++ tool to grab each 16×16 block of pixels, matched the block to a tile, gave it a UID and saved the results to file.

Then I updated the Unity 3D app to load in the appropriate prefab in the world location of each UID found in the level.  This generated the level, let me place enemies and let me tag prefab tiles as ? blocks.  Piece of cake.

It was fun seeing this as well.  Old-school levels, levels I adored as a kid seen for my first time in full 2.5D.  Mario Brothers, Mega Man, I tried a few levels of each and was nothing but excited by the results.

Next up was getting the enemies to move around, no problem here.  Squishing the Goomba’s was fun and rewarding, getting the turtles working was easy and the fireballs were a quick few minutes of work.

What Went Wrong

As always.  Collision.  I hate collision.  Floating point errors, bad math on my part, poor approaches, these have always plagued my collision detection and most of all, collision handling implementations.  One of these days I’ll set out to make it really work, but until then, I’ll continue to let engines implement it for me.  Too bad Unity 3D doesn’t do that.  They have 2 collision tools, character controller style capsules and rigid bodies.  Capsules slide off edges, bad for 2D side scrollers.  Rigid bodies are problematic to get working for actual movement of characters as they tend to prevent movement when on the ground, bounce poorly (especially in Unity) and don’t provide the custom support for jumping that Mario requires.  My own implementation did end up working, but it was re-written 4 times and I was never very happy with the results.  I don’t like saying that I couldn’t get it to work, but to be fair, I don’t exactly put my best foot forward when working on home projects.  I wasn’t super excited about making the collision perfect, I had other motivations.  Good enough is good enough for this project.

Results

In the end I had simple bonking of Goomba’s, fireballs killing Goomba’s, turtles being stomped and powerups.  This was enough to satisfy my interest in the project.  It was fun, worth the time, but far from educational from a gameplay perspective.  In terms of learning Unity 3D, it was educational and one of my deeper dives into Unity.

Here’s some more pictures you can enjoy!

SMB3

SMB2

SMB4

Magic the Gathering Card Cataloger

History

Several years ago while at Warner Brothers/Monolith I joined the MTG league that started up there.  Every new set that was released was accompanied by a 10 booster pack tournament, starting with 6 and opening 1 new pack every other week for 8 weeks total.  It was a ton of fun, and reintroduced my love for MTG.  I started buying up large swaths of cards from co-workers and ended up with a collection of over 10,000 cards.  I wanted to be able to build decks out of these cards without having to constantly go through them to find the cards I wanted.  While I had them sorted, it was still very time consuming to track the cards down.

Solution

So, I wrote a MTG card cataloger.  I wrote it in MFC, as I hadn’t used it much.  WB used it for some of their tools and I wanted to better understand it.  I learned what I didn’t like about it, or at least, what I did with it that made it a pain to use.  Adding new dialog boxes required about 10 minutes of copy/pasting of code to hook everything up.  Really fun stuff…

In the end I had a great tool with the following features:

  1. I can add new cards, including all of their data such as name, casting cost, power/toughness, abilities, type, subtype, some general themes (destroy creature, add mana, counter spell, draw card, etc) and more.
  2. I can edit existing cards.
  3. I can create a new collection of cards and add cards from my collection to it, including foils.
  4. I can browse a collection of cards.  This also shows how many of each card I have, and how many total in a particular set.
  5. I can create and edit decks that I build cards from.  This has even more features:
    1. A filter system to allow me to narrow down search results by color, type, ability, name, and more.
    2. A filter results area that shows all the resulting cards.  This includes a button to add this to my cards to consider for the deck.
    3. An area with cards I’m consider for the deck.  This includes a button to add cards to either the sideboard, or the deck itself.
    4. An area showing the cards in the deck and another area for the sideboard.
    5. An area that shows the total casting cost per color of the cards in the deck, the average per color and the total average.
    6. Each of the above areas show how many cards there are in total in each area.
    7. Finally, buttons to flag standard deck rules or EDH, allowing the system to apply those rules to what cards you can put in your deck (banned cards aren’t there).

Results

CardView

DeckView

I heavily used this tool.  I loved it.  I added thousands of cards.  While I had to download the images for each, that never took more than 10 minutes.  Adding each set of cards was roughly an hour of work and adding the cards from an entire booster box was about an hour as well.  I’ve created more than a dozen EDH decks from it, and building those decks from my actual catalog is quick and painless.  It’s easy to find the cards I want in the boxes as I know how I sorted the boxes.  This tool may not have paid off in terms of time, but I really enjoyed developing it and using it.

Additional Work

Online Editor

After developing it I used it as a tool to learn web development.  I implemented a very simple version of a card editor that allowed me to add new cards and edit cards.  I write it in PHP and Javascript, leveraging SQL queries for the data.  I didn’t take this too far, but it was a useful introduction to web development.

Web Tool

I developed another quick tool that ran SQL queries on an XML catalog that I uploaded.  This catalog contained how many of each card I owned.  I could pull up my phone, hit the website, type in a card name and it would kick me the total count.  I found this really useful when doing trades with other MTG players and verifying that I don’t already have a particular card that I’m interested in.

The catalog was generated with console prints from my MFC tool.  I would copy paste the prints into the XML file and upload the file to the server.  There’s better solutions to this, but I didn’t need to do this very often, so this quick solution worked well for me.

C# App

I also developed it in C# as a better way to understand C# outside of Unity 3D.  This introduced me to WinForms and WPF, though at this point I don’t recall which one I ended up with.  I found the graphic user interface one to be very difficult to use and having separate sheets/pages to be error prone as hell.  I spent far more time trying to get the GUI tool to work right than I did actually writing code.

However, I only wrote the part of the tool that lets me browse cards and browse a catalog.  With this I wanted to know how much my collection was worth.  So, I hooked into C#’s interfaces to call into websites.  I found that my favorite website, magic.tcgplayer.com had a very convenient URL path for each card, that worked flawlessly with my format.  My cards were numbered the same way, named the same, etc.  From here I got the resulting HTML in a string, searched through it for the value tags and accumulated the results, saving the data with the catalog file so I don’t have to re-query the website every time.  Querying the website for my entire collection of cards took somewhere around an hour.  Hope they didn’t mind the extra traffic!  🙂

Takeaways

I had fun on this.  Lots of fun.  I had a direct goal in mind and I wanted nothing more than to achieve it.  This goal provided me with a rich tool that lets me keep track of my cards.  I learned MFC, HTML, Javascript, PHP and more C#.  I call that a win.

AI Spatial Awareness with Projected Volumes

Introduction

For part two of my spatial awareness prototyping I’ll present my solution to Problem #3 of the previous solution.  The previous solution can be found here:  AI Spatial Awareness with Renderable View.

Motivation

The previous solution had a few drawbacks, one of which was the lack of precision with edges of objects such as buildings along with it’s greatly stretched quads for objects near the ground.  I greatly disliked how this looked and sought to find a solution that would solve this.  This solution solves the problem with precision by providing detection of exact edges in geometry based on the desired resolution.

High Concept

Remembering back to my days of graphics rendering I realized projected volumes, such as shadow volumes were a great solution to this.  Using the shadow volume example, any area that is in shadow is hidden from the enemies view and any area that is “lit up” is in view.  It’s that simple.

Algorithm

As always, I love pretty pictures.  So lets go with that first.

SV1

This is an image showing the edges of the polygons from the sources viewpoint.  The white lines represent the detected edges, the green lines represent the normals of the planes that are generated by these lines and finally the blue boxes show what edges intersect with the ground.

A zoomed out picture of this isn’t very interesting without some clipping of the planes, which I have not done.  However, the end result is that an object has a volume extending along behind it.  Points inside this volume are hidden from view and points outside it are in view.

Did not do an exhaustive implementation of this algorithm, nor did I follow proper projected volume techniques, however I will detail what I have done, and my plans for it’s future.

  1. Pre-cache the neighbors of each triangle in the scene.
  2. Set our “camera” to the position of the object who is looking into the world (who our AI will try to hide from).
  3. Go through each object in the scene and each triangle on the object.
  4. If the triangle is forward facing, then check each neighbor.
  5. If the neighbor is backwards facing, create an edge.
  6. Go through each edge and generate a plane from it using the edge normal relative to the “camera” position.
  7. Store the resulting planes for each object in it’s own list.  This allows future convex or non-convex (depending upon your object) tests to be ran against them.
  8. Edges that intersect with the ground do not need planes extending beyond them.
  9. Finally, each forward facing triangle is turned into a plane.  This generates the front facing hull.  There is no need for a backfacing hull.

This provides us with projected volumes.  Any object who is enclosed entirely in the volume is fully hidden from view.  If an object is intersecting with a plane, then the part of the object that is outside of the plane (on the side that the normal points towards) is exposed.

Future work

To complete this prototype, code would need to be added to determine if a point or a shape is in view, hidden from view, or partially in view.  This can be solved with plane tests on the input object.  If the object is convex, such as a triangle, box or circular polygonal object, the solution is simple.  A point would be tested against the dot product with the normal of each plane.  If each returns a negative number, the point is fully hidden.  Doing this for a shape requires additional math that can be found with a simple google search.  Arbitrary shapes quickly become complex, so I would recommend simplifying it to at a minimum, a bounding sphere.

Solving this for the general case volume is a bigger challenge.  I have not researched this, however the issue that I’m presenting is non-convex objects, such as a torus, an L shaped object, bridges, etc.  To determine if an object is inside the non-convex object is more challenging and would need to be researched.  However, an approach that could help with this would be to break the object down into convex hulls.  This will increase the number of planes generated by the algorithm, but would greatly simplify the vision calculation by reducing it to a few dot products.  Hierarchical Approximate Convex Decomposition (HACD) is a great option for generating these convex hulls and would be worth evaluating.

Pathfinding

This solution works well for grid or node based pathfinding solutions.  Each grid could be considered a box or a center position and be tested for visibility.  A node based solution is typically done with points which is the same simple implementation.

Nav meshes are a bit more tricky.  Often pathfinders across nav meshes use the edges of the nav mesh to pathfind rather than the centers.  However, both solutions have the problem that some areas of the polygon may be visible while other parts of it are not.  An option that could work would be to determine how much of the polygon/edge is visible and consider the entire polygon/edge hidden for pathfinding purposes based on a threshold.  Another alternative is to split up a polygon/edge into multiple pieces if it’s found to be partially visible and partially obscured.  From here the resulting connection to other polygons/edges would determine how visible that connection is.  This is then factored into the cost of pathfinding along that connection.

Performance

Projected volumes are expensive to calculate.  If they prove to be too expensive to calculate on the CPU then the GPU is a great alternative.  When generating the volumes, it’s recommended that you use lower resolution geometry.  This however will decrease accuracy and create false positives for the AI.  If that becomes a problem then this can be solved by extruding out the geometry to fill in these gaps at modelling time or runtime or to simply require each evaluated point to be a certain distance behind each plane.

While I haven’t explicitly evaluated it, I would recommend convex hulls over non-convex hulls.  This should be researched if you intend to use this approach.  By storing each plane with it’s corresponding object, we can guarantee convexity.

Another consideration is that when testing if a point is hidden from view, there is no need to test against each object and each plane.  A quick dot product can be ran against the location of each object and only objects that are within a tolerance would be used.  It would be valuable to store the angle to the object relative to the “camera” position’s “look vector” and then the maximum angle that any of it’s planes generate relative to the “camera” position.  This will provide you a cone with the max angle that a point can be relative to the object and allow quick rejection.  An alternative is to create a low resolution set of 4 planes to enclose the entire object if the object has enough planes to warrant it.  As always, performance evaluation of this is prudent.

If the “camera” position is stationary, then this data can be calculated once at tool time or level load time and will never need to be calculated again.  If the “camera” position is not fast moving, then the edges used to generate the planes can be cached with each plane and only those edges need to be updated each frame (or less often for edges that are far away – based on angle).  If an edge was used and becomes invalid, then neighbor edges to the original edges triangles would need to be reconsidered.  Edges that are not in the current set of planes could be reconsidered less often, based on how far the “camera” position moves relative to the object.

In Conclusion

I’m quite happy with this solution to spatial awareness.  It provides as much accuracy as desired through the use of geometry resolution.  View lookups can be performed quickly for convex objects.  Performance may be a drawback, however this can be remedied with some caching concepts presented above.  This is definitely a solution that would be interesting to pull into a large scale game to evaluate.

AI Spatial Awareness with Renderable View

Introduction

Some time ago I began research on AI spatial awareness.  I was saddened to see a dramatic lack of information on the topic.  Generally spatial awareness was solved with influence maps or other fairly rudimentary solutions.  None of these really gave the AI the ability to know where they can safely hide from an enemy and where they can stand in order to peek out and fire at an enemy.  I will present two of the initial solutions I implemented in two separate articles.

Motivation

The influence behind this research was the desire for AI objects in a 3D world to have the ability to dynamically find cover that will provide them safety from an enemy target while providing them a quick and easy “peek and shoot” location.  Leveraging pre-placed cover locations wasn’t something I was interested in.  Games in today’s market demand greater immersion and more realistic, robust and emergent gameplay.

High Concept

My first article will present the first possible solution I considered to this problem.  It uses GPGPU shaders to perform view space calculations on the scene from the perspective of the enemy AI object towards the AI seeking to find cover.  The high concept is that the enemy AI will render the scene from it’s point of view, we will then calculate ground positions, normals, etc for each corner of every pixel and map the resulting valid quads to 3D geometry which will be read back in by the game.  This geometry would overlay on the pathfinding system to define “unsafe” areas for the AI to move to.  With this, the AI knows exactly where it can go to be safe, and it can determine an “unsafe” edge that it can stay close to in order to do “peek and shoot” behaviors. Sound interesting?  Lets dive into the details.

Algorithm

First off, lets see a pretty picture.  I like pretty pictures.  Below you will see an image of the final results.  The red boxes represent the 3D quads, the white lines represent the quad normals and the blue lines represent the pixels corners that did not generate valid quads.  Finally, the capsule in the lower left represents the enemy looking into the scene.  This enemy, for reference is well above the ground. Image The solution that I implemented in Unity 3D was a software rasterizer that is able to quickly prototype this idea and easily display any pieces of information I need to.  Extending this to the GPGPU’s would be trivial enough.  Here are the steps for the software rasterizer.

  1. Generate a 2D array of a desired resolution, the higher the resolution, the smaller and more accurate the final results will be.
  2. For each index in our array, cast a ray from the enemy position along it’s look direction offset by the field of view and our current screen space x/y.  It isn’t necessary to cast out for each corner as this becomes redundant with the next pixels corners.  This data can be merged, but for my implementation this offset by a half pixel is not a concern.
  3. Take the resulting collision information and cache the normal and position.  In addition to this the face of the object hit will need to be recorded and compared to neighboring pixels to ensure that it resides on the same object.  If it doesn’t, additional calculations may need to be performed.
  4. To generate the quads we determine if each array indices 3 neighbors (those that compose of our quad) have a relatively similar normal and have intersection positions that are relatively close to the average of the corner locations.  If a corner is too far away, it’s possible it is on another object, or perhaps on the ground behind an object.  If a corner has a very different normal, then it may be on a wall.
  5. From here we have a quick and dirty version of world space quads for pathfinding.

Here is an image to show the rays being cast: ImageThis solution gives us a great pathfinding benefit.  Pathfinding queries can use these quads to increase the weight of pathing through the navmesh.  Due to the increased weight, AI will prefer not to follow a path that overlaps a quad.  This allows them to know spatially, and in a 3D world where they can and cannot move to remain safe from an enemy target.

GPGPU Algorithm To expand upon this, my thoughts on the required steps for a GPGPU solution are as follows:

  1. Setup a render target for the enemy.
  2. Set the camera to the enemy, point it towards the AI who requires this query to be run.
  3. Render the scene to the render target with as few render objects as necessary.
  4. For each pixel, either use the center of the pixel or each corner, noting that each corner will need to cache it’s results for the next pixel if that solution is used.
  5. Take the depth information in that pixel and convert it to a 3D position.  This should be easy enough to calculate given the camera matrix.  I’ll leave that as an exercise for the reader.
  6. Generate quads from the results on a per pixel basis.  Optionally you can merge quads as needed to create a minimal set of geometry.
  7. Game code will read this geometry off the graphics card, overlay it over the pathfinding solution and apply weights to the data.  If this pathfinding solution is a nav grid, then simply determine which path nodes are inside the geometry (2D math on this for a world where Y is up will be close enough) and update the weight on each of those nodes.  Low density nav grids have precision issues that will need to be considered.  If the solution uses a nav map then the solution requires additional processing to cut up the nav geometry dynamically.  This may be too performance intensive but there may be other solutions, such as leveraging two nav maps instead.

Problems

My solution has three key problems that I will detail below.  I have not vetted these first two problems in a final implementation and do not intend to pursue them at this time.  The third problem I address in my second article through an alternative implementation.

Problem #1

The end results depend heavily on resolution and the location in the world of the enemy.  In the first picture above, the resolution is fairly low, but the enemy is fairly high in the air.  In the below image, the enemy is low to the ground and the resolution is low.  Notice the distinct lack of precision in the left most quad.  Also notice that the quad that attempted to be generated to it’s left wasn’t generated, but that the area to the left of the left-most quad is considered “safe” to the AI when in fact it’s not. Also notice that the precision is very high close to the enemy, yet very low as you get farther away.  When the enemy is close to the ground it emphasizes this problem, but when the enemy is high in the air, this problem largely goes away as you see in the first image in the article.Image To address the above issue, the GPGPU shader will be required to determine where the edge of the valid part of a given quad is.  In the above case, the quad to the left of the leftmost quad will determine that the right two vertices are valid, and the left two are on the wall.  It will need to find the cutoff of where the wall starts, and the plane defining the wall.  This could be generalized with a binary search of raycasts which may perform well enough and give good enough results.  The resulting corners would be used instead of the ones on the wall.

Problem #2

The second problem also that of precision.  If an object is small enough to fit inside of a pixel, it could be missed.  If that pixel covers a screen space that is too large (i.e., the AI object can fit entirely inside of it), then the AI may not find cover in that location that could otherwise be valid.  This problem is easy to solve, increase the resolution of the render target, or perform raycasts into the world on a per pixel basis in order to find these objects.

Problem #3

Another problem is that the AI does not have hard edges of things that it knows are or are not safe.  The AI has to use the resulting path map to determine where it can move.  This may make it hard for the AI to easily find a location to peek out from to shoot.  While this is not a big issue, I found it less desirable.  My second article on this will present a solution to that problem by implementing a different form of spacial awareness.

Render Target Resolution and Performance

The resolution of the render target is another important thing to consider.  It is necessary for an object close to the ground to use a tall render target, perhaps 64×256 in order to get pretty good results.  If the above quad splitting solution is too expensive, then perhaps a lower resolution would be better and get good enough results.  An object that is higher off the ground can leverage a slightly more square render target. Another performance consideration would be to ignore quads who’s vertices are fairly close to each other.  There’s no reason to split a quad who’s normal’s are on the ground and the wall if the total size of the object is smaller than the diameter of the AI object.

Another pretty picture.  This one shows higher resolution image of the previous image in the article.  The previous image was 40×25 and this one is 70×150.  Notice the increased precision along the forward and along the left/right axis from the enemy. Image The super high precision at the bottom of the image is unfortunate.  However, it’s possible that this could be reduced by clamping the vertical FOV to a number that makes the initial quads be farther out into the world.

In Conclusion

I am relatively happy with the results that this solution provided.  The flaws that it has are solvable, however performance is my biggest concern.  I like that this solution generates ground quads that can easily be overlayed onto a pathfinding solution, in particular a node map. Please feel free to comment with your thoughts on the solution and any alternative ideas you have that could add to this solution or spatial awareness in general.

Data Tracking Architecture

At a previous company I wrote a data tracking system that was used to track statistics on players, design tuning values and player profile progression for in-game challenges and achievements. This system needed to track a large permutation of data, up to 50,000 unique pieces of data. This presented a fun set of challenges to solve ranging from the cost of updating a piece of data, accessing a piece of data, in-memory overhead, serialization, uploading to a server and finally, code structure to allow ease of use and maintenance.

I solved all but one of these while implementing and maintaining this system over a period of 2 or 3 months. The one that I didn’t solve on the job was the code structure. I left that task as any programmer would in this situation, unhappy. I went home with the lessons learned and re-wrote it myself. I felt good enough about my solution to present it here. Please note that this is not final code. I’ve abandoned my work on it, but there is much that can be added to it and a few minor details to work out. Those are noted below.

Please do give your thoughts on this!

First off I present a quick layout of the original architecture for reference and to explain what it was that caused me problems. From here I’ll explain how I went about solving this.

enum EStatEvent
{
	kDeath,
	kKill,
	kLevelUp,
};

class CStats
{
private:
	vector<CCharacter>	m_vCharacters;
};

class CCharacter
{
	CCharacter() : m_nID(0), m_nLevel(0), m_nKills(0), m_nTimesPlayed(0) {}

	CCharacter(CCharacter& character)
	{
		m_nID = character.m_nID;
		m_nLevel = character.m_nLevel;
		m_nKills = character.m_nKills;
		m_nTimesPlayed = character.m_nTimesPlayed;
		...
	}

	CCharacter& operator = (const CCharacter &character) {
		m_nID = character.m_nID;
		m_nLevel = character.m_nLevel;
		m_nKills = character.m_nKills;
		m_nTimesPlayed = character.m_nTimesPlayed;
		...
		return *this;
	}

	void OnEvent(EStatEvent eEvent, CStatParams *pParams)
	{
		switch(eEvent)
		{
		case EStatEvent.kDeath:
			//  Increment our m_vDeathsFrom
			break;
		case EStatEvent.kLevelUp:
			++m_nLevel;
			break;
		...
		}
	}

	void OnSave(Stream stream)
	{
		stream.WriteUINT("ID", m_nID);
		stream.WriteUINT("Level", m_nLevel);
		stream.WriteUINT("Kills", m_nKills);
		stream.WriteUINT("TimesPlayed", m_nTimesPlayed);
	}

	void OnLoad(Stream stream)
	{
		...
	}

	void UploadToServer(Packet packet)
	{
		...
	}

	void GetValue()
	{
		...
	}

	void DebugPrint()
	{
		...
	}

private:
	UINT				m_nID;
	UINT				m_nLevel;
	UINT				m_nKills;
	UINT				m_nTimesPlayed;
	vector<CCharacter>		m_vDeathsFrom;
	vector<CWeapon>			m_vWeapons;
};

class CWeapon
{
private:
	UINT			m_nID;
	float			m_nTotalDamage;
	UINT			m_nKills;
}

The above architectures goal was to track data about each character the player played, each weapon they used, how many kills they got as that character, how many kills they got with each weapon, how many times they died from each character, etc. This provided the ability for design to watch a players progression over time, through each level and see if they stop playing and if so, when or why it was. They can perform statistical analysis on the data to determine if certain characters are too strong or too weak and how updates to the game data effect these statistics.

The problem I ran into was adding a new stat. In our code base this required adding that stat to about 11 different functions. The constructor, copy constructor, equals operator, Load(), Save(), OnEvent(), DebugPrint(), Upload(), Download(), Get() and several more. This was a pain in the ass. If I missed a single function I would have a bug that I wouldn’t hear about until a week later. My workflow to add a single value was to update each function and then test each function in order to ensure that it worked properly. This was about a 30 minute process. When we added well over 100 variables (and changed half of them before we shipped), this was a lot of wasted time.

Why not have the variables do all this work themselves? I wish I’d thought of that before!

That’s what I set out to fix.

First off I considered an alternative implementation and some alternatives on it. I summarized it with its benefits and drawbacks. Notice I only have 1 alternative. After some thought, this was all I came up with and if at the time I’d considered others, the tree plan caught my attention the most.

My above implementation:

  • Benefits: Easy to debug, easy to visualize
  • Drawbacks: Game specific, hard-coded, hard to maintain

A tree structure:

  • Benefits: Flexible, dynamic at run-time, less code, less buggy, custom generated types
  • Drawbacks: Hard to debug, tricky code, more custom classes

I then considered a variety of options for the leaves and parent nodes. This factored in leaves being individual stats and class leaves being the “leaf” level of a class itself. Then I had to consider how to add stats. This could be done with run-time pushes, data driven (load time) or at compile time. Then event handling was considered with direct access or events. Events could be solved with leaves registering themselves directly for the event, or each class simply passing it along until it’s consumed. Then I needed to determine which stat to update from an event. An event, such as a kill event needs to know who died, who killed them, what weapon was used, etc. Each piece of data that cares about this needs to know to be updated. This could be solved with a params structure, UID’s or custom callbacks. There’s even more to consider but you get the picture.

The key things I wanted to solve were:

  • Minimize code updates when adding a new stat.
  • Prevent game specific class hierarchies, allowing this to be a core system games build on top of.

I didn’t personally care to solve for data driven stats vs. hard-coded stats, so I chose hard-coded stats. A compile time structure vs. a run-time structure was also not important. Either solution was just fine with me. As it turns out, my implementation supports data driven stat hierarchies as well.

Here’s what I implemented. Again note that this is sample code, it’s far from complete functionality and has flaws here and there. The goal is to present the high level concept of the architecture.

Engine level header file:

enum EStatEvent
{
	eStatEvent_Invalid,
	eStatEvent_EngineSpecificEnum,
	eStatEvent_Count,
};

class CStatParams
{
public:
	CStatParams();
	virtual ~CStatParams()

	int	m_nInt;		//  Sample required param
	float	m_fFloat;	//  Sample required param
	UINT	m_nUID;		//  For array indexing
};

//  Base class for a stat
class CStatBase
{
public:
	CStatBase();
	CStatBase(const char* pName, EStatEvent eStatEvent);
	virtual ~CStatBase();

	virtual void OnEvent( EStatEvent eEvent, CStatParams* pParams ) = 0;

protected:
	const char*	m_pName;
	EStatEvent	m_eEvent;
};

//  Implementation of a built in data type for int.
class CStatInt : public CStatBase
{
public:
	CStatInt();
	CStatInt(const char* pName, EStatEvent eStatEvent, int nDefaultValue=0);

	virtual void OnEvent( EStatEvent eEvent, CStatParams* pParams ) {
		if( m_eEvent == eEvent ) {
			m_nData += pParams ? pParams->m_nInt : 1;
		}
	}

private:
	int		m_nData;
};

//  Implementation of an array stat
template  class CStatArray : public CStatBase {
public:
	CStatArray() : m_nArraySize(nArraySize) {}
	CStatArray(const char* pName, EStatEvent eStatEvent)
		: CStatBase(pName, eStatEvent)
		, m_nArraySize(nArraySize)
	{
		for( UINT i = 0; i < nArraySize; ++i ) {
			m_nArray[i] = T("", eStatEvent);
		}
	}

	virtual void OnEvent( EStatEvent eEvent, CStatParams* pParams ) {
		if( m_eEvent == eEvent ) {
			//  NOTE:  Could use a validation callback instead of m_nUID
			if(    pParams
				&& pParams->m_nUID < m_nArraySize )
			{
				m_nArray[pParams->m_nUID].OnEvent(eEvent, pParams);
			}
		}
	}

private:
	T	m_nArray[nArraySize];
	UINT	m_nArraySize;
};

//  The base class representing a collection of related stats
class CStatClass : public CStatBase {
public:
	CStatClass(const char* pName);
	~CStatClass() {
		for( UINT i = 0; i < m_vStats.size(); ++i ) {
			if( m_vStats[i] ) {
				delete m_vStats[i];
				m_vStats[i] = NULL;
			}
		}
		m_vStats.clear();
	}

	bool AddStat( CStatBase* pStat ) {
		if( !pStat ) {
			return false;
		}
		m_vStats.push_back( pStat );
		return true;
	}

	virtual void OnEvent( EStatEvent eEvent, CStatParams* pParams ) {
		for( UINT i = 0; i < m_vStats.size(); ++i ) {
			m_vStats[i]->OnEvent(eEvent, pParams);
		}
	}

private:
	vector<CStatBase*>	m_vStats;	//  All the stats contained in this class
};

//  The root stats class.  Clients store one of these.
//  It contains all stats in a tree structure.
class CStats {
public:
	CStats() : m_pStats(NULL) {}
	~CStats() { delete m_pStats; m_pStats = NULL; }

	void Init( CStatClass* pRoot ) {
		m_pStats = pRoot;
	}

	bool AddStat( CStatBase* pStat, CStatClass* pParent ) {
		if( !pParent ) {
			return false;
		}

		return pParent->AddStat( pStat );
	}

	void OnEvent( EStatEvent eEvent, CStatParams* params ) {
		if( !m_pStats ) {
			return;
		}

		m_pStats->OnEvent(eEvent, params);
	}

private:
	CStatClass*	m_pStats;
};

In the above implementation I have a core CStats class which clients use as their entryway to the stats system. This class has a single CStatClass class inside of it which holds the root of the stats tree. The interface for adding a new class or adding a new stat to a particular class is clean and easy to use. CStatBase is the base class/interface for all the stats. It contains pure virtual functions for all the functions we want implemented such as Load(), Save(), OnEvent(), etc. CStatClass contains each stat which could be anything from another stat class to a specific data value or an array. Specific data values simply derive off of CStatBase, such as CStatInt. These derived classes simply implement the specific functions for the data type they define. Along with this, the system will need a validation function to ensure that a value that is passed in is the correct one to update a specific stat with. This could be a UID comparison, but it could also be a callback that does game specific work. That has been omitted from the above code.

Now here’s how a client uses this system.

Client level header file:

enum EGameStatEvent
{
	eStatEvent_Kill = EStatEvent::eStatEvent_Count,
	eStatEvent_Death,
	...
};

//  Custom client kill params
class CKillParams : public CStatParams
{
public:
	CKillParams() : CStatParams() {
		m_nKillerID = INVALID_INDEX;
		m_nVictimID = INVALID_INDEX;
	}
	CKillParams(UINT nKillerID, UINT nVictimID) : CStatParams() {
		m_nKillerID = nUINT;
		m_nVictimID = nUINT;
	}

	UINT m_nKillerID;
	UINT m_nVictimID;
};

//  Custom client data type (ignore that killer ID
//  updates m_nData, it's just an example!)
class CStatUINT : public CStatBase
{
public:
	CStatUINT() : m_nData(0) {}
	CStatUINT(const char* pName, EStatEvent eStatEvent, UINT nDefaultValue=0)
		: CStatBase(pName, eStatEvent)
		, m_nData(0)
	{
	}

	virtual void OnEvent( EStatEvent eEvent, CStatParams* pParams ) {
		CKillParams *pKillParams = dynamic_cast<CKillParams*> (pParams);
		if( m_eEvent == eEvent ) {
			m_nData += pKillParams ? pKillParams->m_nKillerID : 1;
		}
	}

private:
	UINT	m_nData;
};

//  Custom client stat classes for “game mode” data and “root” data
class CStatGameMode : public CStatClass {
public:
	CStatGameMode(const char* pName) : CStatClass(pName) { }
	~CStatGameMode();
};

class CStatRoot : public CStatClass {
public:
	CStatRoot(const char* pName) : CStatClass(pName) { }
	~CStatRoot();
};

//  Some client game class that tracks the stats
class CClientGame {
	...
	CStats		m_Stats;
}

 

Notice how the client of the system defines an “extension” enum. This is my least favorite part as C++ doesn’t provide a way to extend a lower level systems enum. More on this below. The client can create custom params classes to handle things like kill events which would contain data such as who the killer was, who was killed, what weapon was used, etc. The client can define its own stat types, such as my example of the UINT. The client can additionally define their own game specific stat classes. Finally, all that’s needed is an instance of the CStats class somewhere in the code base.

Client level source file:

void CClientGame::AllocateStats() {
	CStatRoot* pRoot = new CStatRoot("root");
	m_Stats.Init( pRoot );
	{
		CStatMode* pMode = new CStatMode("create");
		m_Stats.AddStat( pMode, pRoot );
		{
			// Adding an array, float and UINT stat
			CStatArray<CStatInt, 10>* pIntArray = new CStatArray<CStatInt, 10="">("Sample Int Array", eStatEvent_Kill);
			m_Stats.AddStat( pIntArray, pMode );

			CStatFloat* pFloat = new CStatFloat("Sample Float", eStatEvent_Death);
			m_Stats.AddStat( pFloat, pMode );

			CStatUINT* pUINT = new CStatUINT("Sample UINT", (EStatEvent)eStatEvent_Kill);
			m_Stats.AddStat( pUINT, pMode );
		}
	}
}

void CClientGame::StatTestFunction() {
	CKillParams params(1);
	params.m_nUID = 3;	// This will access pIntArray[3]
	params.m_fFloat = 1.3f;
	m_Stats.OnEvent( eStatEvent_Death, &params );

	CKillParams params2(1);
	m_Stats.OnEvent( (EStatEvent)eStatEvent_Kill, &params2 );
}

 

Now for the final bits of the implementation. Above you will see that we define the structure of the stats system dynamically in code. It’s hard-coded sure, but this could be data driven or extended at run-time. This defines the tree structure of the stats system itself.

Issues with this implementation:

Back to my least favorite part of this. The enum. This is quite upsetting. C++ seems really limited here. I wanted to use enums as they provide type safety and a clear name to prevent bugs. If I defined an enum in a game level header file, I could easily extend it with another game level header file using some #include magic. However, with an engine level file, I don’t see a good way to do this. I could have it look for a required header file in a higher level folder, but this doesn’t allow the clients to have the convenience of placing this file where they want. Any recommendations or thoughts on this are appreciated!

My current implementation causes a few problems. It requires users to cast from their game enum to the lower level enum at the call-site. This is far from ideal. This also may cause an implicit cast to an int, though I’m uncertain without scrutinizing the code further. What I like least of all is that when viewing the stat’s event enum in the debugger, it will show “eStatEvent_Count (x)” where x is the enum value of your game level enum, for example 10. It does not show your game level enum. This makes debugging this code less clear.

An alternative is to use strings. Obviously string comparisons aren’t cheap and they would take up large amounts of space, so that’s an alternative that should be shot down quickly. Another alternative is string hashes. This has much better performance however it’s far less readable when debugging. Maybe this is an acceptable tradeoff. A downside is that I may be required to use at least 2 bytes to represent the hash in order to prevent hash collisions. I’d prefer to use 1 byte for the enum representation to cut down on memory costs as there may very well be less than 256 events in a game. I could of course, just use int’s (or char’s in my 1 byte case). This has the same cost as hashes, same memory footprint as hashes and same lack of clarity when debugging. I could use my enums still and all parameters are implicitly cast to an int. It’s still not ideal, but given the alternatives, it may be the best. A final thought is that I could implement a OnEvent() function that accepts EGameStatEvent and internally cast the parameter to an int. This would remove the need to cast it at the call site.

What do you think? How do you like this overall architecture with regards to it minimizing errors when adding a new stat? Can you think of alternative architectures that may prove to be better?

Macro fun

For my very first post I thought I would present a piece of code that I recently found.  It was an interesting exercise for me and I wanted to share my findings.  Here they are.  Enjoy!

Consider the following piece of code:

# define FAIL_RETURN(x) \
 if (0) \
   return x; \
 else \
   for (Result _result_no_collide_ = (x); _result_no_collide_ != gk_ResultOk; ) \
     if (g_breakOnFailure && (__debugbreak(), false)) \
       ; \
     else \
       return _result_no_collide_

I know what you’re thinking.  The first time I saw this my brain wanted to explode.  It looks like a mess, it’s hard to understand and it’s undocumented.  Is all this really necessary?

The answer to that question is mostly.  Here’s why.

1.  if(0) else:  This appears to be simply a convention the programmer followed.  It also allows changing this to an if(1) so we can ignore all the code in the else.  We can safely remove this from the macro without side effects.

2.  for:  The for loop is here to provide scope to the _result_no_collide_ variable.  Without the for loop (or a do/while loop) we would be forced to put curly braces around the result variable that is created to store and test the result of ‘x’.  This causes a semi-colon on the caller’s side of things when using it inside an if/else statement that doesn’t use curly braces to fail to compile.  Without curly braces we run into two obvious problems; First the _result_no_collide_ is now able to be used by the caller’s code.  Second the if/else statement without curly braces will fail when a semi-colon is used.

3.  (x):  This is just good macro programming practice.  Unless you have a specific reason to not wrap your parameter in a parenthesis, wrap it.  It prevents order of operations bugs with parameters passed into a macro.  In the case of our macro though, these parenthesis are unnecessary.  See this link if you would like to read more about why we wrap parameters in parenthesis, and when you don’t need to:  https://www.securecoding.cert.org/confluence/display/seccode/PRE01-C.+Use+parentheses+within+macros+around+parameter+names

4.  Infinite for loop:  This looks like a for loop that will continue to loop forever.  If you don’t get a gk_ResultOk you will enter the for loop.  If you then hit the __debugbreak() and press F5, it appears that it will just run the for loop again.  As point 6 below explains, it won’t do this, however that’s very not clear.  We could remove the conditional in the for loop and add it as an if statement inside the for loop.  This works too and is documented below.  It looks a bit uglier, but could be considered easier to follow by some.

# define FAIL_RETURN(x) \
  if (0) \
    return x; \
  else \
    for (Result _result_no_collide_ = (x); ; ) \
      if(_result_no_collide_ != gk_ResultOk) \
      { \
        if (g_breakOnFailure && (__debugbreak(), false)) \
          ; \
        return _result_no_collide_ \
      } \
      else \
        break

5.  The for loops internal if/else:  This seems redundant at first glance.  However if you were to instead write the macro as follows:

# define FAIL_RETURN(x) \
 if (0) \
   return x; \
 else \
   for (Result _result_no_collide_ = (x); _result_no_collide_ != gk_ResultOk; ) \
     if (g_breakOnFailure && (__debugbreak(), false)) \
       ; \
     return _result_no_collide_

The code would fail to compile due to the empty control statement in the for loops if statement.  So, what about changing that into a return _result_no_collide_?  This doesn’t work either as the final lines return _result_no_collide_ will fail to compile as we are no longer inside the scope of the for loop.  So we could add curly braces to the for loop and a semi-colon after the final return.  Does this work?

# define FAIL_RETURN(x) \
 if (0) \
   return x; \
 else \
   for (Result _result_no_collide_ = (x); _result_no_collide_ != gk_ResultOk; ) \
   { \
     if (g_breakOnFailure && (__debugbreak(), false)) \
       return _result_no_collide_; \
     return _result_no_collide_; \
   }

Not quite. This looks correct but breaks down in a specific case for the callee.


if (a == 0)
    FAIL_RETURN(0);
else
    ...

In this above case, the semi-colon is a natural thing to place after the FAIL_RETURN() call.  C++’s convention says to do it, but in this case, it breaks the else statement after the FAIL_RETURN call.  Removing the semi-colon successfully compiles the code.  Thus, while this solution works, it makes the code awkward due to this obscure and hidden rule.  Programmers should be able to put a semicolon there and not break everything!  Now, one thing that isn’t clear is why the semicolon at the end breaks the statement?  An empty else with a semi colon is ok, but a for loop with a colon after the curly brace breaks the callee’s else statement.  Comment if you know the answer!

6.  if( g_debugBreak && (__debugbreak(), false)):  This is an obscure one.  Most people see this code and think, that won’t compile.  So did I.  C has an obscure operator, the comma operator. In short, the comma operator will execute each expression before it but only use the final one in the overall expression.  Visit this link for more information (Comma section):  http://www.cplusplus.com/doc/tutorial/operators/.  So, what this expression says is “if g_debugBreak” then call “__debugbreak()” and ignore it’s return value, after that, it says “false”.  Thus we have 3 expressions, the g_debugBreak, __debugbreak() and false.  __debugbreak() is ignored due to the comma operator and the end result is the following if statement:

if (g_breakOnFailure && false)

Hence the entire statement fails.  However, if g_debugBreak is true, it fires the __debugbreak, fails the if statement and passes flow control to the else statement.  Also, recall topic #5’s scoping issue for why we can’t refactor out the comma operator and just say:

if (g_breakOnFailure) \
    __debugbreak();
return _result_no_collide_; \

So, in conclusion this messy looking code protects us from many unsafe uses of the macro. Developers don’t have to worry about where they can or cannot use it and they don’t have to worry about enclosed curly braces or hidden else statements. They can use FAIL_RETURN() in single line (non-curly braced) if statements. They won’t be able to use them in ?: operators as it evaluates a condition or an expression, not a statement. It cannot be called inside of a function parameter list. A “return FAIL_RETURN()” will fail as well for syntactic reasons as well as flow control reasons.  If the parameter ‘x’ doesn’t fail, what is returned? You cannot call it with if(FAIL_RETURN()) {} as this fails to compile.  Finally for(FAIL_RETURN(); ; ) {} fails to compile as well.

Some might wonder why not a do {} while(); loop?  That’s a matter of personal preference.  It would be written as follows:

# define FAIL_RETURN(x) \
 do { \
   Result r = x; \
   if( g_breakOnFailure && r != gk_ResultOk ) \
   { \
     __debugbreak(); \
     return r; \
   } \
   break; \
 } while(false)

This would work as well. Note the missing semi-colon after the while(true). This forces the caller of the macro to add the semi-colon, as we would naturally type.