From 2c3c1f8f088e43d41db4c28a44528618249d7860 Mon Sep 17 00:00:00 2001 From: Bas Dado Date: Sun, 19 Dec 2021 12:15:50 +0100 Subject: [PATCH] [WIP] Day 17 part 1 solved --- functions/package.json | 4 +- functions/src/day17.ts | 183 ++++++++++++++++++++++++++++++----------- functions/yarn.lock | 85 +++++++++++++++++++ 3 files changed, 225 insertions(+), 47 deletions(-) diff --git a/functions/package.json b/functions/package.json index 79d96a6..c68c296 100644 --- a/functions/package.json +++ b/functions/package.json @@ -7,7 +7,8 @@ "shell": "npm run build && firebase functions:shell", "start": "npm run shell", "deploy": "firebase deploy --only functions", - "logs": "firebase functions:log" + "logs": "firebase functions:log", + "debug-day17": "ts-node src/day17.ts" }, "engines": { "node": "16" @@ -24,6 +25,7 @@ "eslint-config-google": "^0.14.0", "eslint-plugin-import": "^2.22.0", "firebase-functions-test": "^0.2.0", + "ts-node": "^10.4.0", "typescript": "^3.8.0" }, "private": true diff --git a/functions/src/day17.ts b/functions/src/day17.ts index 96d0548..f5d93d6 100644 --- a/functions/src/day17.ts +++ b/functions/src/day17.ts @@ -2,58 +2,74 @@ import Day from "./day"; class Day17 implements Day { + // steps = 3 + // y = 1 -> 1 + 0 + -1 = 0 + // y = 2 -> 2 + 1 + 0 = 3 + // y = 3 -> 3 + 2 + 1 = 6 + // y = 4 -> 4 + 3 + 2 = 9 + // y = 5 -> 5 + 4 + 3 = 12 + + // steps = 4 + // y = 1 -> -2 + // y = 2 -> 2 + // y = 3 -> 6 + // y = 4 -> 10 + + // y = 0 + // steps = 1 -> 0 = 0 + // steps = 2 -> 0 - 1 = -1 + // steps = 3 -> 0 - 1 - 2 = -3 + // steps = 4 -> 0 - 1- 2 - 3= -6 + // steps = 5 -> 0-1-2-3-4 = -10 + // steps = 6 -> 0-1-2-3-4-5 = -15 + + // v = -1 * x + + // Theoretically, the displacement should be: + // s(t) = s_0 + v_0 t + 1/2 a t^2 + // Filling that in for s_0 = 0 and a = -1, we get: + // s(t) = v_0 t - 1/2 t ^ 2 + // But that gives us non-integer values, for example when t = 3, we get: v_0 * 3 - 4.5 + part1(input: string[]): number | string { const targetArea = TargetArea.parse(input[0]); - // First we find all x-es between 1 and maxX + 1 (as all others will immediately overshoot), that overlap with the target at some points: - for (let x = 1; x <= targetArea.maxX + 1; x++) { - let curSpeed = x; - let curPosX = 0; - for (let step = 0; step < (targetArea.maxX + 1) / x; step++) { + // Assuming v_y0 > 0, the maximum height the probe reaches is triangle(y). + // After reaching the top, the probe starts falling back down, taking "v_y0" steps to get back to a height of 0. + // After that it has a speed of v_y0 - 1. - curPosX += curSpeed; - if (targetArea.isInX(curPosX)) { - // Find y's that given this position and step, match: - // TODO: find out which y's make sense to check - for (let y = 0; y < 100; y++) { - // steps = 3 - // y = 1 -> 1 + 0 + -1 = 0 - // y = 2 -> 2 + 1 + 0 = 3 - // y = 3 -> 3 + 2 + 1 = 6 - // y = 4 -> 4 + 3 + 2 = 9 - // y = 5 -> 5 + 4 + 3 = 12 + // Note that the triangle value of x must be at least the minimal x of the target area, otherwise the probe won't + // reach the target area before it's velocity reaches zero: + const vx0_min = this.triangleInvCeil(targetArea.minX); + // Calculate the x-position we reach after v_x0 steps: + const x_min = this.triangle(vx0_min); - // steps = 4 - // y = 1 -> -2 - // y = 2 -> 2 - // y = 3 -> 6 - // y = 4 -> 10 - - // y = 0 - // steps = 1 -> 0 = 0 - // steps = 2 -> 0 - 1 = -1 - // steps = 3 -> 0 - 1 - 2 = -3 - // steps = 4 -> 0 - 1- 2 - 3= -6 - // steps = 5 -> 0-1-2-3-4 = -10 - // steps = 6 -> 0-1-2-3-4-5 = -15 - - // v = -1 * x - // pos = -.5 * steps^2 + y * steps - - // f(s) = s * y + - } - const { maxY, finalY } = this.simulateY() - } else if ( curPosX > targetArea.maxX || (curSpeed === 0 && curPosX < targetArea.minX)) { - break; - } - curSpeed--; - } + if (targetArea.isInX(x_min)) { + // We have an x-speed that reaches the target area in x_min steps and stays there (e.g. reaches velocity zero in the target area), + // so we can just find the biggest y that at some point ends up in the target area. + // We already know that the y speed when the "probe" comes down is: + // -v_y0 - 1 + // So if we choose as starting y that is one higher than the minimal y value, we end up in the bottom of the target area, while reaching the maximum height: + const vy0_max = Math.abs(targetArea.minY) - 1; + return this.triangle(vy0_max); + } else { + throw Error("fuck :)"); } - - return 0; } + part2(input: string[]): number | string { + + // So that means that the only y values that work are those for which two triangle numbers exists, between which the distance falls + // in the target area. + // Following this reasoning, we also know that y is inside the target area after (2 * v_y0) + { the number of triangle numbers between triangle(vy_0) and triangle(?) in y_range) } + + const targetArea = TargetArea.parse(input[0]); + + + // TODO implement + return 0; + } simulateY(initialSpeedY: number, steps: number): { maxY: number, finalY: number} { let curSpeed = initialSpeedY; @@ -69,9 +85,12 @@ class Day17 implements Day { return { finalY: y, maxY: maxY }; } - part2(input: string[]): number | string { - // TODO implement - return 0; + triangle(n: number): number { + return (n*(n + 1)) / 2; + } + + triangleInvCeil(x: number): number { + return Math.ceil((Math.sqrt(8 * x + 1) - 1) / 2); } } @@ -121,3 +140,75 @@ class TargetArea { } export default Day17; + +const exampleInput = ["target area: x=20..30, y=-10..-5"]; +run(exampleInput); + +const input = ["target area: x=48..70, y=-189..-148"]; +run(input); + + +function run(input: string[]) { + const day = new Day17(); + console.log(`Part 1: ${day.part1(input)}`); + console.log(`Part 2: ${day.part2(input)}`); +} + + +// Archive: + +// const maxYs: number[] = []; +// const yMinStepInTargets = []; +// const yMaxStepInTargets = []; +// // Array(100).fill([]); +// // Let's first find the y values that reach the highest height and end up in the target area: +// for (let vy0 = 0; vy0 < Math.abs(targetArea.minY) + 1; vy0++) { +// +// let y = 0; +// const ys = [y]; +// let vy = vy0; +// let yMinStepInTarget = -1; +// let yMaxStepInTarget = -1; +// let step = 0; +// while (y > targetArea.minY) { +// y += vy; +// ys.push(y); +// vy--; +// if (targetArea.isInY(y)) { +// if (yMinStepInTarget === -1) { +// yMinStepInTarget = step; +// } +// yMaxStepInTarget = step; +// } +// step++; +// } +// yMinStepInTargets.push(yMinStepInTarget) +// yMaxStepInTargets.push(yMaxStepInTarget) +// // yCache.push(ys); +// maxYs.push(Math.max(...ys)); +// } +// +// // Now we "greedily" go over the found y values +// +// // First we find all x-es between 1 and maxX + 1 (as all others will immediately overshoot), that overlap with the target at some points: +// for (let x = 1; x <= targetArea.maxX + 1; x++) { +// let curSpeed = x; +// let curPosX = 0; +// for (let step = 0; step < (targetArea.maxX + 1) / x; step++) { +// +// curPosX += curSpeed; +// if (targetArea.isInX(curPosX)) { +// // Find y's that given this position and step, match: +// // TODO: find out which y's make sense to check +// for (let y = 0; y < 100; y++) { +// +// +// // f(s) = s * y + +// } +// const { maxY, finalY } = this.simulateY() +// } else if ( curPosX > targetArea.maxX || (curSpeed === 0 && curPosX < targetArea.minX)) { +// break; +// } +// curSpeed--; +// } +// } diff --git a/functions/yarn.lock b/functions/yarn.lock index 9c2d8ba..6025839 100644 --- a/functions/yarn.lock +++ b/functions/yarn.lock @@ -23,6 +23,18 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -278,6 +290,26 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + +"@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + +"@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + +"@tsconfig/node16@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + "@types/body-parser@*": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" @@ -474,11 +506,21 @@ acorn-jsx@^5.3.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn@^8.4.1: + version "8.6.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" + integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -530,6 +572,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -745,6 +792,11 @@ cors@^2.8.5: object-assign "^4" vary "^1" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -814,6 +866,11 @@ dicer@^0.3.0: dependencies: streamsearch "0.1.2" +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -1903,6 +1960,11 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -2515,6 +2577,24 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +ts-node@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" + integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== + dependencies: + "@cspotcode/source-map-support" "0.7.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + yn "3.1.1" + tsconfig-paths@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b" @@ -2740,6 +2820,11 @@ yargs@^16.1.1: y18n "^5.0.5" yargs-parser "^20.2.2" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"