Advent of Code – Node, TypeScript & CodeMix

The Advent of Code (AoC) annual 25-day coding challenge kicked off last Sunday (Dec 1). Each year that I’ve participated I use the opportunity to learn something new such as a new coding language or more advanced algorithmic solutions. This year my “new” thing is to improve my Node and TypeScript coding skills while testing the awesome CodeMix plugin for Eclipse. As of today, I’ve completed the first three daily challenges (6 problems in all). Following I briefly highlight a few patterns and lessons I’ve learned and share some of the reusable code I’ve developed. You can find the code here

Abstract Program Structure

Each daily AoC challenge consists of 2 parts. I call them part-A and part-B. You must complete part-A before part-B is revealed to you by the AoC site. Part-B typically reuses some elements of the part-A solution such as common input data, data structures or algorithm. I created an abstract TypeScript class AoCSolution to serve as a common structure for each daily solution. The key responsibilities of AoCSolution are:

  • Provide well defined entry points for implementing part-A and part-B solutions, see abstract methods partA() and partB()
  • Provide commandline flags for specifying to run part-A, part-B; simplifies isolating concerns during coding
  • Provide customizable synchronous data input utility
export abstract class AoCSolution {

    options: {
        a: boolean,
        aFilename?: string,
        b: boolean,
        bFilename?: string
    }

    constructor() {
        this.options = {
            a: false,
            b: false
        };

        let args = process.argv.slice(2);
        if (args.length > 0) {
            args.forEach(arg => {
                switch (arg) {
                    case '-a': this.options.a = true;
                        break;
                    case '-b': this.options.b = true;
                        break;
                }
            });
        } else { //default is to run both partA() and partB()
            this.options.a = true;
            this.options.b = true;
        }
    };

    main() {
        if (this.options.a) this.partA();
        if (this.options.b) this.partB();
    }

    abstract partA(): void;
    abstract partB(): void;
}

Commandline Flags

I develop the code for both parts of a daily solution in a common file, e.g., Day4Solution.ts. In this file, I create a new class by extending AoCSolution. I code and test the part-A solution followed then by repeating the process for part-B. In order to be able to run either part-A or part-B in isolation the AoCSolution class recognizes 2 commandline flags that designate which solution parts, A or B to run. By default if no commandline flags are provide both partA() and partB() methods will be performed in sequential order. To run a single partA or partB solution use the -a and -b respectively

#example to run both partA() and partB()
node dist/Day4Solution -a -b

Simplify Data Input 

Let’s face it, using Node for scripting a procedural solution can be a pain if you don’t account for the complexity of asynchronous activities such as file I/O when reading in the AoC problem dataset. I experienced this with the Day1 solution where I used the Node readline module to asynchronously read the dataset line by line. Way too much unnecessary complexity! 

Version 2 for reading in the problem datasets is to create a simple static utility method loadFile() that synchronously reads the dataset and structures it according to a regex parameter supplied to the function. The resulting data is returned in a string[] for further processing by the respective partX method. Switching to synchronous reads keeps things simple as it enables you to code your solution in a linear flow.

static loadFile(filename: string, separator?: string | RegExp): string[] {
    let content = fs.readFileSync(filename).toString();
    return separator ? content.split(separator) : [content];
}

Developing Quickly

Your AoC ranking is based on how quickly you successfully create daily solutions relative to all other participants. My solution times have ranged from 20 mins to just over an hour – not the fastest but still in the top 70%. I’m spending more time messing with regex’s and mapping matches into data-structures than I’d like. The race begins each day at 12AM EST when a new daily challenge is released. Usually the first 1-2 paragraphs tell a story to setup the challenge. This is followed by an explanation of the problem in detail along with a small test dataset. Lastly a link to the part’s dataset is provided.

Here’s my approach for coding a daily solution as quickly as possible:

  1. Prior to the release of a daily solution I create the DailyXSolution.ts file and populate it with the template code consisting of a custom class that extends AoCSolution.ts. Additionally, I create an empty text file for holding the partA dataset which I copy/paste into during the actual coding session. 
  2. When the challenge is posted skip straight to the problem statement and sample dataset section. Go back and read the intro paragraphs only if more context is needed to understand the problem
  3. Quickly identify the data structures and algorithm to use in crafting a solution
  4. Determine what level of dataset processing is required to map the input into the data structure you’ll process. This frequently requires developing a regex. I use an online regex tester such as regex101.com to interactively craft and test my regex before coding it in TypeScript.
  5. Create a text file if not already available and copy/paste the dataset from the AoC site, e.g., day4-parta.dat
  6. Update the partA and partB options with the path to the respective new dataset file
  7. Implement partA(), iteratively testing file input, parsing and algorithm as I go
  8. Once partA() is producing the correct solution, I enter it into the AoC site and then repeat the process for part-B
  9. If my solution produces the wrong result
    1. slow down and analyze any error messages and my code for silly mistakes
    2. fire up the debugger, set breakpoints and analyze the runtime state of the system
    3. last year, I was not able to properly understand one of the problem statements. I resorted to an AoC Reddit discussion for clarity. In truth when time permits I peruse the Reddit discussions to analyze the solutions submitted by others. I always learn something new when doing this.  

CodeMix Report

I’m using the CodeMix for Eclipse plugin as my main coding tool. My motivations are: 1) I just like using CodeMix for everything except Java coding and 2) I’m part of Genuitec’s ongoing effort to improve the CodeMix UX and identify bugs. I created a single CodeMix project that contains all of my AoC 2018 code. I configured CodeMix to watch the project and auto-compile any file when it’s modified. The CodeMix Terminal+ is handing for running node from the commandline. My debugging tools are console.log() and the CodeMix Node debugger. For simple debugging a quick console.log() usually does the trick. But in some cases – well one at this time – it’s much quicker to fire up the CodeMix debugger to isolate an issue rather than slop up your code with a bunch of log statements that you’ll end up deleting once you’ve corrected the issue.

#HappyCoding

Follow me on twitter, @wayne_parrott