Thursday, October 25, 2018

Soylent Eggnog

I usually drink Soylent for 2 meals every day. With the arrival of fall, I'm in the mood for some 'nog. Here's the recipe I use to make a Soylent that tastes pretty darn close to the flavor of eggnog.

Mix up 12 oz of Original Soylent like normal.  Add the following:

  • 3 tsp sugar
  • 1 tsp vanilla extract
  • 1/2 tsp nutmeg
  • 1/4 tsp rum extract
  • pinch of cinnamon

Tuesday, August 14, 2018

4X Solitaire

Ever since Civilization came out, I've enjoyed the 4X game genre.  If I'm in the mood for a simple 4X experience, I'll play Battle of Polytopia with its small map and sparse tech tree.  Sometimes that's even too complex, so I made a solitaire game with a 4X feel.

Setup


Start with a standard deck of cards.  Remove the jokers. Set aside the kings, face up:

Choose any king. This is your king and your suit. Turn this king face down among the other kings. Here I chose the king of diamonds:

 Leave the kings aside and shuffle the rest of the deck.  Deal three cards, face up, next to each other at the edge of the table.  This is your arsenal:

Turn over the king stack onto the deck.  All cards in the deck will be face down except for your king.  I often find it helpful to manually insert the kings into the deck at random locations like this (my king is sticking out to highlight that he remains face up):

Shuffle the deck.  Deal cards, face down, in a 7 by 7 grid. Of course, your king will still be face up:

Play


The 7 by 7 grid is the map.  Face down cards are parts of the map that you haven't discovered yet.  My initial territory is any face up card matching my suit.  When you start, your territory is only a single card; namely, your king.

To win you must discover the entire map and make it your territory. Try to do it in as few turns as you can.  On each turn, you can perform one of the following moves.

Explore


Turn all cards face up which are adjacent to a chosen face card of your suit.  A card that's fully surrounded has 8 adjacent cards.  A card near the edge may have fewer adjacent cards.

Here I've used my first move to explore the map around my king.  I expanded my territory by one square (the 6 of diamonds) and uncovered four squares of enemy territory.

Travel


You can swap a face card of your suit with any other space in your territory.  In the image above, I could move my king to the right by swapping him with the six of diamonds.  You don't have to travel to an adjacent space, that's just how the layout appears in this image.

Capture


You capture a space, making it part of your territory, by attacking it with a card in your arsenal.  The card in your arsenal must have the same suit as the card you're attacking.  Your arsenal card must also have a higher rank.  Once a card is captured, remove it from the map and place it on top of the arsenal card that captured it.  The captured card is now your arsenal card.

An arsenal card of your suit can be used against any suit.  Although, once it has performed its first capture, it's permanently fixed at that new suit (since the captured card is now your arsenal card).

For example, in the position above I used the eight of diamonds to capture the six of clubs. That gives this position:

The vacant space in the map is now part of my territory.

Conquer


The final move available during a turn is to conquer an opposing king.  To conquer an opposing king, he must be face up and at least 51% of his adjacent spaces must belong to your territory.  For example, if a king has eight adjacent spaces, at least five of them must belong to your territory.

Once you've conquered an opposing king, remove him from the map and place him near your arsenal.  All face up cards of his suit are now part of your territory too.

Tech Tree Variant


Many 4X games have a technology tree. Learning a technology grants the player a new ability.  A tech tree can be added to 4X Solitaire with a slight adjustment to the rules above.

After dealing your arsenal, but before shuffling kings into the deck, choose one arsenal card to be your technology.  The technologies are determined by the suit of the card you choose:

  • spades - flip over one card anywhere on the map
  • hearts - conquer with only 50% of a king surrounded
  • diamonds - explore an entire row/column instead of adjacent cards
  • clubs - capture any enemy card, except a king
Set your technology card aside and deal another arsenal card in its place.

If you choose to play your technology card during the game, you can play it only once and using it counts as a turn.

Friday, June 29, 2018

SMS over IRC

A couple years ago, I moved from Google Voice to custom infrastructure.  That system used XMPP to send SMS.  Now that I've moved from XMPP to IRC, I needed a new set up for sending SMS.  This post is about a small proxy I wrote to translate between SMS and IRC protocols.

When I'm connected to our family IRC server, I can send a message to any nick that's a valid phone number.  The server converts that message into an HTTP request to Twilio to send an SMS to that number.  I map our IRC nicks to our phone numbers to correctly set outgoing caller ID.

Some of my extended family (mom, dad, siblings, etc.) aren't connected to our IRC server.  I contact them frequently enough that I didn't want to use their phone numbers in IRC, so I've given them aliases.  The server pretends that they're on IRC under those aliases.  For instance, when I send a privmsg to the IRC nick "mom", it converts that into a Twilio API call that sends an SMS to my mom's phone number.  As far as my IRC clients are concerned, my mom is on IRC.

When sending SMS, Twilio provides helpful status updates via HTTP.  I've translated some of those into IRC messages too.  In an IRC client, it looks like this:

[12:01:02] michael: Any big plans for your birthday?
[12:01:03] mom: ⌛
[12:01:07] mom: ✓
[12:05:22] mom: I'm going to Cirque du Soleil with some friends


The first two emoji are synthetic messages generated by the IRC server.  ⌛ means, "Twilio has successfully delivered the message to the recipient's phone company" and ✓ means, "the phone company delivered the message to the recipient's phone".

Incoming SMS work as you'd expect.  The phone number to which the SMS was sent, determines which IRC user receives the corresponding privmsg.  If the phone number has an alias, that alias is used as the IRC nick; otherwise, the phone number is used as the nick.

It all works really well in practice.  I can pretty much pretend that everyone I care about is on IRC all the time.

Twilio doesn't support group SMS, which is too bad.  I'd love to translate those into ad-hoc IRC channels.  Twilio supports MMS, but I receive them so rarely that I haven't added IRC support yet.

The code is available, in case you're interested.  It's a couple hundred lines of Go.

Thursday, June 28, 2018

Dadurday

As my five kids have gotten older, I've noticed the world and its activities encroaching more and more on our family time.  While we were traveling in Europe, we changed cities every two weeks and had no outside obligations, so our family spent lots of time together.  It was great.  Our friendships strengthened and we grew closer.

As we prepared to return home, my wife and I decided that we would establish the first and third Saturdays of each month as "Dadurday".  A day when the kids and I would block everything else out and just spend time together: no chores, no outside obligations.  So far we've watched a movie, played board games, read stories, watched the World Cup, collected coal to try to start a fire, had sword fights, and gone on walking adventures.  The kids seem excited about the whole thing and are constantly suggesting activities for the next Dadurday.

The outside has tried to occupy these days, as we suspected it would.  I suppose it's a corollary to Parkinson's Law that non-family activities expand to fill all available time.  Fortunately, Dadurday was on the calendar first.  When other activities try to crowd in, we politely decline and say that we have a previous obligation.  Just knowing that these unstructured, playful days with my kids are waiting for me every couple weeks makes me smile.

Of course, my kids have friends and other interests.  Most of the time they can be scheduled away from Dadurday, but sometimes there's a conflict.  As a family, we decided that each kid gets 3 exemptions per year that let them skip Dadurday if they want to do something else that day.  So far three kids have spent four exemptions total, all to attend birthday parties.

Between Dadurday and our family night on Mondays, I suppose that some people would think it's too much time together, but it's a great balance for us.

Tuesday, June 12, 2018

Goodbye XMPP. Hello IRC

Up until 2 years ago, my wife and kids and I stayed in touch with Google Hangouts.  I disliked being dependent on a third party, being unable to program the system to match our needs, and not having access to it on all the devices we care about, so we switched to XMPP.  It was an improvement and has served us well for the last couple years.

A couple months ago, we started seeing large delays in message delivery times.  We were also getting extended, repeated disconnects from the XMPP server.  Our messaging stack is selfhosted and open source, so I prepared for a debugging session.  As I got ready to dive in, I had a nagging feeling that there was way too much code at play here.  We ran Prosody, which is a great XMPP server, but it's 35k lines of code.  My SMS to XMPP gateway adds another 1k.  All we wanted to do was send small pieces of text between 7 people and set up some chat rooms.  That much code felt like overkill.

After a few hours work and 500 lines of Go, I had a custom IRC server with a built in BNC.  With another 200 lines, I had a working SMS to IRC gateway, so I can send and receive SMS from my IRC clients.  Obviously, setting up chat rooms was painless.

We had some minor glitches during initial deployment, but because the whole system is so small, it was a cinch to debug and repair.  It's been really solid since then.  The minimalism is refreshing.

The code is available in case you're interested. It won't compile by itself since it's part of a larger family server (git, movies, personal assistant, Asterisk dialplan, etc).

Wednesday, May 09, 2018

Easier random numbers in Go

Abstract: Replace import "math/rand" with import "github.com/mndrix/rand" and get high-quality random numbers every time.

Like many Go developers, I use Go's math/rand package for generating random numbers.  That package defaults to a predictable stream of outputs, so you have to seed the generator if you actually want random numbers. I'm embarrassed to admit how often I've forgotten to seed the generator.  In large projects, I see the generator being seeded multiple times across different files because everyone wants to be certain the generator has been seeded.

Even if I do remember to set a seed, I often get hung up on other questions like:
  • is my seed good enough?
  • are these random numbers high enough quality for my application?
  • do I need to reseed the generator after a while?
  • how often?
I just want random numbers without all this complexity.

More than 20 years ago, OpenBSD developers were seeing similar problems.  In 1996, they addressed it by introducing arc4random.  You call the function and get a high-quality random number every single time.  There's no seeding, no failure mode, no need to consume file handles, just random numbers.  It's refreshingly simple.

I wanted that same simplicity in Go, so I wrote github.com/mndrix/rand.  It's just a thin wrapper around Go's crypto/rand which gives those high-quality numbers the same clean interface as math/rand.  In many cases, you can just replace import "math/rand" in your code with import "github.com/math/rand" then delete your calls to math.Seed() and everything works.

Saturday, March 17, 2018

World tour: Morocco

Since leaving Hungary two months ago, we've been in Morocco.  I tried to visit North Africa sixteen years ago during a trip to France and Spain, but was thwarted at the last minute by some trouble with a train conductor.  That near miss made me all the more excited for Morocco on this trip.

We started in Marrakesh near the base of the Atlas Mountains.  I knew there were mountains in Morocco, but I had no idea they were so large.  The tallest mountain in the range is 13,670 feet above sea level and Marrakesh is at 1,500 feet so the mountains have a larger prominence than those along the Front Range or the Wasatch Front.  The Atlas were beautiful and covered with snow.  We didn't bring enough clothes to stay warm in the mountains in February, but I would love to come back to Morocco and hike all over the Atlas someday.

The average wage in Morocco is quite low, so we found it affordable and convenient to hire a private van to drive us between cities instead of using the trains.  The highway system in Morocco is modern and well maintained.  Their rest stops are some of the best I've seen.  Each one had a selection of food vendors, at least one good playground and a small mosque where travelers could pray.  If we visit Morocco again, we'll definitely rent a car.

Anyway, Casablanca was next in line.  Our apartment there was just a couple blocks away from the tallest mosque in the world.

The mosque was really pretty.  It reminded me how much I like Arab architecture with its clean lines and subdued, natural colors.  The mosque was surrounded by quiet gardens:

Between the gardens were covered walkways.  Each one had ceilings with hand carved and hand painted decorations:

Other than the mosque, Casablanca, like most Moroccan cities, is not a destination for sightseers.  Unlike Europe, there isn't much of historical or architectural interest.  We were quite happy with that.  We just enjoyed walking through the city and living our life alongside everyone else.

After Casablanca, we went all the way north to Tangier.  Our apartment was on a hill overlooking the Strait of Gibraltar.  It was cool to see Spain on the other side and to watch all the container ships crossing between the Atlantic and the Mediterranean.  It rained almost the entire time we were there, but we managed one day at a nearby beach once the sun came out:

Our last stop was Fes.  Our apartment was inside the medina right next to the souq.  We went into the souq each day to buy groceries for that day.  There are hundreds of vendors, each with a small shop specializing in one or two items.  You buy bread at one place, eggs at another, fruit at yet another, etc.  It's sort of like a farmer's market the size of an entire city.  By the end, Kinsey was a pro and knew which shops had the best food of different kinds.  The fruit vendor that she preferred recognized our children as they walked through the souq and would give them free strawberries as they passed by.

Overall, I liked Morocco.  If we visit again someday, I'd try to spend most of our time in the rural areas.  The cities are a fun experience once, but the beautiful mountains and open spaces called my name.  I would also love to cross the Atlas and spend some time on the Saharan side of the country.  There's so much beautiful world and so little time.

Next stop: New York and Las Vegas