Forgive the somewhat dramatic title, but today begins one of the coolest coding challenges I've ever done, the Advent of Code. The Advent of Code presents you with two coding challenges a day. (The second is typically a minor modification of the first one.) You can solve the challenge anyway you want. They start off - mostly - kind of simple and then kind of go off the deep end towards the end.
Last year I was able to do all but, maybe, 4 of them, without "cheating", and by "cheating" I just looked at solutions in other languages and rebuilt them in JavaScript. I was still practicing my JavaScript so I still had fun even when I couldn't figure out the solution myself.
In order to encourage my readers to participate, I've decided to share, and blog, my solutions, for every day of contest. I'll probably fall a bit behind on the weekends, and I probably won't code on the 25th (last year I just did it the day after), but if folks want to see how I'm solving these challenges, I'm more then willing to share.
I've set up a repo for my solutions here: https://github.com/cfjedimaster/adventofcode
To be clear, my solutions are going to be pure crap. Do not consider these solutions as examples of best practices or anything even close to intelligent code. But they will work, and I'll have fun writing them, so that's good enough for me. ;)
One piece of advice I'd like to share, and the site makes this suggestion as well. If your solution isn't working, take the sample, smaller input they provide with answers and check your code again. I had to do that today.
Day One
The first challenge wasn't too difficult, but it took me a few reads to figure out exactly what they were asking. Essentially the challenge is this. You're given input that includes a direction and distance traveled across city blocks. Figure out where you end up, then figure out a minimum distance to that point. This is called Taxiway geometry and it turns out there is a super simple solution once you know points 1 and 2:
Math.abs(x1-x2) + Math.abs(y1-y2)
Here is my solution. Again, be gentle.
var fs = require('fs');
var input = fs.readFileSync('./input.txt','utf8');
//var input = 'R2, R2, R2';
var steps = input.split(', ');
/*
we start at 1,1
loop over each step and figure out the new pos
*/
var currentPos = [1,1];
var currentDir = "N"; // North
steps.forEach((step) => {
var dir = step.substring(0,1);
var dis = Number(step.substring(1));
console.log(dir+','+dis);
console.log('current direction '+currentDir);
if(dir === "R") turnRight();
if(dir === "L") turnLeft();
console.log('new direction '+currentDir);
console.log('current pos '+currentPos);
move(dis);
console.log('new pos '+currentPos);
});
var distance = Math.abs(1-currentPos[0]) + Math.abs(1-currentPos[1]);
console.log('Distance is '+distance);
function move(x) {
/*
given a direction and current pos, um, move
*/
if(currentDir === "N") currentPos = [currentPos[0] + x, currentPos[1]];
if(currentDir === "E") currentPos = [currentPos[0], currentPos[1] + x];
if(currentDir === "S") currentPos = [currentPos[0] - x, currentPos[1]];
if(currentDir === "W") currentPos = [currentPos[0], currentPos[1] - x];
}
function turnRight() {
switch(currentDir) {
case "N": {
currentDir = "E";
break;
}
case "E": {
currentDir = "S";
break;
}
case "S": {
currentDir = "W";
break;
}
case "W": {
currentDir = "N";
break;
}
}
}
function turnLeft() {
switch(currentDir) {
case "N": {
currentDir = "W";
break;
}
case "E": {
currentDir = "N";
break;
}
case "S": {
currentDir = "E";
break;
}
case "W": {
currentDir = "S";
break;
}
}
}
For the most part, it should make sense, but the general idea is to read in the input, parse it, and then iterate over every step of the directions. Once I'm done, I simply use the math I described above to get the result.
The second part to the challenge was interesting. Instead of getting the distance to the last point of the path, you now need to get the distance to the first point you traveled to twice. My code above made this difficult as I make my moves in whole blocks, ie if I'm going 5 blocks east, I move 5 at once. I needed to change my code to "step" through each part of the movement and keep track of how many times I visited it. Here's that version:
var fs = require('fs');
var input = fs.readFileSync('./input.txt','utf8');
//var input = 'R8, R4, R4, R8';
var steps = input.split(', ');
/*
we start at 1,1
loop over each step and figure out the new pos
*/
var currentPos = [1,1];
var currentDir = "N"; // North
var visited = {};
var target = "";
steps.forEach((step) => {
var dir = step.substring(0,1);
var dis = Number(step.substring(1));
// console.log(dir+','+dis);
// console.log('current direction '+currentDir);
if(dir === "R") turnRight();
if(dir === "L") turnLeft();
// console.log('new direction '+currentDir);
// console.log('current pos '+currentPos);
move(dis);
// console.log('new pos '+currentPos);
});
console.log('my target is '+target);
var distance = Math.abs(1-target[0]) + Math.abs(1-target[1]);
console.log('Distance is '+distance);
function move(x) {
/*
given a direction and current pos, um, move
*/
for(var i=0;i<x;i++) {
if(currentDir === "N") currentPos = [currentPos[0] + 1, currentPos[1]];
if(currentDir === "E") currentPos = [currentPos[0], currentPos[1] + 1];
if(currentDir === "S") currentPos = [currentPos[0] - 1, currentPos[1]];
if(currentDir === "W") currentPos = [currentPos[0], currentPos[1] - 1];
// console.log('MOVE :'+currentPos);
var newPosLabel = currentPos.toString(); // 1,2
if(!visited[newPosLabel]) visited[newPosLabel] = 0;
visited[newPosLabel]++;
if(visited[newPosLabel] === 2) {
// console.log('been to '+newPosLabel+ ' twice!');
//only record once. there is no break in a foreach
if(target === "") target = newPosLabel.split(',');
}
}
}
function turnRight() {
switch(currentDir) {
case "N": {
currentDir = "E";
break;
}
case "E": {
currentDir = "S";
break;
}
case "S": {
currentDir = "W";
break;
}
case "W": {
currentDir = "N";
break;
}
}
}
function turnLeft() {
switch(currentDir) {
case "N": {
currentDir = "W";
break;
}
case "E": {
currentDir = "N";
break;
}
case "S": {
currentDir = "E";
break;
}
case "W": {
currentDir = "S";
break;
}
}
}
And there ya go. Ugly, but workable.