Skip to content

Instantly share code, notes, and snippets.

@enrique-ramirez
Last active November 30, 2018 15:44
Show Gist options
  • Save enrique-ramirez/2b4d825bb6ce3809b46edd3ad2691f82 to your computer and use it in GitHub Desktop.
Save enrique-ramirez/2b4d825bb6ce3809b46edd3ad2691f82 to your computer and use it in GitHub Desktop.
Advent of Code y(2017) solutions in JavaScript
/**
* --- Day 1: Inverse Captcha ---
* The night before Christmas, one of Santa's Elves calls you in a panic. "The printer's broken! We can't print the Naughty or Nice List!" By the time you make it to sub-basement 17, there are only a few minutes until midnight. "We have a big problem," she says; "there must be almost fifty bugs in this system, but nothing else can print The List. Stand in this square, quick! There's no time to explain; if you can convince them to pay you in stars, you'll be able to--" She pulls a lever and the world goes blurry.
*
* When your eyes can focus again, everything seems a lot more pixelated than before. She must have sent you inside the computer! You check the system clock: 25 milliseconds until midnight. With that much time, you should be able to collect all fifty stars by December 25th.
*
* Collect stars by solving puzzles. Two puzzles will be made available on each day millisecond in the advent calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants one star. Good luck!
*
* You're standing in a room with "digitization quarantine" written in LEDs along one wall. The only door is locked, but it includes a small interface. "Restricted Area - Strictly No Digitized Users Allowed."
*
* It goes on to explain that you may only leave by solving a captcha to prove you're not a human. Apparently, you only get one millisecond to solve the captcha: too fast for a normal human, but it feels like hours to you.
*
* The captcha requires you to review a sequence of digits (your puzzle input) and find the sum of all digits that match the next digit in the list. The list is circular, so the digit after the last digit is the first digit in the list.
*
* For example:
*
* 1122 produces a sum of 3 (1 + 2) because the first digit (1) matches the second digit and the third digit (2) matches the fourth digit.
* 1111 produces 4 because each digit (all 1) matches the next.
* 1234 produces 0 because no digit matches the next.
* 91212129 produces 9 because the only digit that matches the next one is the last digit, 9.
*
* What is the solution to your captcha?
*/
// Input as given by the challenge
const input = '237369991482346124663395286354672985457326865748533412179778188397835279584149971999798512279429268727171755461418974558538246429986747532417846157526523238931351898548279549456694488433438982744782258279173323381571985454236569393975735715331438256795579514159946537868358735936832487422938678194757687698143224139243151222475131337135843793611742383267186158665726927967655583875485515512626142935357421852953775733748941926983377725386196187486131337458574829848723711355929684625223564489485597564768317432893836629255273452776232319265422533449549956244791565573727762687439221862632722277129613329167189874939414298584616496839223239197277563641853746193232543222813298195169345186499866147586559781523834595683496151581546829112745533347796213673814995849156321674379644323159259131925444961296821167483628812395391533572555624159939279125341335147234653572977345582135728994395631685618135563662689854691976843435785879952751266627645653981281891643823717528757341136747881518611439246877373935758151119185587921332175189332436522732144278613486716525897262879287772969529445511736924962777262394961547579248731343245241963914775991292177151554446695134653596633433171866618541957233463548142173235821168156636824233487983766612338498874251672993917446366865832618475491341253973267556113323245113845148121546526396995991171739837147479978645166417988918289287844384513974369397974378819848552153961651881528134624869454563488858625261356763562723261767873542683796675797124322382732437235544965647934514871672522777378931524994784845817584793564974285139867972185887185987353468488155283698464226415951583138352839943621294117262483559867661596299753986347244786339543174594266422815794658477629829383461829261994591318851587963554829459353892825847978971823347219468516784857348649693185172199398234123745415271222891161175788713733444497592853221743138324235934216658323717267715318744537689459113188549896737581637879552568829548365738314593851221113932919767844137362623398623853789938824592'
// Input converted to an array of Numbers.
const inputArray = input.split('').map((e) => parseInt(e, 10))
// Small function to sum 2 numbers
const sum = (a, b) => a + b
// Check
const isEqualToNextIndexInArray = (index, array) => {
const current = array[index]
const next = index === (array.length - 1) ? array[0] : array[index + 1]
return current === next
}
// Answer 1
const answer1 = inputArray.reduce((acc, currentValue, currentIndex, array) => {
return isEqualToNextIndexInArray(currentIndex, array)
? sum(acc, currentValue)
: acc
}, 0)
/**
* --- Part Two ---
* You notice a progress bar that jumps to 50% completion. Apparently, the door isn't yet satisfied, but it did emit a star as encouragement. The instructions change:
*
* Now, instead of considering the next digit, it wants you to consider the digit halfway around the circular list. That is, if your list contains 10 items, only include a digit in your sum if the digit 10/2 = 5 steps forward matches it. Fortunately, your list has an even number of elements.
*
* For example:
*
* 1212 produces 6: the list contains 4 items, and all four digits match the digit 2 items ahead.
* 1221 produces 0, because every comparison is between a 1 and a 2.
* 123425 produces 4, because both 2s match each other, but no other digit has a match.
* 123123 produces 12.
* 12131415 produces 4.
*
* What is the solution to your new captcha?
*/
// Second Check
const isEqualToItemHalfwayOfArrayLengthIndexesAhead = (index, array) => {
const current = array[index]
const halfway = array.length / 2
const next = array.length <= index + halfway
? array[index + halfway - array.length]
: array[index + halfway]
return current === next
}
// Answer 2
const answer2 = inputArray.reduce((acc, currentValue, currentIndex, array) => {
return isEqualToItemHalfwayOfArrayLengthIndexesAhead(currentIndex, array)
? sum(acc, currentValue)
: acc
}, 0)
// console.log our answers
console.log({
answer1, // 995
answer2, // 1130
})
/**
* --- Day 2: Corruption Checksum ---
* As you walk through the door, a glowing humanoid shape yells in your direction. "You there! Your state appears to be idle. Come help us repair the corruption in this spreadsheet - if we take another millisecond, we'll have to display an hourglass cursor!"
*
* The spreadsheet consists of rows of apparently-random numbers. To make sure the recovery process is on the right track, they need you to calculate the spreadsheet's checksum. For each row, determine the difference between the largest value and the smallest value; the checksum is the sum of all of these differences.
*
* For example, given the following spreadsheet:
*
* 5 1 9 5
* 7 5 3
* 2 4 6 8
*
* The first row's largest and smallest values are 9 and 1, and their difference is 8.
* The second row's largest and smallest values are 7 and 3, and their difference is 4.
* The third row's difference is 6.
* In this example, the spreadsheet's checksum would be 8 + 4 + 6 = 18.
*
* What is the checksum for the spreadsheet in your puzzle input?
*/
// Input as given by the challenge
const input = `1224 926 1380 688 845 109 118 88 1275 1306 91 796 102 1361 27 995
1928 2097 138 1824 198 117 1532 2000 1478 539 1982 125 1856 139 475 1338
848 202 1116 791 1114 236 183 186 150 1016 1258 84 952 1202 988 866
946 155 210 980 896 875 925 613 209 746 147 170 577 942 475 850
1500 322 43 95 74 210 1817 1631 1762 128 181 716 171 1740 145 1123
3074 827 117 2509 161 206 2739 253 2884 248 3307 2760 2239 1676 1137 3055
183 85 143 197 243 72 291 279 99 189 30 101 211 209 77 198
175 149 259 372 140 250 168 142 146 284 273 74 162 112 78 29
169 578 97 589 473 317 123 102 445 217 144 398 510 464 247 109
3291 216 185 1214 167 495 1859 194 1030 3456 2021 1622 3511 222 3534 1580
2066 2418 2324 93 1073 82 102 538 1552 962 91 836 1628 2154 2144 1378
149 963 1242 849 726 1158 164 1134 658 161 1148 336 826 1303 811 178
3421 1404 2360 2643 3186 3352 1112 171 168 177 146 1945 319 185 2927 2289
543 462 111 459 107 353 2006 116 2528 56 2436 1539 1770 125 2697 2432
1356 208 5013 4231 193 169 3152 2543 4430 4070 4031 145 4433 4187 4394 1754
5278 113 4427 569 5167 175 192 3903 155 1051 4121 5140 2328 203 5653 3233`
// Input converted to rows of strings
const rows = input.split('\n')
// Small function to sum 2 numbers
const sum = (a, b) => a + b
// Converts a string to an array of Numbers
const toArrayOfNumbers = string => string.split(/\s+/).map(element => parseInt(element, 10))
// Function to parse each row (long string).
// It returns the diff of each row.
const parseRow = string => {
const row = toArrayOfNumbers(string)
// Get max of the row
const max = Math.max.apply(null, row)
// Get min of the row
const min = Math.min.apply(null, row)
// finally, return the diff
return max - min
}
// Answer 1
const answer1 = rows.reduce((acc, row) => {
return acc + parseRow(row)
}, 0)
/**
* --- Part Two ---
* "Great work; looks like we're on the right track after all. Here's a star for your effort." However, the program seems a little worried. Can programs be worried?
*
* "Based on what we're seeing, it looks like all the User wanted is some information about the evenly divisible values in the spreadsheet. Unfortunately, none of us are equipped for that kind of calculation - most of us specialize in bitwise operations."
*
* It sounds like the goal is to find the only two numbers in each row where one evenly divides the other - that is, where the result of the division operation is a whole number. They would like you to find those numbers on each line, divide them, and add up each line's result.
*
* For example, given the following spreadsheet:
*
* 5 9 2 8
* 9 4 7 3
* 3 8 6 5
*
* In the first row, the only two numbers that evenly divide are 8 and 2; the result of this division is 4.
* In the second row, the two numbers are 9 and 3; the result is 3.
* In the third row, the result is 2.
* In this example, the sum of the results would be 4 + 3 + 2 = 9.
*
* What is the sum of each row's result in your puzzle input?
*/
// Small function to divide
const divide = (a, b) => a / b
// Function to test if two numbers are evenly divisible
const divisible = (a, b) => {
return a % b === 0 || b % a === 0
}
// Function to parse each ron (long string).
// It returns the result of dividing the two numbers that evenly divide on the row.
// This function assumes there's only 2 evenly divisible numbers per row.
const parseRow2 = string => {
const row = toArrayOfNumbers(string)
// So we want to filter numbers finding if they are divisible...
const result = row.filter((number, index) => {
// ... based on if they are evenly divisible by any other number in the array...
return row.some((number2, index2) => {
// ... we exclude the current number...
return index === index2 ? false : divisible(number, number2)
})
// ... then we sort in descending order...
}).sort((a, b) => b - a);
// ... and finally, return the result of dividing these two numbers.
return divide.apply(null, result)
}
// Answer 2
const answer2 = rows.reduce((acc, row) => {
return acc + parseRow2(row)
}, 0)
// console.log our answers
console.log({
answer1, // 34581
answer2, // 214
})
/**
* --- Day 3: Spiral Memory ---
* You come across an experimental new kind of memory stored on an infinite two-dimensional grid.
*
* Each square on the grid is allocated in a spiral pattern starting at a location marked 1 and then counting up while spiraling outward. For example, the first few squares are allocated like this:
*
* 17 16 15 14 13
* 18 5 4 3 12
* 19 6 1 2 11
* 20 7 8 9 10
* 21 22 23---> ...
*
* While this is very space-efficient (no squares are skipped), requested data must be carried back to square 1 (the location of the only access port for this memory system) by programs that can only move up, down, left, or right. They always take the shortest path: the Manhattan Distance between the location of the data and square 1.
*
* For example:
*
* Data from square 1 is carried 0 steps, since it's at the access port.
* Data from square 12 is carried 3 steps, such as: down, left, left.
* Data from square 23 is carried only 2 steps: up twice.
* Data from square 1024 must be carried 31 steps.
*
* How many steps are required to carry the data from the square identified in your puzzle input all the way to the access port?
*/
const input = 368078
// Check if number is odd
const isOdd = (n) => n % 2 !== 0
// Find nearest odd
const getNearestOdd = (number, previous) => {
const nearest = previous ? number - 1 : number + 1
return isOdd(nearest) ? nearest : getNearestOdd(nearest, previous)
}
// Find nearest perfect square
const getNearestPerfectSquare = (number, previous) => {
const operation = previous ? 'floor' : 'ceil'
return Math[operation](Math.sqrt(number)) ** 2
}
// Find nearest odd perfect square
const getNearestOddPerfectSquare = (number, previous) => {
const nearestPerfectSquare = getNearestPerfectSquare(number, previous)
return isOdd(nearestPerfectSquare)
? nearestPerfectSquare
: getNearestOddPerfectSquare(getNearestOdd(number, previous), previous)
}
// parseNumber
const parseNumber = (number) => {
// Find nextOddPerfectSquare to figure out our matrix size
const nextOddPerfectSquare = getNearestOddPerfectSquare(number)
// Find the previousOddPerfectSquare to figure out the distance
const previousOddPerfectSquare = getNearestOddPerfectSquare(number, true)
// Matrix side size
const sideSize = Math.sqrt(nextOddPerfectSquare)
// How many steps we need to take from the edge to the center?
const stepsToCenterFromEdge = (sideSize - 1) / 2
// How many elements are on the edge layer of our matrix?
const elementsOnEdge = (nextOddPerfectSquare - previousOddPerfectSquare)
// How far away is our input element on the surrounding layer?
const distance = input - (nextOddPerfectSquare - elementsOnEdge)
// Position on side
const positionOnSide = (distance) % (sideSize - 1)
// Finally we calculate the steps.
// This is done in 2 parts:
// 1. First, we check how many moves we need to make from the
// current axis to get to the center.
// 2. Second, we know that for the second axis it'll always be
// stepsToCenterFromEdge, so sum that.
const steps = Math.abs(positionOnSide - stepsToCenterFromEdge) + stepsToCenterFromEdge
return steps
}
// Answer 1
const answer1 = parseNumber(input)
/**
* --- Part Two ---
* As a stress test on the system, the programs here clear the grid and then store the value 1 in square 1. Then, in the same allocation order as shown above, they store the sum of the values in all adjacent squares, including diagonals.
*
* So, the first few squares' values are chosen as follows:
*
* Square 1 starts with the value 1.
* Square 2 has only one adjacent filled square (with value 1), so it also stores 1.
* Square 3 has both of the above squares as neighbors and stores the sum of their values, 2.
* Square 4 has all three of the aforementioned squares as neighbors and stores the sum of their values, 4.
* Square 5 only has the first and fourth squares as neighbors, so it gets the value 5.
* Once a square is written, its value does not change. Therefore, the first few squares would receive the following values:
*
* 147 142 133 122 59
* 304 5 4 2 57
* 330 10 1 1 54
* 351 11 23 25 26
* 362 747 806---> ...
*
* What is the first value written that is larger than your puzzle input?
*/
const parseNumber2 = (number) => {
// Coming soon...
return number
}
const answer2 = parseNumber2(input)
// console.log our answers
console.log({
answer1,
answer2,
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment