Sunday, December 26, 2021

My Game of the Year Awards: 2021

Dating back to my days as a kid, one of my favorite hobbies is computer and console gaming. Following the Gamers With Jobs rules -- only games that I played for the first time in 2021 are eligible to make the list -- here are my top 10 favorite new-to-me games of 2021 (along with the platform on which I played each game), in descending order:

1. Metroid Dread (Nintendo Switch)

After nearly 20 years, we finally got a new 2D Metroid sequel. And it does so many things right! Gameplay that deeply respects prior games in the series, yet smartly blazes new territory. Beautiful and finely-detailed graphics. Good map/level design. Tough but fair boss battles. It's probably impossible for the greatness of Super Metroid (SNES) to ever be matched again, but Dread is an excellent modern take on the formula.

2. Bravely Default 2 (Nintendo Switch)

Bravely Default 2 takes the raw materials of JRPGs going all the way back to the first Final Fantasy games, iterates and folds them back on themselves many times, and ends up with this pretty nice piece of gaming origami. The most important thing I look for in a JRPG is combat that is fun, well-balanced, and (ideally) rewards smarts/creativity on the part of the player, and Bravely Default 2 delivers.

3. Final Fantasy Pixel Remaster (iPhone)

Speaking of the first Final Fantasy games: It was a treat this year having in my pocket this update to the very first Final Fantasy featuring a wonderful soundtrack, sharp graphics, and modernized and rebalanced gameplay -- while still remaining faithful to the original. I don't often blog about individual games, but I did do a compare-and-contrast between Final Fantasy Pixel Remaster and the NES original.

4. Monster Train (Windows)

With my primary computer these days being a Mac, I don't do a lot of gaming on Windows, but I made an exception for Monster Train. It's a fun and smart iteration on the Slay the Spire deck-builder-battler genre. (I was initially put off somewhat by the protagonists being the forces of Hell, but it's fine; in Monster Train, the angels are a bunch of jerks, and your team is fighting for self-preservation.)

5. Xenoblade Chronicles (Nintendo Switch)

I'm still midway through this expansive classic, and it might end up higher on a hypothetical 2021-2022 combined list, but for now, here we are! I had originally passed on this game, but the combination of my teenage son recommending it to me as having "single-player MMO" combat, plus wanting to learn more about Shulk from Smash Ultimate convinced me to give Xenoblade Chronicles a fair shake, and I'm glad I did. And I really like the soundtrack!

6. Etrian Odyssey 5 (Nintendo 3DS)

I dusted off my 3DS again during a period where the aforementioned teenage son was monopolizing the household Switch, and discovered that he'd purchased a digital copy of Etrian Odyssey 5 (2017) at some point. I'm only midway through, but the tough combat and old-school DIY mapping using the 3DS's stylus are just as engaging as I remember from earlier series entries.

7. New Pokemon Snap (Nintendo Switch)

I had low expectations for New Pokemon Snap, but they were wildly exceeded when I had a couple of weeks to give it a try on a check-out from my local library. I think the thing I like most is the fact that when you manage to get a great photograph, it's 100% due to your own timing and skill -- not due in part to some huge passive buff from a piece of equipped gear, or a lucky roll on some behind-the-scenes RNG. I'm planning to purchase a copy of New Pokemon Snap for myself when it goes on sale.

8. Steamworld Quest (iPhone)

This deck-builder-battler that's not a roguelike, but is instead a party-based RPG, played really well on the iPhone platform, even though it was originally designed for larger screens. The unique battle system kept me engaged all the way through to the end of the game.

9. Roguebook (Mac)

Another fun riff on the deck-builder-battler-roguelike genre, adding a map exploration mechanic that ends up working a bit like FTL, where you need to do all you can in a given area to power yourself up, before taking on the area boss and advancing. A few remaining rough edges in both minor UI bugs and gameplay balance issues don't hold Roguebook back from being fun to play.

10. Sorcery! 4-game series (Mac)

Also available on iPhone, this isn't just a remaster of the original 1980s gamebook series, but a full remake, taking advantage of the digital platform. A combat system that incorporates a bit of skill, and isn't simply dice-rolling -- and smartly, gives the player the option to immediately replay battles that didn't go so well -- adds to the fun.

Honorable mention

Best new DLC: Fighters Pass 2 for Super Smash Bros Ultimate (Switch). I find it crazy how well this mashup of 82 or so distinct playable characters from dozens of different series actually works in practice. My favorite gaming experience of 2021, eclipsing any of the new games mentioned above, is playing 2v2 local-vs-online matches with the teenage son. (The characters I mainly play are Samus and Ludwig von Koopa!)

Friday, December 10, 2021

Kotlin: My first impressions, via days 1-3 of Advent of Code 2021

While working on Ruby solutions to this year's Advent of Code programming challenges, my attention was caught by one of the sponsored messages running in the site's sidebar, from JetBrains:

Get ready to jingle with Advent of Code in Kotlin! Have fun, learn new things, and win prizes. Believe in magic with Kotlin. Happy holidays!

That link redirected to a post on the JetBrains / Kotlin blog inviting developers to try out the Kotlin language via the Advent of Code (AoC) problems, and providing a nice Kotlin AoC GitHub template as a starting point.

I'd never worked with Kotlin before, but I have used AoC to try out new programming languages before. Serendipitously, I had some time available today, and so I decided to give it a go!

In the time I had, I wound up solving the first 3 days of AoC 2021 in Kotlin.  Here's a quick write-up of my very first impressions of Kotlin!

What I liked!

Good out of the box support in the IntelliJ IDE (which I downloaded for the first time today), including suggestions for more idiomatic Kotlin syntax. (Which makes sense, as JetBrains provides both the IntelliJ IDE and the Kotlin language itself!)  I was saved from some instances of typical "trying to write language X as if it were language Y" newbie mistakes by warnings/suggestions in the IDE.

IntelliJ displays, inline in loop declarations, whether the lower and upper bounds are inclusive of exclusive. This made it easy for me to understand whether, in a Kotlin until loop over an array, I needed to use myArray.length or myArray.length - 1 as the upper bound of the loop. (The former!)

I found the data class syntax to be a nice succinct and readable way for a method to return some related values. In a method in one of my Ruby AoC solutions where I was returning two values just wrapped together in an array, it was easy in the Kotlin equivalent to declare a one-liner data class, and then have my method return an instance of that class, to make what was being returned much more obvious to readers.

If ... else in Kotlin is an expression, not a statement. While looking up an equivalent for the typical ? : ternary expression syntax, I actually enjoyed discovering that ? : is actually not supported in Kotlin, and an inline if ... else should be used instead.

Support for ".."-syntax ranges like in Ruby (e.g. (5..15) to represent the set of integers 5 through 15, inclusive) was fun to see in a C-like language.

The variable-declaration keywords val and var, similar to let and const in JavaScript ES6, to respectively declare immutable and immutable variables.

The ability to pass a function "pointer" as a parameter to a function -- something both the Ruby and Kotlin variations of my Day 3 solution actually made use of.


What I maybe didn't like so much

I actually wasn't aware of this ahead of time, but Kotlin is built on top of Java. (Or perhaps it's more accurate to say that Kotlin, like Java, is a language that runs on the JVM?) The first time I became aware of this was when my program threw an exception... and there was a mix of Java and Kotlin code in the call stack. I have no idea to what extent aspects of Java poke their heads up while working in Kotlin... but it's something that I'd want to understand more about before committing to using Kotlin for a real project, versus other available languages where that kind of thing is much more of a non-issue.

JetBrains' Kotlin template for AoC helpfully included a check call to test each problem's provided sample answer against the corresponding sample input. What wasn't so helpful was that a failure of the test just resulted in a generic java.lang.IllegalStateException : Check failed -- with no accompanying mention of the check's expected or actual values. I had to manually add a print statement to find out what the failure was. Maybe I was just missing something?

I ran into some trouble using the built-in pow to raise 2 to the power of a particular variable:

  • It took me a little while to figure out why I was getting an Unresolved reference: pow.  I ended up googling for that exact phrase, while led me to discover that an import kotlin.math.* is needed. Not surprising in retrospect, but it would have been nice to a a prompt in Kotlin's error message about an import/reference possibly being needed. (I believe C# does this, probably among others.)
  • The pow method (as of the current version, 1.6) can't be used on integers. After a little more confusion, I figured out that I had to use the literal 2.0 (instead of just 2), and then manually convert the result back to an Int after getting my result. Why make developers do that?



Verdict? It's much too early for a verdict! I only worked with Kotlin for a few hours, on some toy problems, on my own! 

It was fun, though, to get to play with a new language! Kudos to the Kotlin team at JetBrains for their combination of the Advent of Code sponsored message, their blog post, and the Kotlin Advent of Code GitHub template, which taken together, were enough to get me to take a look at Kotlin!

And if you're a developer but have never taken a look at Advent of Code before, I strongly recommend checking it out! The event is extremely well crafted and executed each year. It's worth your time!

Thursday, October 07, 2021

Git tip: Visual indicator of the 50-character git commit summary message limit

Although it's somewhat too short for my taste, I generally comply with the conventional wisdom of having the summary line of git commit messages be no more than 50 characters.

Even using an editor that shows the current position of the insertion point (such as vim with set ruler enabled), it's not readily discernible how much of the available 50 characters remains, relative to text that has been keyed in so far.

My solution to this has been to set up a ".gitmessage" commit message template that includes a comment showing the 50-character limit:

#                                                  |<-- 50 char summary line limit

As long as my summary message ends before that vertical pipe character, I'm good!

Friday, September 03, 2021

New iOS app: Gong Sound

Would you like the ability to make your iPhone to emit a "gong" sound? Further, would you, ideally, like that gong sound to be accompanied by a low-fidelity, semi-realistic animation of an actual swinging gong? If so, then do I have the app for you!

Introducing Gong Sound, available for free on Apple's App Store!

Amazing features

  • On a tap, the app emits a "GONG!" sound! Wow!
  • The gong swings back and forth -- in a physics-approximating, 7-frame animation! -- for a few seconds after being tapped!
  • iPad support!
  • Supports landscape (device held sideways) mode!
  • High score counter! (With classic-arcade-like non-persistence!)
  • No ads! No in-app purchases! No data harvesting! No review nags! No network connectivity used or required! 
  • FREE!

Wait... so it's just a gong app?

Yep. 🙂 The Gong Sound app is just a "toy" project. I got the code written, and the art and sound assets adapted, for the initial 1.0 version in all of a couple of hours. (As compared to the dozens of hours that I spent on Desktop Journey, or the hundreds spent making Vigil RPG!)

At my company, teams strike a gong that we have in the office as (part of) a celebration of notable achievements.  In these days of remote work, I figured: Why not give folks an easy way to "gong" from home which sounds better than banging pots and pans together, and isn't likely to play an ad instead of the desired sound effect when the "Play" button is pressed. 

(And doesn't violate any of the items in that second-to-last bulleted list item above, in the fashion of most existing similar apps on the App Store.)

Gong Sound is a free download on iOS devices, so if you're reading this post on your iPhone: Share and Enjoy!

Thursday, August 05, 2021

Impressions: Final Fantasy 1 Pixel Remaster (iPhone)

Square Enix recently released "Pixel Remaster" versions of the first three games in the Final Fantasy series. Over the past four or so days, I purchased, played through, and completed the iPhone version of Final Fantasy 1 Pixel Remaster.

As a veteran of multiple playthroughs of the original NES version of Final Fantasy 1, I thought I'd share my impressions of this update to this classic game, focusing mostly on compare/contrast of the new pixel remaster version with the original.

With the original Final Fantasy 1 having a high frequency of standard enemy encounters plus a very limited number of spell slots, my favorite party composition is Fighter, Black Belt (Monk), Thief, and Red Mage. That’s also the party that I used for my playthrough of the remaster.

Final Fantasy 1 Pixel Remaster Battle: 6 ogres vs party of Fighter, Red Mage, Thief, and Monk

All-caps character names for old times' sake!

Spoiler statement: This article avoids spoilers about game plot / events, and for the most part, about specifics of particular dungeons and enemies. There are some mentions of particular character abilities and spells.

Quality-of-life improvements 💆

These are numerous! Some highlights that I noted during my playthrough:

Auto-battle. ⏩ The remaster has a button available on the combat screen which causes the party continuously repeat whatever actions they performed last round, and also increases the speed at which the battle plays out. Both a convenience (particularly on mobile, where performing precise button taps multiple times every round to select actions is more annoying) and a time-saver for when the party is moving through an area of easily-winnable battles.

Tap to move. 👆 The iPhone remaster takes nice advantage of the touch screen by providing an ability to tap a location on the screen to move there (as well as a traditional on-screen D-pad option). Movement speed when en-route to a tapped location is also significantly increased, which is a nice time-saver.

Save anywhere. 💾 One “quick save” slot is available which can be used in any location. Definitely a must-have feature in a mobile game like this — at least, for a game not featuring automatic continuous progress saving (like at least one other RPG native to the iOS platform I’m familiar with!).

Unlimited inventory space for weapons and armor. In the NES original, there was no shared inventory pool for weapons and armor. Instead, each of the four characters could hold four weapons and four pieces of armor — and once those 16 slots of each type were full, there was no picking up more until space was freed up. (This resulted in a sort of primitive encumbrance system!)

The remaster features a modern-style, unlimited-size shared inventory pool (similar to later Final Fantasy games). This ends up being a gameplay advantage as well as a quality-of-life improvement. There isn't, for example, a decision between keeping a helmet with the best armor rating, and a helmet with a lower armor rating but with a special ability usable in battle. Now you can equip the former, while using the latter as an item in battle out of the shared inventory pool.

Dungeon maps. While in dungeons, a scrollable map of the current dungeon level is available — even portions that haven’t been explored yet. Handy for avoiding the dead end corridors that are a part of a number of the dungeons.

World map. 🗺 It’s readily available now — no “TCELES B HUSP” required! Additionally the maps shows counts of the treasures and key items available in each visited location, and the number of each of those obtained. Handy for going for 100% completion! Speaking of which:

Achievements. 🏆 The remaster has them! I’m not sure they’re integrated with the iOS Game Center achievements system, though. (I can’t speak to the Steam or other versions of the game.)

No more invisible, fixed enemy encounters. In the NES original, there were a number of spots/squares in dungeons -- often immediately in front of important treasure chests -- where a fixed enemy encounter would take place when entered. (Repeatedly, if you left the square and then returned!) This has been replaced with (1) those encounters being triggered via the relevant treasure chest being opened, or (2) the enemy being visible on the map, with the encounter initiating when the enemy is character is interacted with. (The "Hall of Giants" is implemented in the latter fashion.)

Automatic use of key items. 🗝     There were a few spots in the original game where you needed to open the menu and use a particular key item at a particular location in order to progress. That's smoothed out in the remaster, with the appropriate key item being used automatically when you arrive at the appropriate location. I can definitely see this feature saving a few new players a trip to a FAQ to figure out what they need to do next.

Fast potion buying. Buying 99 healing potions is quick and easy now! No need to visit a shop with healing potion of at the top of the list, and zone out for several minutes while mashing the A button (or using a controller with a turbo function!).

Balance and Gameplay changes ⚖

(📉 Difficulty nerf) Better potions! 🍾 The original Final Fantasy featured only three types of potions: HEAL (restoring 50 HP), PURE (removing poison), and SOFT (curing petrification).  The remaster has those, and additionally makes available at stores Hi-Potions (restoring 150 HP), and more significantly, Phoenix Down (restoring a slain warrior to 1 HP). 

The availability of Phoenix Down in particular is a game-changer, as it makes reviving slain warriors possible before the Life spell is available at white magic level 5, and also removes the limit on the number of revives that can be performed in a given dungeon delve being tied to the number of level 5 white magic slots available.

Ether potions (restores 1 spell slot of each level) are also available at shops, making it possible to restore spell slots while in dungeons -- something that was not possible at all in the original game.

There are also a few additional status-restoring items in the game, although I found that I didn’t really use those in my playthrough. (The enemies in the game’s first large dungeon are as poisonous as ever, though! I burned through some 20 or 30 antidote potions while in there.)

Finally, there are a just a few X-Potions (fully restores HP) and Elixirs (fully restores HP and MP). These came in handy for me at one particular spot in the late game, when the warriors have much more HP than a Hi-Potion is able to recover.

(📉 Difficulty nerf) Lower gold costs / more gold rewards. 💰 Costs at shops are lower in the remaster. I was always able to afford everything I wanted at shops, with no need to grind for gold at all, or otherwise save up. I even bought the expensive “steel armor” / “knight armor” that’s available for sale at a high price (about a third of the way through the game), which I don’t believe I ever did in the original game.

(📉 Difficulty nerf) Faster leveling / easier foes? Most enemies and encounters seemed less deadly in the remaster. For example, in the original, having the entire party get stun-locked for multiple rounds by groups of undead with paralysis attacks was a big concern in some mid-game dungeons. I never came close to having this happen in the remaster. 

Further, in some spots in my playthrough of the remaster, my party felt over-leveled for the enemies I was facing. A particular undead mini-boss in the first half of the game went down especially easily (in the first combat round). Much of the second half of the game in general felt pretty easy difficulty-wise. The major dungeon after the “prove your courage” event in particular felt more like a victory lap than a challenge. (I do recall this to some extent being the case in the original game as well, however.)

(📈 Minor difficulty buff) Multiple large enemies. 👹 In the NES original game, fights were limited to a maximum of four large-size enemies (such as ogres) at a time, due to screen space constraints. In the remaster, although most fights stuck to the constraints of the NES original, I did have a handful of fights against 5 or 6 large enemies at one time, as pictured at the top of the article.

(📈 Difficulty buff) The final boss. 👿 Without getting into spoiler territory: With a well-prepared and properly-equipped party, the final boss in the NES original game could be fairly reliably defeated. In the remaster, although my party never had a wipe prior to the final boss, beating that last boss took me a good dozen or so attempts! (I do suspect that having a White Mage on the team might have made things significantly easier.) I was definitely glad for the ability to create a save immediately before the final boss fight in the remaster!

Bugs fixed 🐞🔨

This is almost certainly not a complete list, but here are a couple of fixes that I noticed that the remaster made over the original in my play through: 

The TMPR / Temper and SABR / Saber spells. Evidently in the original game, these spells were buggy, and had no effect on combat at all! I verified that they do indeed work now, and are effective melee buffs — along with the FAST / Haste spell, which is a very effective melee buff in both the original game and the remaster. (Pro tip: In the remaster, these spells can all be stacked onto the same character for a greater effect, too!)

Darkness status. 🕶 This is another one in the original game that didn’t seem to do anything at all. In the remaster, although I didn’t test exhaustively, it did appear to cause both warriors and enemies to deal fewer hits in melee. I actually did have my Red Mage use the black magic Darkness spell in a few combats against large groups of melee enemies.

New bugs 😱

Unfortunately, I did notice a couple of new bugs introduced by the remaster! (At least in the 1.0 launch version. Maybe these will get corrected in a post-launch update?) 

Elemental protection buff spells miss! 🔥 The NulShock / NulBlaze / NulFrost spells (formerly ALIT / AFIR / AICE) (reducing damage of the applicable element by half for the entire party) worked great and were effective when I first obtained them in the remaster. In particular, a battle with a fire-themed major boss was made much easier in my playthrough by the NulBlaze spell. 

Unfortunately, when I tried using these spells in the late game, about 90% of the time, they resulted in “miss!” on each of my own party members, instead of the buff being applied! I assume there’s some bug where the warriors’ evade chance is being applied to these spells, as they would be to incoming enemy status-affecting spells. 

Monk “optimal” equipment. 👊 When pressing the “Optimal” button on the equip screen for my Monk (formerly “Black Belt”) character in the early parts of the game, the game equipped the monk with one of my nunchaku items — even past the point where the monk would work better fighting unarmed (dealing a bit less damage per hit, but more hits). It’s easy to see this issue tripping up new players, making the monk class less effective than it should be for them.

Miscellaneous improvements ⚔

Music. 🎶 The music is not just remastered versions of the original chiptune NES tracks, but actually re-recorded as orchestral tracks. They really are great-sounding. They are very true to the original versions. Some of the original tracks that were very tight loops in the NES original, such as the shopping music and menu music, have gotten new extended versions, which are also great.

Dungeon backgrounds. 🌵 Each location has a new and nice-looking battle backdrop.

Spell animations. 💥 Each spell in the remaster has a unique and beautiful animation!

Dialog text. 💬 All of the dialog text in the game has been retranslated and improved. (The protagonists are still completely silent, however.) I did notice and appreciate that the classic line very early on in the game, "I, Garland, will knock you all down!" was preserved as-is, though!

Enemy names. The enemies have been assigned names that are more true to (what I assume are) the Japanese originals, are better translations, or can fit now that name length constraints are relaxed somewhat. A few such changes off the top of my head:

  • IMP ➡ Goblin
  • MadPONY ➡ Crazy Horse
  • BONE ➡ Skeleton
  • CREEP ➡ Gigas Worm
  • SAHAG ➡ Sahagin
  • KYZOKU ➡ Buccaneer

In-game hints. 💡 At least in the early portions of the game, if you talk to the dancer NPC in the starting town, she'll give you a hint as to what you should be doing next, a feature which is new to the remaster. (Another NPC in the castle near the starting town will clue you in that the dancer will do this.)

Things kept the same! 🛡

Character classes. No new ones, and they work the same as before!

Magic. 🪄 The system of a fixed count of available spell slots per level remains in place (instead of a more modern MP pool system). This keeps the system of needing to use potions instead of white magic for most healing outside of battle, and needing to use offensive black magic sparingly in general -- especially in the early game -- the same as it was originally.

The available spells, what they do, and which character classes can learn which spells, are also unchanged.

Items. The equippable items and key items available in the game, where they can be found, what they do, and which character classes can use which items, are almost entirely the same. 

(A single exception that I noticed: The "silver sword" / "mithril sword" is no longer available for sale in the elf town. Getting into why it was probably removed would touch on spoiler territory!)

World map, towns, and dungeon layouts. No changes that I spotted!

High random encounter rate. Random encounters occur quite often in both versions of this game! The above-mentioned auto-battle feature definitely makes this more palatable in this current day and age, particularly for a mobile game.

Deadly enemies! Some enemies that were particularly deadly in the original game remain so in the remaster! A particular type of enemy present in a mid-game ice-themed area with multiple abilities that can instant-kill individual warriors retains those abilities in the remaster. (The availability of Phoenix Down potions in the remaster does make the possibility of a warrior being taken down in that way a lot less scary!)

Overall recommendation: 👍

For anyone nostalgic for the NES original and looking for a solid premium RPG for their iPhone, I can easily recommend picking up this version of Final Fantasy!

Saturday, July 17, 2021

Hotels! Let me play stuff on my room's TV screen!

It has been my observation that successful hotel chains seem to always be on the lookout for new ways to positively differentiate themselves from their competition -- and thus, to attract more guests, and/or be able to charge higher rates. Such differentiating amenities range from the lavish -- such as building elaborate "pool complexes" on the hotel property -- to the simple -- a classic example being offering a warm cookie at check-in.

In this spirit: Hey, hotel chains! Here's a simple "amenity" suggestion that would get me to book with you over your competition, all else (e.g. location and price) being approximately equal: You know those nice big flat-screen TVs you've put in each of your guest rooms? It would be super great if you'd let me use that nice screen with my gaming console to play a game, or with my laptop to watch a video -- instead of, say, actively blocking my ability to do so!

Pictured: Not necessarily the optimal guest experience.

I was recently fortunate enough to be able to spend a couple of weeks traveling around Utah with my family, hiking in the national parks. Beautiful, and highly recommended! (Although if the forecast calls for 110°F+ temperatures, I recommend heading out as early in the morning as you can manage!)

Unfortunately, we had a lot better luck with the natural scenery than we did getting streaming video to work in our various hotel rooms' TVs.  I had come on the trip prepared -- or so I thought -- with a laptop and an HDMI cable, so we would be able to stream a show in the evenings after our daily adventures (or in the hot afternoons between them!).

In the trip's best-case hotel, we had a TV where I was able to reach around to the back of the TV to get at an HDMI port, then access a hidden function of the remote to change the input source on the TV, managing to get the show being streamed via my laptop to play on the TV.

In the worst-case hotels -- plural -- the TV's onboard HDMI ports had evidently been disabled entirely. Even though (peering behind the wall-mounted TVs) there were HDMI ports present on the TVs, there was no way to switch to them. The TVs were "hotel models" whose only physical control was an on/off button, and there was also no way to switch via the TV's remote controls.

This resulted in some silly situations like the one pictured above, with the family sitting around the hotel room's TV to watch some LEGO Masters -- with the TV uselessly powered off, while we huddled around the show on my laptop's small screen. 

While certainly a trivial problem in the grand scheme of things, it would nevertheless be quite a nice amenity to stay in a hotel which made it easy, instead of difficult or impossible, to play content on the room's TV screen from my laptop or gaming console, via an HDMI connection!  (And I have to imagine that this would be cheaper and easier for hotels to implement than adding more "pool complexes" to their properties!)

Sunday, April 18, 2021 Updated With New 2021 (Unicode 13.1) Emoji

My single-page website for quickly and easily getting a particular emoji character onto your clipboard for pasting into a document you're composing,, has been updated with the new 2021 set of emoji from the Unicode Consortium. New emoji include "face in clouds" and "heart on fire".

As of the time of this writing, these newest emoji likely aren't available on your preferred platform yet, but when they are, will be ready!

Incidentally, isn't strictly needed anymore. The latest versions of both Windows and MacOS now include built-in emoji pickers that can be accessed from any place where you're composing text. The keyboard shortcuts to access them are Win+Period (Windows) and Cmd+Ctrl+Space (Mac).

Still, I find myself still regularly using while composing text on desktop platforms. You're welcome to use it, too! Enjoy! 😁

Friday, March 12, 2021

Jon's Five-Monitor Work-From-Home Desk Configuration: 2021 Edition

Although many folks are working remotely these days due to the Coronavirus pandemic, I've had longer than most to accumulate a nice desk configuration!  For the past seven years now, I've worked remotely full-time from my home in Michigan.

Here's my current desk setup:

Five-monitor Mac desk configuration

My current primary computer, a 2019 16" MacBook Pro, is the screen on the far left.  Of the other four monitors, two are connected directly to the MacBook Pro via USB-C cables; the remaining two are connected to my OWC 13 Port Thunderbolt 3 Dock -- that's the component sitting on the desktop behind my keyboard.

Delta since 2018

I didn't purchase and assemble this setup overnight! Instead, I've been able to add a part every year or so, building it up over time. This current setup is a nice improvement over my 2018 desktop configuration:

My 2018 monitor configuration

The current setup replaces that smaller rightmost monitor with a new portrait orientation monitor; the remaining monitors are the same, just repositioned.

Application layout

One reason I've opted for this setup of multiple monitors, as opposed to a single ultra-large monitor -- in addition to having had the ability to slowly purchase and assemble the setup over a period of years  -- is that the multi-monitor setup lends itself really nicely to having a particular application always be placed at a particular location.

It is a really nice luxury to just be able to just look at a particular screen to get a view of a given application, instead of having to Cmd+Tab or otherwise search through multiple windows to find an application that I'm looking for. 

Here's how I typically have my applications arranged in this current setup:

5-monitor application arrangement

Leftmost monitor

As mentioned above, this is the MacBook itself. I have it up on a stand, both to better match the vertical positions of my other monitors, and to get a little bit of desk real estate back to store some small items under the stand.

I typically have my (multi-tab) iTerm2 terminal window running on this monitor, as well as a music player in "mini" mode. 

I've been using the Mac App Store free app MiniPlay for the latter recently, and it's been working really nicely. In addition to being very similar in size and UI layout to my own "Schneider's Eleven" minimal skin for Windows Media Player that I previously used on a daily basis when I primarily developed on a Windows machine, it seamlessly supports both music player apps that I typically use, Music (formerly iTunes) and Spotify.

Left portrait-orientation monitor

This is where I view and write code! As I've blogged previously, I find it really productive to be able to view a long swath of code at once on the tall monitor.

 (An exception is when I'm pairing with someone over screen-sharing software, since most of my peers don't have a portrait-orientation monitor. In that case, I typically drag the IDE over to my primary monitor.)

Bottom landscape-orientation monitor

This is my "primary monitor". I keep the Mac's Dock on this monitor. I typically have my main web browser window open on this monitor. 

I'm currently composing this blog post on this monitor. 😁

Top landscape-orientation monitor

As mentioned in my 2018 post (and more visible in my 2018 image above), this monitor is mounted on a pole anchored to my desk via a WALI Extra Tall Monitor Desk Mount. 

Sometimes I've got nothing happening on this monitor -- when I'm sitting, I have to crane my neck just slightly to look up at it -- but when I need a place to put some secondary window like a database/SQL query window, a code diff, or just another browser window, it's another place I can do so without obscuring any of my other open apps!

Right portrait-orientation monitor

This is where I keep my communications windows. The bottom portion is dedicated to Slack while I'm at work. I use the top portion for other communications apps like Messages and WhatsApp.

Phone stand

The small, portrait-orientation "screen" in front of my MacBook is an iPhone 12. I finally caved and bought myself a new phone, replacing my venerable iPhone 6S. Despite the lamentable lack of headphone jack technology, I have been otherwise been enjoying the improved battery life and memory on the new device.

The phone's "stand" is a magnetic wireless charger from Choetech, which takes advantage of the new MagSafe technology in the 12. (The phone's position while on the stand doesn't actually obscure any of the area of the MacBook monitor behind it when I'm sitting at my desk, as it appears to do in this photo.)

The app that you can see running on the phone is my own free App Store app, Desktop Journey.  I developed it because I wanted my phone to be doing something useful while sitting and charging in its stand on my desk. I figured an at-a-glance "dashboard" view of the time, my next calendar event, my current Reminders app reminders, the weather, and occasional prompts to stand up and stretch or do a few reps of an exercise would be ideal. 

After failing to find such an app anywhere on the App Store, I decided to build it myself, and created Desktop Journey! It's a completely free and ad-free app (except for the weather functionality, to help cover my costs for the non-free weather API that I'm using to query global weather data), and collects no personal data. Free Desktop Journey download on the App Store.

Small top-left screen

The last remaining little screen, at the top-left, is a Raspberry Pi running Pi-Hole, a new addition to my home network, a Christmas gift from my wife Melissa this past winter. With my local high-speed Internet monopoly having started imposing a bandwidth cap -- which my family regularly bumps up against, just through normal everyday usage -- I'm happy to take advantage of the opportunity to save some bandwidth by declining to send outgoing telemetry and requests for ads from my local network.


The image is of Peyto Lake at Banff National Park, which my family had the good fortune to be able to visit in 2019. Banff -- along with the other nearby national parks in Canada, Jasper and Yoho -- is the most beautiful place I've ever visited. I hope to be able to go back and visit again someday!

As MacOS still lacks built-in support for spanning an image across multiple monitors, I used the App Store paid app Multi Monitor Wallpaper to generate this wallpaper.

Friday, February 19, 2021

Easy one-handed Control Center and Notification Center access on iPhone 12/11/X

 I finally opened up my wallet and replaced my aging iPhone 6S -- fare ye well, headphone jack -- with a new iPhone 12. It's my first purchase of a brand new iPhone since buying an iPhone 5S, all the way back in 2013. (I had bought the 6S secondhand, via a seller on Swappa.)

While I'm finding the 12 to be a nice upgrade, one big usability regression jumped out at me in the early going: The difficulty of activating Control Center. (That's the screen where you can adjust volume and brightness, enable/disable wi-fi and Bluetooth, and so forth.) 

On the 6S, activating Control Center was easily done while holding the phone in one hand, via an easy swipe up from the bottom of the screen. On the 12 -- which has no Home button -- that gesture now navigates to the Home screen. Control Center is now activated via a swipe down from the top-right corner of the screen. 

The 12's larger size makes that gesture difficult to perform one-handed. Compounding the problem, although I'm right-handed, I use my left hand when operating the phone one-handed -- so that top-right corner is unreachable without substantially shifting my grip on the phone (which increases the risk of fumbling it).

With its generally very good support for accessibility features, I figured Apple would provide a simple option somewhere in Settings to swap that top-right-corner gesture for the top-left-corner. Unfortunately -- at least as of iOS 14 -- there is no such option.

The solution

To the rescue comes a configurable accessibility gesture pointed out by @temptertemper: The "Double Back Tap". Under Settings → Accessibility → Touch → Back Tap, you can set a firm "tap tap" performed on the back of your phone to perform one of a number of options -- including bringing up Control Center!

That double-tap is easily done with an index finger, while still securely holding the phone in a one-handed grip. In the early going, I've found the double-back-tap to be a much easier-to-perform alternative to the stock swipe-down-from-top-right-corner gesture.

Triple-back-tap is also assignable from the same place in Settings. I've assigned that to bring up Notification Center, which is also by default activated by a swipe down from the (distant) top of the phone screen.

Thanks to @tempertemper for this great suggestion, which I expect to be a nice quality-of-life improvement in daily use of my new iPhone!

Monday, February 15, 2021

Interview: Amanda Schneider Milne, Software Developer / Manager / Executive


I’m super proud of my mom, Amanda Milne! She was among the first to earn a degree in Computer Science from the University of Michigan, has worked in the field of software development her entire career, and worked her way up to become president of her own custom software development company here in Michigan, and later on, a Senior Vice President of a Fortune 500 company.

This is an interview with Amanda about her early years in software development, back in the 1970s and 1980s, and about one project in particular that I remembered her doing from my days as a grade school kid.

You were among the first Computer Science graduates at the University of Michigan, right?

I was! I graduated from Michigan with a Computer Science Degree in 1974. The Computer Science degree program was fairly new back then. We had no textbooks, just mimeographed handouts (which I wish I’d kept!). 

Nearly all of our work was done on an IBM System/360 Mainframe Computer, primarily by submitting our code on punched cards via a remote card reader. I took one class on a DEC PDP-8 Mini Computer, and I learned to build logic boards by soldering chips in an elective Electrical Engineering course. The programming languages were primarily Fortran and Assembler. 

Only 43 of us graduated with a Computer Science degree from U of M in ‘74. Fun Fact: I graduated without ever actually seeing the IBM System/360 Computer!

What did you do after graduation?

I worked as a Programmer for various companies (and customer accounts). 

My first assignment was to single-handedly write the real time process control code to run a custom carburetor test stand for Oldsmobile.  The computer was a16-bit Modcomp mini computer with 16K of memory, and no disk! When I started that project, I knew nothing about Modcomp computers, their operating system, their language -- or what a carburetor was! 

I wrote the code in an Assembler level language, and loaded my compiled code into the machine via paper tape.  When I found a bug in my code, I wrote the fix, de-compiled it by hand, converted it to hexadecimal, and keyed the fix into memory using 16 binary switches. 

Writing process control code was fun because I could actually see my code working as it opened and closed mechanical valves, and turned stepping motors. I was only 21, but I did successfully complete the project.

That’s pretty amazing! As a developer these days, there are so many layers of indirection between code we write, and what’s actually happening at the hardware level.  You actually got to add instructions to memory directly, with no steps in between! (And without Google or StackOverflow to help you know what to do!)

After that, you worked at a startup called Custom Software Services, right? What was your role there?

In 1981, Custom Software Services, a programming consultancy startup in the Detroit area, hired me to work part-time on a two-month project. I only had 3 years of programming experience at the time, because I took a 4-year break when you and Jeremy were born. My assignment was to single-handedly develop a daily Profit and Loss system for a local restaurant chain on an IBM Series/1 Computer (a 16-bit mini computer with the EDX Operating System), using a low level language called EDL. At the time, CSS had only one employee. After the project was completed, I stayed on to work on other projects. After 4 years, I went full time as a programmer. 

Highland Superstores was a big appliance sales company back in the 1980s, right? What was the project that you did for them?

Highland Superstores was a national chain of 100 appliance stores selling TVs, electronics, washers, dryers and refrigerators. In 1987, the Internet did not yet exist. Highland Superstores wanted a networked Point of Sale (POS) system in all of their stores, and headquarters. The system would track their inventory, daily sales, gross margins, commissions, and so forth.

Highland Superstores wanted a system to enable the salespeople on the floor to negotiate pricing, view how a lowered price negatively affected their own commission, check accurate up-to-date store inventories, sell the items, and schedule delivery. 

Before this system, a salesperson had to leave the customer (or make a phone call) to check inventory in the back of the store. Salespeople had no idea if the next closest store had the item in stock that his customer desired. It was important to Highland to have up-to-date inventories of ALL of their stores available in each store. With this feature, a salesperson would be able to sell the item, and send the customer to the next closest store to pick it up.  

The POS system in each store had to communicate and update the headquarters computer every 15 minutes. It also had to download inventory updates from the other stores every 15 minutes.

How did IBM get involved in this project? 

Highland Superstores had spent 2 years working with another local software company to develop the POS system using PC LANs, but it wasn’t working and Highland was getting frustrated. 

Rick Teague, Highland’s IBM Salesperson, told them that putting an IBM System/36 in each store, all communicating with an IBM System/38 at their headquarters, would work.  The hardware was up to the job.

With no Internet, how did this kind of communication work? Dial-up modems?

Yes, dial-up modems. The POS system had to dial the headquarters IBM System/38 every 15 minutes, establish a connection, upload the local store inventory, and download updated inventories from the other 99 stores.

You mentioned Highland was getting frustrated? What was the issue with the other company’s in-progress implementation?

Well, they couldn’t get their PC LAN system to work. If I remember correctly, their software could perform  the in-store functionality, but it had a great deal of trouble getting the communications with headquarters to work properly and quickly.

So how did you, and CSS, get involved in this project? 

The IBM System/36 hardware solution was a good idea, but there was no POS software to run on that platform. Since Highland Superstores had been paying another software company, they weren’t willing to fund another software development project on a different platform.

Rick said, “If I can get your POS system up and running in one of your stores on an IBM System/36, will you buy 100 System/36s?” Highland Superstores said maybe, provided they didn’t have to spend any money for the trial, and that the system would be up and running in the pilot store in 90 days! 

Highland Superstores also made it clear they were keeping their focus and money on the PC LAN-based solution. The new IBM System/36-based solution would have to out-perform a solution that had a two-year head start! 

IBM approached several software companies in the Detroit area, and asked if they would be willing to develop the POS system for free, demonstrate that it worked, and then hope to get paid afterwards. They all said “No”... except my boss, Don George, who owned Custom Software Services.

Our company had about 15 employees at the time, business was slow, and we were in danger of going under.  Don was an ex-IBM Systems Engineer, so he knew the guys in the local IBM office. He sat down and had some serious discussions with them about the project, and then he decided to take on the risk and tackle the project. I got involved when my boss asked me to manage the project.

That does sound like quite a risk! What was it like working on a project, and leading a team, that would have a zero payout for everyone involved if it wasn’t successful? 

It was extremely challenging. We couldn’t afford to pay a team of developers to write the software, so Don came up with a creative idea to share the risk. He offered several local developers a double-or-nothing deal. If they signed on with our project, we’d track all of their hours, and then pay them double their going hourly rate at the end -- if it was successful and Highland Superstores purchased it. If not, they’d get zero. 

Surprisingly, he found 7 developers who were willing to take his offer, but they all had to keep their day jobs. So, they worked on our POS project nights and weekends. 

And you were in charge of managing this? How did you approach that? 

I was 34 years old, and this was my very first project management assignment. The first thing I did was to read 3 books on project management. I knew that the project would be very difficult, and that I’d have to use every bit of education, new knowledge, guts, and perseverance I had! 

As it turned out, it was even more stressful, and challenging that I’d first thought. The project lasted 5 months, not 3. At the 90 day mark, we had accomplished enough that we earned another month or two as a grace period. 

The developers and I were all working 2 shifts, 7 days a week. Over that summer, none of us had any family time, or vacation time. There were many days when I came home late at night, dropped my clothes on the floor and fell into bed. Then, the next morning, I showered, grabbed clean clothes and was gone again. We worked 2 shifts, 7 days a week. The good news is that as the project progressed, everyone had more skin in the game. The developers saw their potential earnings mount, and became more and more unwilling to walk away. 

When we had the code (in the RPG language) running on a remote IBM System/36, IBM installed a System/36 in one of the Highland Superstores (for a free trial), and we spent about a month troubleshooting and debugging the software in a live store environment.

That must have been exhausting! How about the flip side? Was there a best part about the project?

Yes! No scope creep! This was the only software development project I’ve ever worked on with zero scope creep. 

We didn’t realize at first, how important this would be, but it was one of the biggest contributors to the project’s success. There was no scope creep because Highland Superstores had already invested 2 years in hammering out the functionality of the system. All we had to do was design and develop our code to exactly produce the same POS interface, files and results. 

Also, we were all working for free, so we had added incentive to stick to the exact specs. There were many, many times during the project that someone involved would suggest a change or improvement, and my answer was always ‘No’. 

The project team was all men… except for you! What was that like?


Amanda (far right), her team, her boss (Don, far left), and a few IBMers at the project kickoff meeting.

I don’t know what it was like for the other people involved. I can only tell you that I really didn’t think much about being the only woman, except on two occasions. 

The first time was at the beginning of the project. I knew I would be managing a team of all men, and that I had never managed a project of this scale, scope or importance before. I knew I had to establish credibility - and authority right from the start. So, I called a Project Kick Off Meeting and I invited all of the developers, and the local IBMers. 

At the meeting, I laid out the project plan, the timeline, the assignments, and the project ground rules. After the meeting, one of the IBMers told me it was the best kickoff meeting he’d ever attended.

Amanda with 2 IBMers and John, a fellow employee

The second time it mattered to me that I was the only woman, was during the roll-out phase of the project. At that point, our software had been awarded the contract, and I had a team working at the Highland Superstore headquarters to help install the software in all of their stores. 

We were all working long hours, and it was very stressful - getting the hardware and software up and running in 100 stores nationwide before the Christmas shopping season. One evening after most people had gone home for the day, one of the IBM Managers got into a heated argument with one of the Highland Superstores Managers -- they were yelling and screaming at each other so loudly, someone called security to stop them before they started hitting each other. 

A week or two later, I raised my voice in a meeting, and I was accused of ‘being emotional’!! No one ever said the screaming men were emotional, but I was accused of being emotional, which I thought was very unfair.

That’s super lame. I’m very glad there is generally more awareness in corporate environments in general, and in the software development industry in particular, about the unfairness (and lameness) of those kinds of double-standards. I’m hopeful that if either of my daughters get into the industry when they grow up, the state of things will be such that it won’t even cross their minds to be concerned about that kind of issue.

Were there any particularly memorable challenges during the project? 

Lots! At one point, a certain bug was making us all crazy. I solved it when I noticed -- on the old dot matrix print-outs we used at that time -- that there was a letter O where a 0 (zero) was supposed to be in the code. 

The worst was when my lead developer, who was working on the critical path, decided to walk off the project when we were about ⅔ through development. That surprised me, and freaked me out. Especially since, although I was a developer, I didn’t know RPG, and wasn’t able to jump in and take over his code myself.  Luckily, another of the developers on the team agreed to step up and successfully took over his portion of the code. 

Another time, in the middle of the project, Northwest Airlines Flight 255 crashed shortly after takeoff from Detroit Metropolitan Airport killing all six crew members and 148 of its 149 passengers -- including a family member of one of our software developers. We were all horrified, but we soldiered on. 

What wisdom would you share with people working on software projects today?

Make sure to keep an open mind, open communications, and always hear both sides of the story. 

I remember one time, the customer came to me with “evidence” that one of my developers had deliberately caused a significant problem in the code. Upon seeing the evidence, I too became very upset, and thought about firing him on the spot. 

But, I calmed myself down and went to talk with the developer in question. I asked him to tell me about the day, and code in question -- just tell me what happened. His description threw a completely new light on the situation, and made it clear that he was not trying to sabotage the project. I was then able to explain it to the customer, and keep the project going. 

Another lesson is one I learned was from Rick, the IBM salesperson. One evening, after a team meeting at my home, we were in my driveway talking about the odds that Rick had to surmount to get this project started. With great persistence, Rick had spent months and months calling on Highland Superstores before they even began to consider his pitch, and a possible IBM-based solution. 

During our talk. Rick picked up a basketball and started bouncing it while he walked down my long, curved, sloped driveway. When he neared the street he said, “You have to be optimistic. You’ll never get a parking spot closest to the mall entrance if you don’t drive down the first row. You have to take a shot.” Then, without turning around, with his back to the basketball hoop, he shot the basketball one-handed, over his head -- and the ball swooshed through the basket!! I will never forget that moment!

How did it all turn out in the end?

Well, it was a true horse race right to the end. After 5 months of working grueling hours, Highland Superstores made the decision to scrap the PC LAN-based system, and purchase ours! 

Testing the software in a live store environment

We were ecstatic. We all celebrated with an impromptu party. Over the following months, Highland Superstores successfully installed an IBM System/36 and our software in all 100 stores. 

Rick, the IBM Salesperson was handsomely rewarded with commissions on all of those machines. My boss, Don paid each of the software developers double their hourly rate for every hour they worked on the project. 

And me?  Don promoted me to VP of the company, gave me a $25,000 bonus, a new car, a Caribbean sailing vacation, and the opportunity to purchase a minority stake in his company -- which I did!

I’m so impressed that you were able to do all of that! I’m super proud to have been able to follow in your footsteps, in having a career creating software to solve problems for people!

Thanks, Mom, so much for taking the time to do this interview and share this part of your story!