Advent of Code 2016 Day 15 (Typescript)

Advent of Code 2016 Day 14 (Go)
Advent of Code 2016 Day 13 (Lua)
Advent of Code 2016 Day 12 (C#)
Advent of Code 2016 Day 11 (Groovy)

Advent of Code 2016 Day 10 (OcaML)

Advent of Code 2016 Day 9 (Factor)

Advent of Code 2016 Day 8 (c++)
Advent of Code 2016 Day 7 (Javascript)
Advent of Code 2016 – Day 6 (Julia)
Advent of Code 2016 – Day 5( Java 8)

Advent of Code 2016 – Day 4 (Perl 6)

Advent of Code 2016 – Day 3 (Clojure)
Advent of Code – Day 2 (Python 3)
Advent Of Code 2016 – Day 1(Haskell)

Day 15

Start: 12/20/2016

Finish 12/20/2016

Language: Typescript

SPOILER ALERT: If you have any inkling, any whatsoever, to work on the Advent of Code…. DO NOT READ THIS BLOG POST.  DO NOT LOOK AT MY GITHUB PROJECT.  It is no fun if you’re not solving it yourself, and you’ll feel so much better about yourself if you can figure it out without looking up an answer.  This blog post is not to give away the answer, but instead, is there for people to learn from.

As always, the following code is in my GitHub: https://github.com/pviafore/AdventOfCode2016

The Challenge

 

I’m not sure how to describe this challenge, as it was a bunch of math calculations to see when a bunch of interacting circles were in a specific position.   It’s probably best if you go check out the problem yourself, as I really can’t describe it well. 

I decided to try out Typescript on this one.  I prefer static typing, and I want to get better at some languages that compile to Javascript.

Part 1

 

This only took  me 40 lines of TypeScript, so that was real nice.  I think I found a pretty cool solution for taking care of this, and I got to learn about ECMAScript2015 generators along the way.

interface Disc {
   totalPositions: number,
   initialPosition: number,
}

function* infiniteSequence(){
  var index = 0;
  while(true)
    yield index++;
}

function* filter(iterable:IterableIterator, f:(number)=>boolean){
    for(let item of iterable){
        if(f(item)){
            yield item
        }
    }
}

function toDisc(str:string): Disc {
    const regex:RegExp = /Disc #\d has (\d+) positions; at time=0, it is at position (\d+)./
    const matches = str.match(regex);
    return {totalPositions: parseInt(matches[1], 10), initialPosition: parseInt(matches[2], 10)}
}

function isTimeAllowed(num: number, disc:Disc)
{
    return ((num + disc.initialPosition) % disc.totalPositions) == 0;
}

function getAllowedPositions(iterable:IterableIterator, disc:Disc, discNumber:number ){
    return filter(iterable, num => isTimeAllowed(num + discNumber + 1, disc));
}

declare function require(name:string);
const fs = require("fs");
const lines = fs.readFileSync("day15.txt", "utf8").split("\n");
const discs = lines.filter(s => s !== "").map(toDisc);
const allowedNumbers = discs.reduce(getAllowedPositions, infiniteSequence());
console.log(allowedNumbers.next().value);

I’m going to skip over the Disc interface, as it just kept up with the initial position and total number of positions.  Converting from a string to a disc was easy too, as I had the use of a regex to help me out.
So, how I tried to tackle this solution was to start with an infinite sequence of integers to represent my time domain.  I also provided a way to filter out values I didn’t want.

function* infiniteSequence(){
  var index = 0;
  while(true)
    yield index++;
}

function* filter(iterable:IterableIterator, f:(number)=>boolean){
    for(let item of iterable){
        if(f(item)){
            yield item
        }
    }
}
Then, I wanted to reduce each disc against that time series,  continuing to apply a filter function on it to make sure that only that disc’s positions would match up at that time. This meant that I was using the composition of all the discs at once to determine which allowedNumbers I could use.

const allowedNumbers = discs.reduce(getAllowedPositions, infiniteSequence());
console.log(allowedNumbers.next().value);
I calculate if a disk is allowed by taking the discNumber, adding my current time and adding 1.  I add the discNumber since it takes a second to reach each disc, and add 1 since the disc numbers are zero-based and I need them one-based.  Then I add the initial position in and make sure that my modulus of the total positions is zero (meaning I can accept a ball.


function isTimeAllowed(num: number, disc:Disc)
{
    return ((num + disc.initialPosition) % disc.totalPositions) == 0;
}

function getAllowedPositions(iterable:IterableIterator, disc:Disc, discNumber:number ){
    return filter(iterable, num => isTimeAllowed(num + discNumber + 1, disc));
}

Part 2

Another real easy one, as I just had to enter in another line of input to my text file and my code still worked.

 

Wrap-up

I give myself an A- for this one.  Typescript was fun; I prefer static typing and it was still Javascript that I was familiar with.  I didn’t dive deep enough to really understand what else Typescript can buy me, so that’s why its not a straight up A.  I really liked my solution too, as I got to deal with infinite sequences and reducing filter functions over them.

 

Next up is Scala for Day 16.  Back to the JVM for that one.  See you then.