diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..696fc57 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +input-test* +*.txt \ No newline at end of file diff --git a/README.md b/README.md index 90942c3..f745119 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ -# advent-of-code-2022 -Advent of Code 2022 Solutions +# Advent of Code Solutions (2022 Branch) + +Contains my solutions for the [Advent of Code](https://adventofcode.com/2022) challenges. + +They won't be efficient or beautiful but hopefully they will at least work :) + +(2023 update: I think `day13` is broken? Found these uncomitted and unstaged new files so might as well commit+push them) \ No newline at end of file diff --git a/day01/day01.js b/day01/day01.js new file mode 100644 index 0000000..16fd7d9 --- /dev/null +++ b/day01/day01.js @@ -0,0 +1,26 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + + +// Find what each elf is carrying +let elves = []; +let i = 0; + +for (const line of input.split("\n")) { + if (line.length <= 1) i++; // if the line is blank, move onto the next elf + if (!elves[i]) elves[i] = 0; // if the current elf hasnt been seen before, init with 0 + elves[i] += parseInt(line); // add cals to current elf +} + +// Find the biggest +let currentTop = 0; +elves.forEach(e => { if (e > currentTop) currentTop = e }); +console.log(currentTop); // Part One answer + + +// Part Two: Adding top three values +let elvesSorted = elves.sort((a, b) => b - a); // this feels like cheating +let topThree = elvesSorted[0] + elvesSorted[1] + elvesSorted[2]; // this is the best code i have ever written +console.log(topThree); // Part Two answer \ No newline at end of file diff --git a/day02/day02-pt2.js b/day02/day02-pt2.js new file mode 100644 index 0000000..ff3f3f0 --- /dev/null +++ b/day02/day02-pt2.js @@ -0,0 +1,80 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + + +let totalPoints = 0; + +for (const line of input.split("\n")) { + if (!line) continue; // ignore blank lines + + let pointsThisTime = 0; // points from this game + let state = null; // win/loss/draw, for adding right # of points in each game + let needTo = null; // win/loss/draw, only for logging later + + const theirPlay = line.split(" ")[0].toLowerCase(); + let myPlay = line.split(" ")[1].toLowerCase(); + + // this will change our shape depending on the goal of this game + switch (myPlay) { + case "x": // we need to lose + needTo = "lose"; + if (theirPlay == "a") myPlay = "z"; // they chose rock - to lose we need to pick scissors + if (theirPlay == "b") myPlay = "x"; // they chose paper - to lose we need to pick rock + if (theirPlay == "c") myPlay = "y"; // they chose scissors - to lose we need to pick paper + break; + case "y": // we need to draw + needTo = "draw"; + if (theirPlay == "a") myPlay = "x"; // they chose rock - to draw we need to pick rock + if (theirPlay == "b") myPlay = "y"; // they chose paper - to draw we need to pick paper + if (theirPlay == "c") myPlay = "z"; // they chose scissors - to draw we need to pick scissors + break; + case "z": // we need to win + needTo = "win"; + if (theirPlay == "a") myPlay = "y"; // they chose rock - to win we need to pick paper + if (theirPlay == "b") myPlay = "z"; // they chose paper - to win we need to pick scissors + if (theirPlay == "c") myPlay = "x"; // they chose scissors - to win we need to pick rock + break; + } + + // do as part one now we've changed our shape + switch (myPlay) { + case "x": // rock + pointsThisTime += 1; // we get 1 point for choosing rock + if (theirPlay == "a") state = "draw"; // they chose rock + if (theirPlay == "b") state = "loss"; // they chose paper + if (theirPlay == "c") state = "win"; // they chose scissors + break; + case "y": // paper + pointsThisTime += 2; // we get 2 points for choosing paper + if (theirPlay == "a") state = "win"; // they chose rock + if (theirPlay == "b") state = "draw"; // they chose paper + if (theirPlay == "c") state = "loss"; // they chose scissors + break; + case "z": // scissors + pointsThisTime += 3; // we get 3 points for choosing scissors + if (theirPlay == "a") state = "loss"; // they chose rock + if (theirPlay == "b") state = "win"; // they chose paper + if (theirPlay == "c") state = "draw"; // they chose scissors + break; + } + + switch (state) { + case "win": + pointsThisTime += 6; + break; + case "draw": + pointsThisTime += 3; + break; + case "loss": + pointsThisTime += 0; + break; + } + + console.log(`a ${state} for us (we needed to ${needTo})! ${pointsThisTime} from this game (${theirPlay} vs ${myPlay})`); + + totalPoints += pointsThisTime; +} + +console.log(totalPoints); \ No newline at end of file diff --git a/day02/day02.js b/day02/day02.js new file mode 100644 index 0000000..f892008 --- /dev/null +++ b/day02/day02.js @@ -0,0 +1,59 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + + +let totalPoints = 0; + +for (const line of input.split("\n")) { + if (!line) continue; // ignore blank lines + + let pointsThisTime = 0; // points from this game + + const theirPlay = line.split(" ")[0].toLowerCase(); + const myPlay = line.split(" ")[1].toLowerCase(); + let state = null; + + // i'm sure there's a more mathematical way of doing this + // perhaps by converting the rock/paper/scissors into numbers + // but this is less to think about and there's not too much to manually write + switch (myPlay) { + case "x": // rock + pointsThisTime += 1; // we get 1 point for choosing rock + if (theirPlay == "a") state = "draw"; // they chose rock + if (theirPlay == "b") state = "loss"; // they chose paper + if (theirPlay == "c") state = "win"; // they chose scissors + break; + case "y": // paper + pointsThisTime += 2; // we get 2 points for choosing paper + if (theirPlay == "a") state = "win"; // they chose rock + if (theirPlay == "b") state = "draw"; // they chose paper + if (theirPlay == "c") state = "loss"; // they chose scissors + break; + case "z": // scissors + pointsThisTime += 3; // we get 3 points for choosing scissors + if (theirPlay == "a") state = "loss"; // they chose rock + if (theirPlay == "b") state = "win"; // they chose paper + if (theirPlay == "c") state = "draw"; // they chose scissors + break; + } + + switch (state) { + case "win": + pointsThisTime += 6; + break; + case "draw": + pointsThisTime += 3; + break; + case "loss": + pointsThisTime += 0; + break; + } + + console.log(`a ${state} for us! ${pointsThisTime} from this game (${theirPlay} vs ${myPlay})`); + + totalPoints += pointsThisTime; +} + +console.log(totalPoints); \ No newline at end of file diff --git a/day03/day03-pt2.js b/day03/day03-pt2.js new file mode 100644 index 0000000..a13a36c --- /dev/null +++ b/day03/day03-pt2.js @@ -0,0 +1,58 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let sum = 0; // total sum +let i = 1; // loop index, starts at one for % +let thisThree = []; // each group of three bags + +for(const bag of input.split("\n")) { + if(!bag) continue; // blank lines + + thisThree.push(bag); // add this bag to the current group + + // every three we can work on the group + if (i % 3 == 0) { + const commonChar = getCommonCharacter(thisThree[0].split(''), thisThree[1].split(''), thisThree[2].split('')); + + let thisCharValue = commonChar.toLowerCase().charCodeAt() - 96; // use ascii codes to get pos in alphabet (1-26) + if (commonChar.toUpperCase() == commonChar) thisCharValue += 26; // if it's a capital letter, we need to add 26 for 27-52 + + console.log(thisThree, commonChar, thisCharValue); + + sum += thisCharValue; + + // wipe the group now we're done + thisThree = []; + } + + i++; +} + +console.log(sum); + + +// Out of curiosity, I got the new OpenAI chatbot to write this bit +// https://chat.openai.com/chat +// prompt: "in javascript, write a function to get the only common character across three arrays of single character strings" +// took a few attempts to get a working version, it got very confident that there were no problems with some obviously broken implementations + +// == BEGIN AI STUFF == +function getCommonCharacter(arr1, arr2, arr3) { + // Create a Set from each array to remove duplicates + const set1 = new Set(arr1); + const set2 = new Set(arr2); + const set3 = new Set(arr3); + + // Use the spread operator (...) to convert each Set to an array + // and then use the Array.filter() method to keep only the elements + // that are present in all three arrays + const common = [...set1, ...set2, ...set3].filter(c => set1.has(c) && set2.has(c) && set3.has(c)); + + // Return the first element in the common array, or null if the array is empty + return common.length > 0 ? common[0] : null; +} +// == END AI STUFF == + +// the future is terrifying but also this is SO COOL \ No newline at end of file diff --git a/day03/day03.js b/day03/day03.js new file mode 100644 index 0000000..ab2fc2c --- /dev/null +++ b/day03/day03.js @@ -0,0 +1,29 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let sum = 0; + +for(const bag of input.split("\n")) { + if(!bag) continue; // blank lines + + const firstHalf = bag.slice(0, bag.length/2).split(''); // first compartment + const secondHalf = bag.slice(bag.length / 2, bag.length).split(''); // second compartment + + let matchingLetter = null; + firstHalf.forEach(letter => { + if(secondHalf.includes(letter)) { + matchingLetter = letter; + return; + } + }); + + let thisCharValue = matchingLetter.toLowerCase().charCodeAt() - 96; // use ascii codes to get pos in alphabet (1-26) + if (matchingLetter.toUpperCase() == matchingLetter) thisCharValue += 26; // if it's a capital letter, we need to add 26 for 27-52 + + console.log(matchingLetter, thisCharValue); + sum += thisCharValue; +} + +console.log(sum); \ No newline at end of file diff --git a/day04/day04-pt2.js b/day04/day04-pt2.js new file mode 100644 index 0000000..70f8240 --- /dev/null +++ b/day04/day04-pt2.js @@ -0,0 +1,54 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let count = 0; + +for(const group of input.split("\n")) { + if(!group) continue; // empty group + + const firstElfRange = getBetweenRange(group.split(',')[0]); + const secondElfRange = getBetweenRange(group.split(',')[1]); + + let overlap = false; + + // this could be made more efficient using one for loop + // taking into account the min/max of both arrays + // buuuuut that's effort and this works and got the answer + // so i am calling this a win + + // check if any of the first elf's sections are also in the second elf's + for(const section of firstElfRange) { + if(secondElfRange.includes(section)) { + overlap = true; + break; + } + } + + // likewise but for the second elf + for (const section of secondElfRange) { + if (firstElfRange.includes(section)) { + overlap = true; + break; + } + } + + if (overlap) count++; +} + +console.log(count); + +// turns out i did need this after all! +function getBetweenRange(range) { + let nums = []; + + const from = parseInt(range.split('-')[0]); + const to = parseInt(range.split('-')[1]); + + for (let i = from; i <= to; i++) { + nums.push(i); + } + + return nums.sort((a, b) => { return a - b }); // sort from smallest to largest and return +} \ No newline at end of file diff --git a/day04/day04.js b/day04/day04.js new file mode 100644 index 0000000..020358e --- /dev/null +++ b/day04/day04.js @@ -0,0 +1,38 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let count = 0; + +for(const group of input.split("\n")) { + if(!group) continue; // empty group + + const firstElfRange = group.split(',')[0]; + const secondElfRange = group.split(',')[1]; + + const firstElfLowest = parseInt(firstElfRange.split('-')[0]); + const firstElfHighest = parseInt(firstElfRange.split('-')[1]); + + const secondElfLowest = parseInt(secondElfRange.split('-')[0]); + const secondElfHighest = parseInt(secondElfRange.split('-')[1]); + + // if the second elf's sections are within the first elf's + // as in the example from aoc below: + // .2345678. 2 - 8 + // ..34567.. 3 - 7 + // second elf's lowest is greater than or eq to first elf's lowest + // second elf's highest is less than or eq to first elf's highest + if (secondElfLowest >= firstElfLowest && secondElfHighest <= firstElfHighest) { + count++; + continue; + } + + // inverse of above; if first elf is within second elf + if (firstElfLowest >= secondElfLowest && firstElfHighest <= secondElfHighest) { + count++; + continue; + } +} + +console.log(count); \ No newline at end of file diff --git a/day05/day05-pt2.js b/day05/day05-pt2.js new file mode 100644 index 0000000..6d9f1c2 --- /dev/null +++ b/day05/day05-pt2.js @@ -0,0 +1,63 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + + +let lookingAtStart = true; +let stacks = new Array(); + +for(const line of input.split("\n")) { + const blankLine = (line.length <= 1) ? true : false; + + if (blankLine) lookingAtStart = false; // whether we are looking at the input (the stacks from 1-9), we can use the first blank line to determine this + + if (lookingAtStart) { // if we're looking at the start we need to process the input + const matched = line.match(/.{1,4}/g); + if(!matched) continue; + + let i = 0; + for (const crate of matched) { // split each line into groups of 4 chars + if (crate.match(/ [0-9] /g)) continue; // if the content is " X " we're at the bottom row so discard it + + let value = crate.replace(/\[|\]/g, "").replace(/ /g, "") // remove the square brackets and then spaces + + if (!stacks[i]) stacks[i] = new Array(); // init a new array if one doesn't exist for this stack + + if (value !== "") stacks[i].push(value); // add to the array if not a blank + + i++; + } + } else { // we can move on and work on the movements now + if (blankLine) continue; // skip over blank lines + + const commands = line.match(/[0-9]+/g); + const moveWhat = parseInt(commands[0]); // how many crates + const moveFrom = parseInt(commands[1] - 1); // from what stack (minus one since we are zero-indexed) + const moveTo = parseInt(commands[2] - 1); // to which stack (minus one since we are zero-indexed) + + console.log(`\n==new move command==\ngoing to move ${moveWhat} crates from stack ${moveFrom+1} (index ${moveFrom}) to stack ${moveTo+1} (index ${moveTo})`); + + let toMove = []; + + for(let i = 0; i < moveWhat; i++) { // repeat for how many crates we have to move + console.log(`moving crate #${i+1}/${moveWhat} with value ${stacks[moveFrom][0]}`); + + toMove.push(stacks[moveFrom][0]); // add to list of crates we're going to prepend + stacks[moveFrom].splice(0,1); // remove the crate (the one we just queued to be moved) from the source stack + } + + toMove.reverse(); + + toMove.forEach(crate => stacks[moveTo].unshift(crate)); // add source crates to start of dest stack + + } +} + + +console.log(stacks); + +let bottomRows = ""; +stacks.forEach(s => bottomRows += s[0]); // append first (top) crate of each stack + +console.log(bottomRows); diff --git a/day05/day05.js b/day05/day05.js new file mode 100644 index 0000000..29ef497 --- /dev/null +++ b/day05/day05.js @@ -0,0 +1,56 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + + +let lookingAtStart = true; +let stacks = new Array(); + +for(const line of input.split("\n")) { + const blankLine = (line.length <= 1) ? true : false; + + if (blankLine) lookingAtStart = false; // whether we are looking at the input (the stacks from 1-9), we can use the first blank line to determine this + + if (lookingAtStart) { // if we're looking at the start we need to process the input + const matched = line.match(/.{1,4}/g); + if(!matched) continue; + + let i = 0; + for (const crate of matched) { // split each line into groups of 4 chars + if (crate.match(/ [0-9] /g)) continue; // if the content is " X " we're at the bottom row so discard it + + let value = crate.replace(/\[|\]/g, "").replace(/ /g, "") // remove the square brackets and then spaces + + if (!stacks[i]) stacks[i] = new Array(); // init a new array if one doesn't exist for this stack + + if (value !== "") stacks[i].push(value); // add to the array if not a blank + + i++; + } + } else { // we can move on and work on the movements now + if (blankLine) continue; // skip over blank lines + + const commands = line.match(/[0-9]+/g); + const moveWhat = parseInt(commands[0]); // how many crates + const moveFrom = parseInt(commands[1] - 1); // from what stack (minus one since we are zero-indexed) + const moveTo = parseInt(commands[2] - 1); // to which stack (minus one since we are zero-indexed) + + console.log(`\n==new move command==\ngoing to move ${moveWhat} crates from stack ${moveFrom+1} (index ${moveFrom}) to stack ${moveTo+1} (index ${moveTo})`); + + for(let i = 1; i <= moveWhat; i++) { // repeat for how many crates we have to move + console.log(`moving crate #${i}/${moveWhat} with value ${stacks[moveFrom][0]}`); + + stacks[moveTo].unshift(stacks[moveFrom][0]); // add source crate to start of dest stack + stacks[moveFrom].shift(); // remove the first crate (the one we moved) from the source stack + } + } +} + + +console.log(stacks); + +let bottomRows = ""; +stacks.forEach(s => bottomRows += s[0]); // append first (top) crate of each stack + +console.log(bottomRows); diff --git a/day06/day06.js b/day06/day06.js new file mode 100644 index 0000000..ebc37c1 --- /dev/null +++ b/day06/day06.js @@ -0,0 +1,27 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + + +let currentCheck = []; +let currentCharIndex = 0; +let uniqueChars = 14; // this is 4 for part one, 14 for part two + +for (const char of input.split("")) { + + currentCheck.push(char); // add current character to the current 4 we're checking + + // console.log(currentCheck); + + if (currentCheck.length === (uniqueChars+1)) { + currentCheck.shift(); // remove first element if we have unique+1 chars in the array + // then check whether any chars are unique + const currentCheckSet = new Set(currentCheck); // sets only contain unique elements + console.log(currentCheck, currentCheckSet.size); + if (currentCheckSet.size === uniqueChars) { console.log(currentCharIndex+1); break; } + + } + + currentCharIndex++; +} \ No newline at end of file diff --git a/day07/day07.js b/day07/day07.js new file mode 100644 index 0000000..6747543 --- /dev/null +++ b/day07/day07.js @@ -0,0 +1,96 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let fs = {}; +let workingDir = []; + +for (const line of input.split("\n")) { + if(!line) continue; // skip blank lines + + + // set our current directory + if(line.startsWith("$ cd")) { + + // parse the command + const cmd = line.split(" ")[1]; // will be "cd" + const arg = line.split(" ")[2]; // will be the folder + + if(arg == "/") workingDir = []; // return to / properly + + if (arg == "..") { + workingDir.pop(); // go up a folder by removing the last in the working dir array + } else { + workingDir.push(arg); // else we can append the folder to the end + } + + // if the current dir doesnt exist we init its size + if (!fs[workingDir.join("/")]) fs[workingDir.join("/")] = 0; + continue; // done with this line + } + + + // deal with files + if(line.startsWith("$")) continue; // we don't care about the other commands + + const size = line.split(" ")[0]; // file size + const name = line.split(" ")[1]; // file name + + if (size === "dir") continue; // we don't care about it telling us there's a dir there + + fs[workingDir.join("/")] += parseInt(size); // add the size of the file to the size of the current dir +} + + +// i stole this bit off george and i dont understand it really +// but maybe it works? +for (const dir in fs) { + const name = dir; + let size = fs[dir]; + + for(const subDir in fs) { + const subName = subDir; + const subSize = fs[subDir]; + + if (name == subName) continue; + if (subName.startsWith(name)) size += subSize; + } + + fs[dir] = size; +} +// thanks again big g x + +let totalSize = 0; // of all folders <= 100000 +for(const dir in fs) { + const name = dir; + const size = fs[dir]; + + if(name == "/") continue; // skip over root + if (size <= 100000) totalSize += size; // if we're under we can add to total size +} + +console.log("part one, total size:", totalSize); + + +// PART TWO +const spaceTotal = 70000000; +const spaceRequired = 30000000; +const spaceUsed = fs["/"]; +const spaceFree = spaceTotal - spaceUsed; +const toFree = spaceRequired - spaceFree; + +console.log(spaceFree, "free"); +console.log(toFree, "needed to free"); + +let candidates = []; // for deletion +for (const dir in fs) { + const name = dir; + const size = fs[dir]; + + if (size > toFree) candidates.push({ name, size }); +} + +const sortedCandidates = candidates.sort((a, b) => { return a.size - b.size; }); + +console.log("part two, folder to delete:", sortedCandidates[0].size, sortedCandidates[0]); \ No newline at end of file diff --git a/day08/day08-pt2.js b/day08/day08-pt2.js new file mode 100644 index 0000000..3a4b3f3 --- /dev/null +++ b/day08/day08-pt2.js @@ -0,0 +1,93 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + + +let grid = []; + +let i = 0; +for(const line of input.split("\n")) { + if(!line) continue; // skip blanks + + if(!grid[i]) grid[i] = []; // if there's no existing row then make one + + // build the rows by adding trees + for(const char of line.split("")) { + grid[i].push(char); + } + + i++; +} + +console.table(grid); + +let scores = []; + +for(let row in grid) { + for(let col in grid) { + // i hate JS + row = parseInt(row); + col = parseInt(col); + + const tree = grid[row][col]; + console.log("checking tree", tree, "at", row, col); + + // Efficiency? what's that? + + // check above + let visibleFromAbove = 0; + for(let i = row-1; i >= 0; i--) { // start one row above and move up + const checkTree = grid[i][col]; + if(checkTree >= tree) { + visibleFromAbove++; + break; + } + visibleFromAbove++ + } + + // check below + let visibleFromBelow = 0; + for (let i = row + 1; i < grid.length; i++) { // work downwards + const checkTree = grid[i][col]; + if (checkTree >= tree) { + visibleFromBelow++; + break; + } + visibleFromBelow++ + } + + // check left + let visibleFromLeft = 0; + for (let i = col-1; i >= 0; i--) { // work leftwards + const checkTree = grid[row][i]; + if (checkTree >= tree) { + visibleFromLeft++; + break; + } + visibleFromLeft++ + } + + // check right + let visibleFromRight = 0; + for (let i = col + 1; i < grid[row].length; i++) { // work rightwards + const checkTree = grid[row][i]; + if (checkTree >= tree) { + visibleFromRight++; + break; + } + visibleFromRight++ + } + + + const score = visibleFromAbove * visibleFromLeft * visibleFromBelow * visibleFromRight; + + console.log(" above", visibleFromAbove, "below", visibleFromBelow, "left", visibleFromLeft, "right", visibleFromRight, "for a score of", score); + + scores.push(score); + } + +} + +const sortedScores = scores.sort((a, b) => a - b).reverse(); +console.log(sortedScores[0]); diff --git a/day08/day08.js b/day08/day08.js new file mode 100644 index 0000000..25e3e37 --- /dev/null +++ b/day08/day08.js @@ -0,0 +1,83 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + + +let grid = []; + +let i = 0; +for(const line of input.split("\n")) { + if(!line) continue; // skip blanks + + if(!grid[i]) grid[i] = []; // if there's no existing row then make one + + // build the rows by adding trees + for(const char of line.split("")) { + grid[i].push(char); + } + + i++; +} + +console.table(grid); + +let visible = 0; + +for(const row in grid) { + for(const col in grid) { + const tree = grid[row][col]; + console.log("checking tree", tree, "at", row, col); + + // Efficiency? what's that? + + // check visibility from above + let visibleFromAbove = true; + for(let i = 0; i < row; i++) { + const checkTree = grid[i][col]; + if (checkTree >= tree) { + visibleFromAbove = false; + break; + } + } + console.log(visibleFromAbove, "above"); + + // check visibility from below + let visibleFromBelow = true; + for (let i = grid.length - 1; i > row; i--) { + const checkTree = grid[i][col]; + if (checkTree >= tree) { + visibleFromBelow = false; + break; + } + } + console.log(visibleFromBelow, "below"); + + // check visibility from left + let visibleFromLeft = true; + for (let i = 0; i < col; i++) { + const checkTree = grid[row][i]; + if (checkTree >= tree) { + visibleFromLeft = false; + break; + } + } + console.log(visibleFromLeft, "left"); + + // check visibility from right + let visibleFromRight = true; + for (let i = grid[row].length - 1; i > col; i--) { + const checkTree = grid[row][i]; + if (checkTree >= tree) { + visibleFromRight = false; + break; + } + } + console.log(visibleFromRight, "right"); + + if(visibleFromAbove || visibleFromBelow || visibleFromLeft || visibleFromRight) visible++; + + } +} + +console.log(visible); \ No newline at end of file diff --git a/day09/day09-pt2.js b/day09/day09-pt2.js new file mode 100644 index 0000000..e4195a5 --- /dev/null +++ b/day09/day09-pt2.js @@ -0,0 +1,71 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let visited = new Set(); +let headX = 0; +let headY = 0; + +let knots = [] +for(let i = 0; i < 9; i++) knots.push([0, 0]); +// for easier reference to the knots +const x = 0; +const y = 1; + +for(const instruction of input.split("\n")) { + if(!instruction) continue; + + const direction = instruction.split(" ")[0].toLowerCase(); + const amount = parseInt(instruction.split(" ")[1]); + + console.log(direction, amount); + + for(let i = 0; i < amount; i++) { + switch (direction) { + case "u": // up + headY--; + break; + case "d": // down + headY++; + break; + case "l": // left + headX--; + break; + case "r": // right + headX++; + break; + } + + // first knot follows old tail logic + if (Math.abs(headX - knots[0][x]) > 1 || Math.abs(headY - knots[0][y]) > 1) { + if (headX > knots[0][x]) knots[0][x]++; + if (headX < knots[0][x]) knots[0][x]--; + if (headY > knots[0][y]) knots[0][y]++; + if (headY < knots[0][y]) knots[0][y]--; + } + + for(let knot in knots) { + // we've already moved the first knot + if(knot == 0) continue; + let prevKnot = knots[knot - 1]; + let thisKnot = knots[knot]; + + // if knot is no longer adjacent to prev knot, move it + if (Math.abs(prevKnot[x] - thisKnot[x]) > 1 || Math.abs(prevKnot[y] - thisKnot[y]) > 1) { + if (prevKnot[x] > thisKnot[x]) thisKnot[x]++; + if (prevKnot[x] < thisKnot[x]) thisKnot[x]--; + if (prevKnot[y] > thisKnot[y]) thisKnot[y]++; + if (prevKnot[y] < thisKnot[y]) thisKnot[y]--; + } + } + + const lastKnot = knots[knots.length - 1]; + visited.add(`${lastKnot[x]},${lastKnot[y]}`); + } + + console.log("head", headX, headY, "visited", visited.size); + +} + +console.log(visited.size); \ No newline at end of file diff --git a/day09/day09.js b/day09/day09.js new file mode 100644 index 0000000..64aedbb --- /dev/null +++ b/day09/day09.js @@ -0,0 +1,51 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let visited = new Set(); +let headX = 0; +let headY = 0; +let tailX = 0; +let tailY = 0; + +for(const instruction of input.split("\n")) { + if(!instruction) continue; + + const direction = instruction.split(" ")[0].toLowerCase(); + const amount = parseInt(instruction.split(" ")[1]); + + console.log(direction, amount); + + for(let i = 0; i < amount; i++) { + switch (direction) { + case "u": // up + headY--; + break; + case "d": // down + headY++; + break; + case "l": // left + headX--; + break; + case "r": // right + headX++; + break; + } + + // if tail is no longer adjacent to head, move tail + if(Math.abs(headX - tailX) > 1 || Math.abs(headY - tailY) > 1) { + if(headX > tailX) tailX++; + if(headX < tailX) tailX--; + if(headY > tailY) tailY++; + if(headY < tailY) tailY--; + } + + visited.add(`${tailX},${tailY}`); + } + + console.log("head", headX, headY, "tail", tailX, tailY); + +} + +console.log(visited.size); \ No newline at end of file diff --git a/day10/day10.js b/day10/day10.js new file mode 100644 index 0000000..af9b940 --- /dev/null +++ b/day10/day10.js @@ -0,0 +1,60 @@ +const readFileSync = require("fs").readFileSync; + +// Read Input file +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let cycle = 1; +let registerX = 1; +let valuesAtCycle = []; + +const cyclesToCheck = [20, 60, 100, 140, 180, 220]; + +for(const instruction of input.split("\n")) { + if(!instruction) continue; // ignore blank lines + + const opcode = instruction.split(" ")[0]; + const value = instruction.split(" ")[1]; + + switch (opcode) { + case "noop": + // "noop takes one cycle to complete. It has no other effect." + valuesAtCycle.push(registerX); + cycle++; + break; + case "addx": + // "addx V takes two cycles to complete. + // After two cycles, the X register is increased by the value V. (V can be negative.)" + cycle++; + valuesAtCycle.push(registerX); + + registerX += parseInt(value); + + cycle++; + valuesAtCycle.push(registerX); + break; + } + + console.log(cycle, "(after)", registerX, opcode, value); +} + +let sum = 0; +cyclesToCheck.forEach(c => sum += valuesAtCycle[c-2] * c); +console.log("part one:", sum); + + +// PART TWO +// this is 100% not how you are meant to do this +// but it works and I'm not going to spend any more time on it + +let out = ""; + +for(let c in valuesAtCycle) { + const beamPos = c % 40; + const spritePos = valuesAtCycle[c-1]; + + out += (beamPos == spritePos || beamPos == spritePos - 1 || beamPos == spritePos + 1) ? "#" : " "; + + if(beamPos === 39) out += "\n"; +} + +console.log(out); diff --git a/day11/day11.js b/day11/day11.js new file mode 100644 index 0000000..8d3e3c7 --- /dev/null +++ b/day11/day11.js @@ -0,0 +1,86 @@ +const readFileSync = require("fs").readFileSync; +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +const ROUNDS = 10000; // 20 for part one, 10000 for part two + +// i found this on the subreddit and i have no idea what it means even after reading the theory +// thanks j122j: https://www.reddit.com/r/adventofcode/comments/zifqmh/2022_day_11_solutions/izuniw9/ +// and jake-gordon: https://github.com/jake-gordon/aoc/blob/main/2022/D11/Explanation.md +let thisIsBeyondMyMathsGrade = 1; + +// monkey cage +let monkeys = []; + +class Monkey { + constructor(num, startingItems, operation, operand, divisor, ifTrue, ifFalse) { + this.num = num; + this.items = startingItems; + this.operation = operation; + this.operand = operand; + this.divisor = divisor; + this.ifTrue = ifTrue; + this.ifFalse = ifFalse; + this.inspected = 0; + } + + giveItem(item) { + this.items.push(item); + } + + inspectItems() { + // inspect each item, working backwards from the end of the array + for (let i = this.items.length - 1; i >= 0; i--) { + const item = this.items[i]; + + const operand = parseInt((Number.isInteger(this.operand)) ? this.operand : item); // if the operand is "old" set it as the current worry level + + const worryLevelA = Math.floor(eval(`(${item} ${this.operation} ${operand})`)); + const worryLevel = worryLevelA % thisIsBeyondMyMathsGrade; // thank you subreddit + + if (worryLevel === Infinity) throw new Error(`uh oh: ${item} ${this.operation} ${operand} = ${worryLevelA} (mod ${thisIsBeyondMyMathsGrade} = ${worryLevel})`); + + if (worryLevel % parseInt(this.divisor) === 0) { + const recipient = monkeys[parseInt(this.ifTrue)]; + recipient.giveItem(worryLevel); + + } else { + const recipient = monkeys[parseInt(this.ifFalse)]; + recipient.giveItem(worryLevel); + } + + // remove this item from the array + this.items.splice(i, 1); + + + this.inspected++; + } + } +} + + +// FIGURE OUT THE SILLY INPUT +for (const monkey of input.split("\n\n")) { + const splitMonkey = monkey.split("\n"); + const num = parseInt(splitMonkey[0].replace(/[^0-9]/g, "")); + const startingItems = splitMonkey[1].replace(/[^0-9,]/g, "").split(','); + const operation = splitMonkey[2].charAt(23); + const operand = parseInt(splitMonkey[2].replace(/[^0-9]/g, "")); + const divisor = parseInt(splitMonkey[3].replace(/[^0-9]/g, "")); + const ifTrue = parseInt(splitMonkey[4].replace(/[^0-9]/g, "")); + const ifFalse = parseInt(splitMonkey[5].replace(/[^0-9]/g, "")); + + monkeys.push(new Monkey(num, startingItems, operation, operand, divisor, ifTrue, ifFalse)); +} + +thisIsBeyondMyMathsGrade = monkeys.reduce((a, b) => a * b.divisor, 1); + +// do the rounds of inspecting items +for (let i = 0; i < ROUNDS; i++) { + monkeys.forEach(m => m.inspectItems()); +} + +// get top two monkeys based on items inspected (thank you copilot ily) +const topMonkeys = monkeys.sort((a, b) => b.inspected - a.inspected).slice(0, 2); + +const monkeyBusiness = topMonkeys[0].inspected * topMonkeys[1].inspected; +console.log(monkeyBusiness); \ No newline at end of file diff --git a/day12/day12-pt2.js b/day12/day12-pt2.js new file mode 100644 index 0000000..07f9fd5 --- /dev/null +++ b/day12/day12-pt2.js @@ -0,0 +1,129 @@ +const readFileSync = require("fs").readFileSync; +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let grid = []; +// yes i know i can just reverse the previous solution (start at E and look for the first "a") +// but this is funnier +let starts = []; +let end; + +let bestDists = []; + +for (let y in input.split("\n")) { + y = parseInt(y); + + const line = input.split("\n")[y]; + + if(!grid[y]) grid[y] = []; // init missing rows + + for (let x in line.split("")) { + x = parseInt(x); + + const letter = line.split("")[x]; + + // set to height value of the letter (1-26) + grid[y][x] = {x, y, letter, height: letter.charCodeAt(0) - 96, visited: false, dist: Infinity}; + + // if it's a starting spot, add to array + if(letter === "a") starts.push(grid[y][x]); + + // if it's the end, set to height 27 + if(letter === "E") { + grid[y][x].height = 27; + end = grid[y][x]; + } + } +} + +for(const start of starts) { + let queue = [start]; + + // loop over every element in grid and reset + for (let y in grid) { + for (let x in grid[y]) { + grid[y][x].visited = false; + grid[y][x].dist = Infinity; + } + } + + start.dist = 0; + + // while there are still points in the queue + while (queue.length > 0) { + const currentSpot = queue[0]; + const x = currentSpot.x; + const y = currentSpot.y; + + // remove the current point from the queue + queue.shift(); + + // ignore visited spots + if (currentSpot.visited) continue; + currentSpot.visited = true; + + + if (currentSpot.letter === end.letter) { + bestDists.push(currentSpot.dist); + break; + } + + // check the 4 surrounding points + // if they are not visited, and are within the height limit, add them to the queue + + // above + const spotAbove = (grid[y - 1]) ? grid[y - 1][x] : false; + if (spotAbove) { // make sure the row exists + if (spotAbove.height <= currentSpot.height + 1 && !spotAbove.visited) { + spotAbove.dist = currentSpot.dist + 1; // set its distance + queue.push(spotAbove); // add it to queue + } + } + + // below + const spotBelow = (grid[y + 1]) ? grid[y + 1][x] : false; + if (spotBelow) { // make sure the row exists + if (spotBelow.height <= currentSpot.height + 1 && !spotBelow.visited) { + spotBelow.dist = currentSpot.dist + 1; // set its distance + queue.push(spotBelow); // add it to queue + } + } + + // left + const spotLeft = (grid[y][x - 1]) ? grid[y][x - 1] : false; + if (spotLeft) { // make sure the row exists + if (spotLeft.height <= currentSpot.height + 1 && !spotLeft.visited) { + spotLeft.dist = currentSpot.dist + 1; // set its distance + queue.push(spotLeft); // add it to queue + } + } + + // right + const spotRight = (grid[y][x + 1]) ? grid[y][x + 1] : false; + if (spotRight) { // make sure the row exists + if (spotRight.height <= currentSpot.height + 1 && !spotRight.visited) { + spotRight.dist = currentSpot.dist + 1; // set its distance + queue.push(spotRight); // add it to queue + } + } + } +} + +console.log(bestDists.sort((a, b) => a - b)[0]); + +function printGrid() { + let outGrid = ""; + for (let y in grid) { + for (let x in grid[y]) { + outGrid += (grid[y][x].visited) ? "#" : grid[y][x].letter; + // outGrid += grid[y][x].letter; + // outGrid += (grid[y][x].visited === true) ? "y" : "n"; + // outGrid += (grid[y][x].dist === Infinity) ? "∞" : grid[y][x].dist; + // outGrid += " "; + } + outGrid += "\n"; + } + + console.log(outGrid); +} + + diff --git a/day12/day12.js b/day12/day12.js new file mode 100644 index 0000000..837c095 --- /dev/null +++ b/day12/day12.js @@ -0,0 +1,119 @@ +const readFileSync = require("fs").readFileSync; +const input = readFileSync("input.txt", { encoding: "utf8", flag: "r" }); + +let grid = []; +let start; +let end; + +for (let y in input.split("\n")) { + y = parseInt(y); + + const line = input.split("\n")[y]; + + if(!grid[y]) grid[y] = []; // init missing rows + + for (let x in line.split("")) { + x = parseInt(x); + + const letter = line.split("")[x]; + + // set to height value of the letter (1-26) + grid[y][x] = {x, y, letter, height: letter.charCodeAt(0) - 96, visited: false, dist: Infinity}; + + // if it's the start, set to height zero + if(letter === "S") { + grid[y][x].height = 0; + grid[y][x].dist = 0; + start = grid[y][x]; + } + + // if it's the end, set to height 27 + if(letter === "E") { + grid[y][x].height = 27; + end = grid[y][x]; + } + } +} + + +let queue = [start]; + +// while there are still points in the queue +while (queue.length > 0) { + const currentSpot = queue[0]; + const x = currentSpot.x; + const y = currentSpot.y; + + // remove the current point from the queue + queue.shift(); + + // ignore visited spots + if(currentSpot.visited) continue; + currentSpot.visited = true; + + + if(currentSpot.letter === end.letter) { + console.log("Found end!"); + console.log(currentSpot); + break; + } + + // check the 4 surrounding points + // if they are not visited, and are within the height limit, add them to the queue + + // above + const spotAbove = (grid[y-1]) ? grid[y-1][x] : false; + if(spotAbove) { // make sure the row exists + if(spotAbove.height <= currentSpot.height+1 && !spotAbove.visited) { + spotAbove.dist = currentSpot.dist + 1; // set its distance + queue.push(spotAbove); // add it to queue + } + } + + // below + const spotBelow = (grid[y+1]) ? grid[y+1][x] : false; + if(spotBelow) { // make sure the row exists + if(spotBelow.height <= currentSpot.height+1 && !spotBelow.visited) { + spotBelow.dist = currentSpot.dist + 1; // set its distance + queue.push(spotBelow); // add it to queue + } + } + + // left + const spotLeft = (grid[y][x-1]) ? grid[y][x-1] : false; + if(spotLeft) { // make sure the row exists + if(spotLeft.height <= currentSpot.height+1 && !spotLeft.visited) { + spotLeft.dist = currentSpot.dist + 1; // set its distance + queue.push(spotLeft); // add it to queue + } + } + + // right + const spotRight = (grid[y][x+1]) ? grid[y][x+1] : false; + if(spotRight) { // make sure the row exists + if(spotRight.height <= currentSpot.height+1 && !spotRight.visited) { + spotRight.dist = currentSpot.dist + 1; // set its distance + queue.push(spotRight); // add it to queue + } + } + + printGrid(); +} + +function printGrid() { + let outGrid = ""; + for (let y in grid) { + for (let x in grid[y]) { + outGrid += (grid[y][x].visited) ? "#" : grid[y][x].letter; + // outGrid += grid[y][x].letter; + // outGrid += (grid[y][x].visited === true) ? "y" : "n"; + // outGrid += (grid[y][x].dist === Infinity) ? "∞" : grid[y][x].dist; + // outGrid += " "; + } + outGrid += "\n"; + } + + console.log(outGrid); +} + + diff --git a/day13/day13.js b/day13/day13.js new file mode 100644 index 0000000..3dd3fe5 --- /dev/null +++ b/day13/day13.js @@ -0,0 +1,56 @@ +const readFileSync = require("fs").readFileSync; +const input = readFileSync("input-test.txt", { encoding: "utf8", flag: "r" }); + +let group = []; +let pairIndex = 1; +let inOrderIndexes = []; + +for(const line of input.split("\n")) { + if(!line) { + group = []; + continue; + } + + group.push(line); + + if(group.length == 2) { + if (!group[0].startsWith("[") || !group[1].startsWith("[")) continue; + + const left = eval(group[0]); + const right = eval(group[1]); + + if (checkOrder(left, right)) { + inOrderIndexes.push(pairIndex); + } + + pairIndex++; + } +} + +console.log(inOrderIndexes); + +function checkOrder(left, right) { + // console.log(left, right); + + const largest = Math.max(left.length, right.length); + + for (let i = 0; i < largest; i++) { + const firstItem = left[i]; + const secondItem = right[i]; + + + if (!firstItem) return true; + if (!secondItem) return false; + + if (typeof (firstItem) === 'number' && typeof (secondItem) === 'number') { + if (firstItem > secondItem) return false; + if (secondItem < firstItem) return true; + } + + if (!Array.isArray(firstItem)) return checkOrder([firstItem], secondItem); + if (!Array.isArray(secondItem)) return checkOrder(firstItem, [secondItem]); + + const knowsOrder = checkOrder(firstItem, secondItem); + if (knowsOrder !== undefined) return knowsOrder; + } +} \ No newline at end of file