Getting Things Done, A Guide

I was inspired by Sarah Knight‘s, Get Your Sh*t Togther book here, and optimized her ideas for my needs. I read it once, then reread it right after, because it was so engaging. Buy it and read it yourself.


The Way

I start with four lists. First is my ‘To-do’ list, which is my generic catch all list for ideas. The next is my ‘Must-do’ list. This is the important list that requires daily attention. I take the highest priority items in the ‘To-do’ list, and pull over the items that must be done on the day into the ‘must-do’ list. The third list is the ‘Doing’ list. I pull one item from ‘Must-do’, and pull it into ‘Doing’ until it is complete. The last list is the ‘Done’ list, that starts out empty every morning, and is satisfyingly full at the end of every day.

The To-do List

The To-do List is meant to be an open catch all for tasks and items that creep into my head over the course of a day. “Get to the grocery store…”, gets added to the list. “Get the bikes tuned”, check. “Workout for {date}”, added to the list.

Sometimes I get a random thought, and I just add it to the To-do list, if only just to allow myself the freedom to say “this is worth spending some time thinking about, but it’s not the priority now.” The value here is that those things that are ‘wishes’ become things I allow myself to treat as eventually ‘doable’, just not as important right now. This works well for my ‘I need to rewrite this in F#’ feelings.

Prioritizing the To-do list

One of the major functions of the to do list is to show me a pile of all the things I’ve been thinking about, and enables me to stack them in a simple list of most important to least important. Simple and small items tend to get to the top of the list, if only because I know how to do them quickly, and the process of getting things done feels good.

Pro tip: Hack of the to-do list by making tasks small and manageable. “Spend half-an-hour on Udemy course” is MUCH simpler to schedule and accomplish than “Get Better with Machine Learning and AI.”

Prioritization is function of my values. One of my values is making sure my personal finances are in shape, as they were not, for an embarrassingly long time. My to-do list consistently has budgeting and finance items right at the top of the list. Self-care with physical fitness is also a high priority for me right now, so daily physical activity takes the top spots as well.

The Must-do List

The ‘Must-do’ list is where I put top priority items from the to do list. My ‘to-do’ list will contain forty or fifty things. The ‘Must-do’ list I keep less than eight, and only add items to it as items are completed. That keeps the list doable over the course of a day, and creates a simple block around what is actually possible.

Items that are regular ‘Must-dos’:

  • Daily workout.
  • Daily budget/finances check.
  • Write for 45 minutes.
  • Log food for the day.
  • Correspondence.

Putting these items as distinct tasks allow for some distinct optimizations. A daily workout is a scheduled item that ends up on my calendar. In COVID times, that has been a Zoom session, and with the update to phase 1.5 in King County, it’s a small class session at my old gym.

Correspondence tasks can tend to elongate over the course of a day, e.g. who hasn’t spent multiple hours on a slack channel, but when I treat it like a task with a defined end things take on a different mode. Personal email is managed in the morning. Work email is managed immediately afterwords, and then once again in the afternoon. Slack communications can follow the same cadence, and once folks are aware that is how you are working, the immediacy of the medium is not as demanding as it seems.

Logging food is usually a simple task, which can <5 minutes after any meal, but can be done at the end of the day. The point is to remember the intent of the task. I log food to remind myself to measure portions and be intentional about my consumption, not to be 100% accurate down to the calorie. I accept the risk of inaccuracy (did I eat 100 grams of blueberries or raspberries in my yogurt this morning?) for time management.

The Doing List

The Doing list is the loneliest column because I allow one thing in it at a time.

I cannot multitask. At all. In order to do anything competently, I need to focus on one thing at a time.

One valuable feature of the single ‘doing’ list, is that, when my brain has wondered I can quickly glance at the doing list, and ask myself “Am I really doing what I said I’m doing?”

If I have Twitter open, and my ‘Doing’ task doesn’t read ‘read Twitter feed and get pissed off’, there’s a good chance I need to re-engage myself on what I want to spend my time on.

The Done List

The Done list is the most fun list, obviously. I start with a clean list and as I complete tasks from the Must-do list, I put them on the Done list. Initially, an empty Done list is underwhelming, but as the day progresses, it can get satisfying and full. What makes this particularly satisfying is the list ends up usually in the 15-20 items completed over the course of a day, and all of them are the priority items according to what I value the most.

I prioritized ‘daily workout’ and got it done.
I prioritized ‘developer coaching session’ and got it done.

Getting done what I chose and prioritized is empowering.


Further Optimizations

One optimization I have made to this structure since starting it has been creating two other lists to assist some work items, and a few more ‘values’ based optimizations.

First, I created a ‘work week done’ list that simply contains the tasks I have done specifically for work. This helps me write full and accurate weekly status reports for work. In work-from-home COVID times, being able to communicate what I have accomplished over the course of the week seems invaluable.

Second, I created a ‘Do Every Day’ list that I use a simple copy feature to move to the ‘Must Do’ list. This saves me the few minutes spent putting the everyday tasks in the To-Do list.

Estimates: Time-Based vs Point-Based, and when to use them.

There are two general methods for software estimates, time-based and point-based. Here are some tips on when to choose either one.

Time-Based Estimates

Time-based estimates can be tricky. They are the simplest style of estimate and that simplistic nature lends engineers to treat them flippantly. Just about every engineer has said “That’s easy, it’ll take 15 minutes” about something trivial, and then spent a full workday working on it. Time-based estimates get a bad rap from engineers, because they feel punished for missing them. Project managers like them because planning work is simpler.

Time-based estimates work best for near immediate-priority bug fixes / feature requests, and costing and budgeting breakdowns.

Point-Based Estimates

Points-based estimates are a common model in Agile shops, and are largely the same except in terms of flavor when they are requested in terms of t-shirt sizes or Fibonacci series values. They are meant to be quick estimates that give a sizing gauge for the feature without the negativity associated with a missed estimate. Points-Based estimates get a bad rap from project managers, because the statistical approach to ‘backing into’ the time something takes is not accurate enough to get an ‘on-time’ delivery of anything. Engineers like them because they don’t feel as beholden to them.

Point-based estimates work best for high-level estimates of long-term work, or as a quick way to gauge consensus between engineers.


When To Use Them

A project team will generally follow a particular methodology (Agile, SAFE, Waterfall), so you may be held to the preferences and norms of that group, but in general, follow these guidelines for successful estimating.

  • When to use a Points-Based Estimate
    1. The amount of time dedicated to estimating is low.
    2. The priority on the work is variable; your product owner will use your estimate to assist in determining its priority.
    3. Engineers have substantively different approaches to the work, or the work is largely exploratory.
    4. Your dev-to-release cycle is fast.
  • When to use a Time-Based Estimate
    1. The work is your next immediate priority item.
    2. You will use the estimate to directly assess cost.
    3. There is an understood and correct way to do the work, and will not drastically change if done by one engineer or another.
    4. Your release cycle is slow, or singular.

Mocking without Dependencies in F#

I am not a fan of mocking frameworks. Never have been. However, F# allows for some very simple mocking behavior which pretty much throws away the need for mocking frameworks across the board.

Imagine you have the following:

using System;

namespace TestNamespace
{
    public struct Item 
    {
         public string Name { get; set; }
         public double Price { get; set; }
    }

    public interface IDoStuff
    {
          int DoStuffForRealsies(int a, int b, Item y);
    }

    public class ImportantClassToTest
    { 
        public ImportantClassToTest(IDoStuff constructorInjectedDependency)
        {
             /// etc.
        }
    }
}

Depending on your needs, you might choose to make a mock of that IDoStuff interface for testing purposes. Maybe your standard IDoStuff has some database stuff you don’t want running in simple unit tests, or maybe it involves another installed dependency. If you are working in C#, you might be stuck with Moq, or NSubstitute, but in F#, you get a much nicer model.

module WhyFSharpIsBetterV2123

open Xunit
open SampleTestableClasses

let stubDoStuff = { new IDoStuff with 
                            member this.DoStuffForRealsies(a, b, y) = 
                                4
                        }

[<Fact>]
let ``To Heck with Mocking``() =
    let rd = new ImportantClassToTest(stubDoStuff)
    Assert.Equal(2, rd.MethodThatRequiresDi(5))


A nice quick implementation of the interface without any mocking extra dependencies, or any extra libraries to maintain. Just use your F# compiler, unit test library of choice and go.

The Wine We’re Drinking

I live three miles away from Woodinville, Washington, a town with over two hundred wineries in it, including two major labels and a smattering of medium sized ones. Living here and not enjoying wine is like living in Colorado and not skiing. The grapes are typically grown in eastern Washington, but the wines are produced here, or are simply sold here in a tasting room. As it is a very short drive away, a frequent weekend activity is to stop into a local tasting room. With the COVID19 pandemic, however, the tasting rooms have been closed since mid March until this weekend, when we have FINALLY been able to come back. These are the wines we were able to try this past weekend.

1. Lord Lion

We started at Lord Lion, as we had a club release pickup waiting for us, and we didn’t know precisely how tasting would really work in phase one point five, but the tasting was as wonderful as always.

We went in, sanitized our hands, and were directed to a table a good distance from the other patrons. We were handed small glasses, and proceeded a flight of six newly released wines. Aside from the distance the poor folks had to wander about to pour wine, there really was not much different between pre-COVID times and post-COVID tasting.

One thing we especially love about Lord Lion is that Graham releases wines later than other winemakers in the area. This recent release included a 2014 Petit Sirah, a 2015 Malbec, a 2015 Cabernet Sauvignon, and a 2016 oaked Chardonnay. Other wineries we visited were in the middle of their 2017s and 2018s releases. If you’re noticing that ‘everyone has the same stuff’, Lord Lion has an wonderfully atypical selection.

There are many things things to sample there. Graham always does a fantastic Viognier, and the 2019 was lovely, if a bit sweeter than the year before. His 2019 rosé of Sangiovese has been lovely for the past two years. The star in this release was the 2014 Petit Sirah. Full, inky dark, and lovely paired with a ribeye, or even something like a beef short-rib.

2. Adrice

Adrice was our second stop. Frankly, we stop there fairly often. With phase one point five, they were able to really open up the tasting room with large tables, a bar. and food served! Heather and I stopped in after calling ahead to make sure they could fit us in.

Heather waiting on pour number three at Adrice.

I do not have enough data to say for absolute certain (still working on collecting that), but Pam from Adrice may be one of the top 3 winemakers in the state. She simply does NOT make a bad bottle of wine. Her cheap stuff is great, and her expensive stuff is absolutely worth it! She is one of the few local producers that I will happily spend $75 on a good bottle for, although as I am budget conscious, I do enjoy my club discount for that particular bottle.

The tasting included a flight of six wines, and Heather and I also grabbed two charcuterie plates to keep up our strength. The takeaway favorites were: a damn near perfect 2019 Sauvignon Blanc from Yakima Valley; an award winning 2017 Red Blend called ‘Lift Off’ (which is a crazy steal at $25 a bottle); and a beautiful 2017 Malbec. Pam also poured us a pre-sneak-quel of a Cab / Barbera blend she’s got coming out in July, that will be some lovely stuff.

3. Long Cellars

Our final stop of the day was to an old favorite, Long Cellars. Jason is a mad scientist back there, but when he makes contact, he hits nothing but home-runs. Never one to stick with the same-ole ideas, the trick to tasting Long Cellars is to taste not only what the wine is now, but what it will be in 5-8 years.

Tasting room welcomed us warmly again, with a giant Frankenstein statue right up front. The room is small, but we were able to sneak into a table in the back, where we had been to two Long Cellars-hosted burlesque shows. Barrels everywhere, Heather was tempted to hunt around for a barrel thief, and eventually found one hidden away.

Be careful about putting Heather near the bottles, Jason!

We tasted two whites, and two reds before our daughter called and requested a pickup from a ‘social distance pickup’, so unfortunately our tasting was cut short. The steal of the show was a 2018 Cabernet Sauvignon, which tasted like fresh strawberry jam, which was unique and intriguing for a Long Cellars Cab. You could pair it with a light salad, and it wouldn’t be out of place. It was inexpensive, fresh and fruity with that classic peppery pull at the end that let you know it was a Cab. His 2018 Reserve Malbec proved absolutely wonderful and deep to finish the tasting, but as I said at the beginning, the best part will be waiting on it.

Coaching Engineers – A Review

One of my regular responsibilities at my new job at the Credit Union is coaching developers, engineers, sdets and QA folks. Today, I got to be involved in three different coaching sessions that all had unique subjects and discussion points.

Session 1: How to get to Senior – Developing Expertise

This is a fairly common situation. A developer wants to go from Software Developer to Senior Software Developer.

The process of making Senior Software Developer generally comes down to adding more responsibility and influence to your day-to-day job. To get to a senior role, you can do one of the following:

  1. Take on a team lead role. In this case, you are the point person and responsible for more of the project work itself. You T-shape your skill set, but become the primary point person for the whole project.
  2. Take on a manager role. In this case, you’re trying to mentor and grow the skills of the folks around you. You may not be directly responsible to all the functions in the project, but you help and mentor those folks around you.
  3. Take on an expert role. In this case, you target getting deeply technical and specialized. Your plan is to become a known leader and expert on a particular technology.

The developer in question was interested in learning more about this third pattern of developing her expertise, and what it would take to continue that progression. She expressed interest in web user interfaces with Angular, and spent the session showing me what she had learned and worked on, and where she was going next.

To coach, sometimes you just need to be the accountability buddy.

Session 2 : Whose Design is Right?

In this session, a team of software developers had some questions about the nature of their solutions. They did not agree about the approach to a problem, and this particular session was with one side of that argument.

Side note: I love these sorts of discussions. Folks getting passionate about the way they choose to solve a problem is WONDERFUL.

The best part is that there was not a clear winner in the design of the application itself. They were different designs, to be sure, but they each had technical merits that could very easily be seen.

At the core, this one came down to coaching back to the engineering. The crux of the problem was that there was no data proving one solution better than another. The quantitative features of the respected solutions had not yet been tested, and that was the end state I coached towards here.

If your design is better, prove it with data. Otherwise, GTFO of the way.

Session 3 : SDETs in the Credit Union

Initially, this one was setup to be a discussion about how to write code to use a Windows Application automation tool (Selenium with WinAppDriver), but after the first session, it was apparent many of the SDETs present already had a lot of experience with those libraries. There were four SDETs and one analyst in this session, so it became a larger discussion about the nature of testing. We started collaborating on ideas about the about the best ways we could automate some of the harder tests to deal with.

Finally, it came down to discussions about the AAA pattern of testing, the kind of test code we wanted across the org, and even some of the difficulties in teams where SDEs and SDETs have a combative relationship.


Coaching engineers is exhausting and inspiring all at once. It was a great day, and I feel blessed to be able to do it!

Protests

This morning, like most of the rest of the US population, I saw protests against police brutality in our cities. Most protests have been nothing but peaceful displays of solidarity. Some, less so, with police responding to property destruction and graffiti with violence, including pepper spraying an eight year old.

I am a pacifist; I do not believe in the use of force, in any case.

Recent news has shown, in clear and not uncertain terms, that being a white male shows that I am not a target. People of color do not enjoy that privilege. It is easy to be a pacifist when systemic racism does not target me.

Statistics back me up here. I am unlikely to be arrested, injured or killed by a police officer. I don’t need to send messages to my friends and family when I have been pulled over by a police officer, as that police officer is unlikely to believe me to be ‘aggressive.’ **

** See the book So You Want to Talk About Race, by Ijeoma Oluo, for details here. Also, it’s just a fascinating book, you should buy it.


In general, I trust police officers to keep us safe. I fully accept that my privileged position supports that trust. That said, I want all people; people of color, LGBTQ people, differently-abled, and any marginalized group I’m (as of yet) unaware of to feel the same.

The police should make people feel safe.

Everyone’s life should matter. However, saying #alllivesmatter’ is fundamentally ignoring systemic racism. Use of force by police statistically impacts people-of-color drastically differently than it impacts white people.


So, being unable to protest myself in the time of COVID19 (I am high risk, heart condition), I will say emphatically here: Black Lives Matter.

Two Rules to Estimating Software Features

OK, you’re a software developer, and someone’s asked you to quick look at a feature, and give them an estimate on how long it’s going to take to develop. For this example, I will refer to that feature as ‘SuperFeature’, and we will have two estimating developer examples; Gina, who examples good estimates, and Lisa, who examples less good estimates. Priya is our product owner, and since Priya owns the product, good estimates give her the information required to make a intelligent decisions. Well-formed estimates enable her to prioritize work well, and manage expectations of customers and stakeholders.

Here’s two rules to estimating features successfully.

Rule 1: Your estimate should represent doing the ‘work done in a vacuum.’

It is counter-intuitive to estimate work in this way, but creating an estimate, as if that work is being done in a vacuum is the best way for your product owner to assign the feature a priority.

Example:

Gina, who estimates in a vacuum – “SuperFeature will take about 2 weeks to do.”

Lisa, who estimates based off of her current workload – “I will need 6-8 weeks to do this.”

In the first case, Priya knows how long the feature will take to develop. She knows that Gina and Lisa are both working on very high priority items, so she gets Liam to work on that feature.

In the second case, the Priya knows how long Lisa will take to get it done, but has very little awareness of the what priority Lisa is putting on the work. At next week’s stand-up, imagine Priya’s surprise to know that Lisa hasn’t even started work on it yet!

The lesson: Your product owner owns priority of the features. An estimate should give your product owner the information required to set that priority.

Rule 2: The larger an estimate, the more detail it needs.

If your feature is large, your product owner needs to know and understand why. In order to understand the work needing to be done, it should be broken down into tasks.

Example:

Lisa, who doesn’t break the work down – “SuperFeature will take about 4 months to work on.”

Gina, who realizes the work is complicated, and Priya needs to understand the details. – “In all, SuperFeature will take about 4 months. We’ll need 3 days to start building the catalytic converter, and then a week to refit and install the Whizzbang…” etc, etc.

In the first case, it’s hard to really even start the work, or even know how to break it up. Is it 4 months altogether? Can you break the work apart? Can you create multiple work streams?

In the second, Gina gives Priya all the details she needs to break up the work accordingly. She also does so succinctly, so that ordering tasks and dependencies are clear.


Following the two rules above will help make your estimates more valuable and your relationship with your product owner more beneficial.

5 months, abridged

Quite a bit has changed since my end of December post. The first two months of 2020 were a blur. {Redacted} went into a full change-over, a new CTO, and a new org structure early in the year, just as I had received word from a regional credit union that they wanted me to be their new, and only, Principal Software Engineer. I had to make a excruciating decision; to leave a company and people I loved, to move over to a new-idea-to-the-org position. Instead of building things and fixing things, I would be responsible for fixing the org’s developers and development processes.

One of the things I will take away is {Redacted}’s dedication to making the customer right, if there was a mistake or error.

Nothing is more freeing than the knowledge that, no matter the mistake, we will make the client right.

Knowing that we would always make the client whole allowed for experimentation and mistakes. Engineers are free there to do the best they can. There are many orgs who strive to be the very thing that {Redacted} has been.

Still, careers should not stagnate, and there was no place for me to go at {Redacted}. I had reached a space that the only place to go was if my superior retired or quit.

It has been three and a half months since I left, and I still miss everyone terribly.


Well, with the whirlwind of changes that occurred in February, naturally, a pandemic ensued which caused me to get a whopping three weeks of face-time with my new team. There are some outstanding people at my credit union, and with this past three months, I have been working to carve out my new-to-the-org role.

Some notes from the first

  1. Larger organizations have larger org problems. Each subteam has its’ own micro-climate.
  2. Empathy, empathy, empathy. You simply have to assume everyone is trying their best.
  3. TDD is nowhere near as ubiquitous as I assumed that it was.

On item three, I have been a TDD practitioner in spurts and starts since 2005, and I was later than I should have been to the game. It has simply been a part of a majority of the code I have dealt with. When folks describe it as new, it is a bit of a shock.


The elephant in the room clearly has to be the same thing everyone is dealing with. The big CV19. Here’s the high points.

  • My kids have been homeschooling since March.
  • My wife was furloughed from the Y, but still teaches the occasional ZOOM yoga course. We are blessed, in that the income from my new job covers what she was bringing in.
  • I do Crossfit at home, either on ZOOM, or simply by myself, and work from home completely. My outings are walking the dog, or going to the grocery store. I have visited a few wineries for club pickups, and recommend everyone go visit a local winery and support small business!

Stay safe!

Resolution

Clearly, this hasn’t been the most updated of blogs, and being that it is the 31st of December, I think we need to resolve to change that, among other elements of my life.

So, here goes: My resolution this year is to be a little more rigorous and diligent in my life and goals. Simply put, I resolve to decide what kind of person that I want to be, and then achieve it.

I resolve to publish more, blocking off at least an hour a week to write. To help with that, this blog will change a bit. I’ll include a bit more about my life / family / coaching / CrossFit and all those other things that will help me be a whole person. F# is still a passion language for me, so most posts will have SOMETHING to do about my favorite language. But I might need help making that hour-a-week goal, so I might include stuff like “My son really wanted a Switch this Christmas…” If that ain’t for you, well fine. There should be enough content generated to let you skip a post.

I am resolving to to get down to 205lbs by December 2020, at 17% body fat. That means sticking with my 5x a week CrossFit schedule, and adding some regular distance running to the mix so I can hang out once a week with an old buddy of mine. I want to be in the kind of shape that I could hear about a 5K run going on somewhere, enter that week, and be in good enough shape to run well. I have an Excel sheet for tracking against my target, and I will post details, if only to ensure myself a little accountability.

I resolve to be more diligent about my health. Specifically, my teeth. No more of this poor-kid thing of ‘not bothering’ to go to a dentist until something hurts. Just simple checkups. 2 of ’em a year. I pay for great dental insurance. Better use it.

I’ll talk about the other resolutions I have in the next post.

Solving Sudoku with F#

An unfinished sudoku puzzle
Can you solve it?

One of my more recent additions to the life of {Redacted} is a thing we call the Thursday Kata. Every Thursday, I pop a message into our slack channel, with a challenge for the week.  Dev team members noodle over the problem, slack a gist with their solution, and then on Wednesday, during our weekly team meeting, we vote on a submitted solution and have the author present what they did, how they did it, and what they learned.

It’s a lot of fun, and is gaining quite a bit of traction within the development org. Developers are walking around discussing the kata, and strategies for solving it, and it interrupts what can be some of the more mundane day-to-day tasks.

The tasks aren’t meant to take more than an hour or two. A more recent example was a Sudoku solver. Devs were challenged to come up with a solver, in whatever language they wanted to look at.

Unfortunately, as the emcee of this event, my own solutions are off limits for voting… but that’s when I remembered I have readers of this blog (even if it is only myself and my mom. Hi Mom!)  Here’s the gist.

Basically, I had two ideas. First, be able to parse and render a puzzle grid, and second, solve the grid using a simple recursive algorithm.

I started with a few types that would help me with the domain.

  1. Sector. A type that represented the 9 blocks (tic-tac-toe blocks) of a Sudoku grid.
  2. Position. A type that represented an individual location of a Sudoku grid. The position was made of a Rank and File, which indicated the X and Y coordinate (in similar terms to chess, a game I’m more familiar with.)
  3. Grid. A type simply mapping a Position (Rank and File) with integers.

The first thing I did was create a simple set of all available positions, and second I created a Map of positions with a unique set of positions to “check”. For example: the position at Rank.One and File.A (the upper left square), would be mapped to a distinct set of positions that included:

  • All positions in Rank.One (A1, B1, C1… I1)
  • All positions in File.A (A1, A2, A3… A9)
  • All positions in the same TopLeft sector (A1, B1, C1, A2, B2, C2, A3, B2, C3)

The next three functions were some simple rendering / parsing functions, enabling me to quickly test my solver, by entering grids as simple strings.

let easy = parseGrid "5,-,-,-,1,-,-,-,4,2,7,4,-,-,-,6,-,-,-,8,-,9,-,4,-,-,-,8,1,-,4,6,-,3,-,2,-,-,2,-,3,-,1,-,-,7,-,6,-,9,1,-,5,8,-,-,-,5,-,3,-,1,-,-,-,5,-,-,-,9,2,7,1,-,-,-,2,-,-,-,3";;

val easy : Grid option = Some (map [({Rank = One; File = A;}, 5; .... 

let rendered = renderGrid e;;
val rendered : string =
"
 5 - - | - 1 - | - - 4
 2 7 4 | - - - | 6 - -
 - 8 - | 9 - 4 | - - -
 *********************
 8 1 - | 4 6 - | 3 - 2
 - - 2 | - 3 - | 1 - -
 7 - 6 | - 9 1 | - 5 8
 *********************
 - - - | 5 - 3 | - 1 -
 - - 5 | - - - | 9 2 7
 1 - - | - 2 - | - - 3
"

Ah UI code… my favorite 🙂

I created a few helper functions to make some logical tests work as well.

  • getAvailableValues : Grid -> position -> (Position * int[])
    This function took a grid and a position, and returned the array of possible number values that were available for the position in question.
  • isNotSolvable : (‘a * ‘b []) list -> bool
    This was a simple test to look at a list of things, and if there were any items that came in with an empty array (as the b value).
  • isSolved : (‘a * ‘b []) list -> bool
    This simple test ensures that the incoming list, the max length of the b array is 1 value.

All together, this made the solve method easy to write.

Solve : Grid -> Map<Position, int> option.

If solve returned a None value, no solution was found. Otherwise, it would return Some solution (see what I did there.)

In that function, it defined a recursive function called ‘createSolution’, taking a grid g.

That function would first iterate through allGrid positions, and then create a list of Position * int [], mapping over the getAvailableValues function and the grid passed in.  That gave us a list of positions, and all positions potential answers. It caled the isNotSolvable method, to test if ANY of the positions couldn’t be solved (because they had no avialable options in the array.)  If so, the function returned None. Otherwise, it checked if the puzzle was solved, using the isSolved function above, and if so, it created a Grid map, and returned Some with that grid.

Finally, the meat of the function assumed that at least one of the Grid squares had more than one possible answer. It then did a simple query for the square with the smallest number of possible answers, and then simply called ‘createSolution’ recursively with one of those possible answers set for the square in question.

What ends up here is a depth first solution, but one that always returns (eventually.)

Here are some simple results I got. Fire up FSI and see what you get! Have a good puzzle everyone!

// easy (solve time 0.167 seconds)
let easy = Option.get (parseGrid "5,-,-,-,1,-,-,-,4,2,7,4,-,-,-,6,-,-,-,8,-,9,-,4,-,-,-,8,1,-,4,6,-,3,-,2,-,-,2,-,3,-,1,-,-,7,-,6,-,9,1,-,5,8,-,-,-,5,-,3,-,1,-,-,-,5,-,-,-,9,2,7,1,-,-,-,2,-,-,-,3");;
 
// hard (6.401 seconds) (38x easy)
let hard = Option.get (parseGrid "-,-,5,-,-,-,9,-,-,-,-,4,6,9,-,1,-,-,7,9,-,-,-,-,-,-,-,-,1,-,2,-,-,-,-,3,-,7,-,-,-,6,-,8,-,-,-,-,-,1,4,6,-,2,2,3,-,-,-,8,-,-,-,-,-,-,-,5,-,-,-,7,-,-,-,4,-,3,-,1,-");;
 
// extreme (20 minutes, 50.464 seconds) (7,487x easy)
let extreme = Option.get (parseGrid "8,-,-,-,-,-,-,-,-,-,-,3,6,-,-,-,-,-,-,7,-,-,9,-,2,-,-,-,5,-,-,-,7,-,-,-,-,-,-,-,4,5,7,-,-,-,-,-,1,-,-,-,3,-,-,-,1,-,-,-,-,6,8,-,-,8,5,-,-,-,1,-,-,9,-,-,-,-,4,-,-");;