# 150 coding challenges in 25 days

What I learned from attempting Advent of Code 2017, 2016 & 2015 all at one go.

Yong Jun

24 Jan 2018

Advent of Code a series of small programming puzzles for a variety of skill levels.

They are self-contained and are just as appropriate for an expert who wants to stay sharp as they are for a beginner who is just learning to code.

Each puzzle calls upon different skills and has two parts that build on a theme.

# Generators

``````function* generator () {
// do something
yield 'someValue'

// do some more thing
yield 'anotherValue'
}

const iterator = generator()

console.log(iterator.next())
// {value: 'someValue', done: false}

console.log(iterator.next())
// {value: 'anotherValue', done: false}

console.log(iterator.next())
// {value: undefined, done: true}``````

## AoC 2017 Day 3: Spiral Memory

Right x 1

Up x 1

Left x 2

Down x 2

Right x 3

Up x 3

Left x 4

Down x 4

``````function * traverse () {
let step = 1
let x = 0
let y = 0
yield [x, y]
while (true) {
for (let right = step; right > 0; right--) {
yield [++x, y]
}
for (let up = step; up > 0; up--) {
yield [x, ++y]
}
step++
for (let left = step; left > 0; left--) {
yield [--x, y]
}
for (let down = step; down > 0; down--) {
yield [x, --y]
}
step++
}
}

function manhattanDistance (target) {
let coordinates
const location = traverse()

for (let i = 0; i < target; i++) {
coordinates = location.next().value
}
return Math.abs(coordinates) + Math.abs(coordinates)
}``````

# Why use a generator?

• Rather than create an object that maintain internal state by variables assignment (explicit)

• Internal state can be implicit in the position of the yield statement
• Behave sort of like the goto statement from old school imperative programming

# Another example

## Reindeer Olympics

``````function * reindeer (speed, fly, rest) {
let distance = 0
while (true) {
for (let i = 0; i < fly; i++) {
distance += speed
yield distance
}
for (let i = 0; i < rest; i++) {
yield distance
}
}
}``````

# RegEx

## Internet Protocol Version 7

Find ABBA

Solution:

``````function containsAbba (str) {
const match = str.match(/([a-z])([a-z])\2\1/)
if (!match) return false
if (match !== match) return true
return containsAbba(str.slice(match.index + 1))
}``````

## Solution

``````// no proper tail call

function lookNsay (str) {
const match = str.match(/(.)(\1*)/)
if (match.length === str.length) return match.length + match
return match.length + match + lookNsay(str.slice(match.length))
}

// with proper tail call

function lookNsay (str, prefix = '') {
const match = str.match(/(.)(\1*)/)
if (match.length === str.length) return prefix + match.length + match
return lookNsay(str.slice(match.length), prefix + match.length + match)
}``````

## Limitations

• 'use strict'
• --harmony
• Only node 6 & 7

## Workaround

• rewrite recursion into iteration
• use a stack
• eg. Depth-first-search

# Enumeration

## No Such Thing as Too Much

Given containers of various sizes (eg. 20, 15, 10, 5, and 5).

Find how many different combinations total up to a given capacity.

For example if required total is 25, four ways are possible:

• 15 and 10
• 20 and 5 (the first 5)
• 20 and 5 (the second 5)
• 15, 5, and 5

Solution:

Enumerate all possible assignment combination

Eg. 000, 001, 010, 011, 100, 101, 110, 111

## All in a Single Night

Given distances between each location pair, find the

shortest route where every location is visited exactly once.

Solution:

Enumerate all possible permutation

Eg. ABC, ACB, BAC, BCA, CAB, CBA

## Knights of the Dinner Table

Find the ideal seating arrangement around a round

dining table

Solution:

Fix a start point and enumerate all possible

permutation that start and end with that point. Eg.

• A > B > C > D > A
• A > B > D > C > A
• A > C > B > D > A
• A > C > D > B > A

## Science for Hungry People

With exactly 100 teaspoons of ingredients. Find

the ideal recipe

Solution:

Enumeration all possible split combinations. Eg.

 0/0/100 0/1/99 1/0/99 0/2/98 1/2/98 2/1/98 0/3/97 1/2/97 2/1/97 3/0/97

## Enumerate Assignment Combinations

• Count up from 0 to
• Convert number to binary representation using Number.prototype.toString(2)
• Left pad string with zeros
• Convert to array using String.prototype.split('')
2^n - 1
``````function getAssignments (items, groups = 2) {
const combinations = []
const nCombinations = Math.pow(groups, items)
for (let i = 0; i < nCombinations; i++) {
const combiString = (zeroPad + i.toString(groups)).slice(-items)
const combination = combiString.split('')
if (groups === 2) combinations.push(combination.map(v => +v))
else combinations.push(combination)
}
return combinations
}``````

## Enumerate Split Combination

• Instead of enumerating units, enumerate cuts. Eg.

• If you have k different ingredients, the number of cuts to enumerate will be k - 1
• Pick any position that is between last cut's position and N inclusive
• Cuts can overlap
• Subtract adjacent cut index to get units of ingredient
• i.e.
1 1 1 1 1 1 1 1 1 1

First cut is always at 0

Last cut is always at N

Second cut at 2

Third cut also at 2

Fourth cut at 5

unit_i = cut_i - cut_{i-1}

## Implementation

``````function getSplitCombinations (total, items) {
const combinations = []
function recurse (division, i) {
if (division.length < items - 1) {
while (i <= total) {
recurse(division.concat(i), i)
i++
}
} else {
division = [0, ...division, total]
const combination = []
for (let i = 1; i < division.length; i++) {
combination.push(division[i] - division[i - 1])
}
combinations.push(combination)
}
}
recurse([], 0)
return combinations
}``````

## It Hangs in the Balance

Split cargo (28 pieces) into 3 groups of equal weight.

Pick combination with least items in the first group.

Analysis:

Total possible combinations is                                                       !!!

Takes too long to enumerate every possible combination.

Need a way to exit early once a match is found.

3^{28} = 22,876,792,454,961

## Iterables

``````const iterable = {
[Symbol.iterator]: function* () {
let n = 0
while (true) {
yield n++
}
}
}

for (let n of iterable) {
if (n >= 10) break
}``````

## Example

``````// lazy version of assignment enumerator

function getAssignments (items, groups = 2) {
const nCombinations = Math.pow(groups, items)
return {
[Symbol.iterator]: function* () {
for (let i = 0; i < nCombinations; i++) {
const combiString = (zeroPad + i.toString(groups)).slice(-items)
const combination = combiString.split('')
if (groups === 2) yield combination.map(v => +v)
else yield combination
}
}
}
}``````
``````// wrong use

const iterable = getAssignment(n)
Array.from(iterable)
[...iterable]``````
``````// correct use

const iterable = getAssignment(n)
for (let item of iterable) {
if (/* condition met */) return item
}

``````

# Graph search

### Depth-First-Search

``````function bfs (root) {
const visited = {}
const unvisited = []
unvisited.push([root, 0])

while (unvisited.length > 0) {
const [next, steps] = unvisited.shift()

if (next in visited) continue
visited[next] = 1

if (found) return steps

next.children
.forEach(child => {
unvisited.push([child, steps + 1]
})
}
}``````
``````function dfs (root) {
const visited = {}
const unvisited = []
unvisited.push([root, 0])

while (unvisited.length > 0) {
const [next, steps] = unvisited.pop()

if (next in visited) continue
visited[next] = 1

if (found) return steps

next.children
.sort(sortFunc)
.reverse()
.forEach(child => {
unvisited.push([child, steps + 1]
})
}
}``````

## Dealing with coding task of the assembly language kind

• Use Chrome inspector
• node --inspect-brk script.js

## Wrapping up

• Work it out on paper
• Premature optimization
• Readability & Extensibility > Efficiency & Speed
• Some knowledge of computation complexity is helpful

By yongjun21

# 150 coding challenges in 25 days

What I've learned from attempting Advent of Code 2017, 2016 & 2015 all at one go.

• 740