Compare commits

...

3 Commits

Author SHA1 Message Date
5d54a107d4 [TASK] Cleaned up (and improved the performance a little) for day 17 2021-12-19 14:53:25 +01:00
9fc38b9b88 [TASK] Solved Day 17 2021-12-19 14:42:04 +01:00
2c3c1f8f08 [WIP] Day 17 part 1 solved 2021-12-19 12:15:50 +01:00
5 changed files with 244 additions and 69 deletions

View File

@@ -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

View File

@@ -2,78 +2,154 @@ 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
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 v_x0_min = Day17.triangleInvCeil(targetArea.minX);
// Calculate the x-position we reach after v_x0 steps:
const x_min = Day17.triangle(v_x0_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 v_y0_max = Math.abs(targetArea.minY) - 1;
return Day17.triangle(v_y0_max);
} else {
throw Error("Unsupported target area, only a target area with an x values that is a triangle number is supported");
}
return 0;
}
simulateY(initialSpeedY: number, steps: number): { maxY: number, finalY: number} {
let curSpeed = initialSpeedY;
let y = 0;
let maxY = 0;
for (let step = 0; step < steps; step++) {
y += curSpeed;
curSpeed--;
if (y > maxY) {
maxY = y;
}
}
return { finalY: y, maxY: maxY };
}
part2(input: string[]): number | string {
// TODO implement
return 0;
const targetArea = TargetArea.parse(input[0]);
const v_x0_min = Day17.triangleInvCeil(targetArea.minX);
const v_x0_max_static = Day17.triangleInvFloor(targetArea.maxX);
const maxYStepsBelowZero = Day17.triangleInvCeil(Math.abs(targetArea.minY));
let solutionCount = 0;
for (let v_x0 = v_x0_min; v_x0 <= targetArea.maxX; v_x0++) {
for (let v_y0 = targetArea.minY; v_y0 < Math.abs(targetArea.minY); v_y0++) {
// If v_x0 <= v_x0_max_static, we end up in a position where eventually all x-es fall in the valid range
// So in that case, we base the max steps on the y values.
const maxSteps = v_x0 <= v_x0_max_static
? (v_y0 < 0 ? maxYStepsBelowZero : 2 * v_y0 + 1 + maxYStepsBelowZero)
: v_x0_max_static;
for (let steps = 1; steps <= maxSteps; steps++) {
const xRangeResult = this.isInXAfterSteps(v_x0, steps, targetArea);
if (xRangeResult === RangeResult.TOO_HIGH) {
break;
} if (xRangeResult === RangeResult.YES) {
// Check what y values work for this x and step count:
const yRangeResult = this.isInYAfterSteps(v_y0, steps, targetArea);
if (yRangeResult === RangeResult.YES) {
// console.log(`${v_x0}, ${v_y0}`)
solutionCount++;
break;
}
}
}
}
}
return solutionCount;
}
isInXAfterSteps(v_x0: number, steps: number, targetArea: TargetArea): RangeResult {
if (steps >= v_x0) {
// reached final position, which is the triangle number of the initial speed:
return targetArea.isInX(Day17.triangle(v_x0));
} else {
// On our way to the final position:
const x = Day17.triangle(v_x0) - Day17.triangle(v_x0 - steps);
return targetArea.isInX(x);
}
}
isInYAfterSteps(v_y0: number, steps: number, targetArea: TargetArea): RangeResult {
if (targetArea.maxY >= 0) throw Error("Only target area's below the starting point are supported")
if (v_y0 > 0) {
if (steps < 2 * v_y0) {
// We haven't gone below the
return RangeResult.TOO_HIGH;
} else {
// This is equivalent to firing with a negative y speed after v_y0 * 2 + 1 steps (so the steps left = steps - 2 * v_y0 - 1)
return this.isInYAfterSteps(-v_y0 - 1, steps - 2 * v_y0 - 1, targetArea);
}
} else {
// Firing downward:
const y = - Day17.calcPositionAfterIncreasingVelocity(Math.abs(v_y0), steps);
return targetArea.isInY(y);
}
}
static calcPositionAfterIncreasingVelocity(v: number, steps: number): number {
if (v < 0) throw Error("Velocity must be positive");
return Day17.triangle(v - 1 + steps) - Day17.triangle(v - 1);
}
static triangle(n: number): number {
return (n*(n + 1)) / 2;
}
static triangleInvCeil(x: number): number {
return Math.ceil(this.triangleInv(x));
}
static triangleInvFloor(x: number): number {
return Math.floor(this.triangleInv(x));
}
static triangleInv(x: number): number {
return (Math.sqrt(8 * x + 1) - 1) / 2;
}
}
enum RangeResult {
TOO_LOW = -1,
TOO_HIGH = 0,
YES = 1
}
class TargetArea {
@@ -107,16 +183,16 @@ class TargetArea {
)
}
isInX(x: number): boolean {
return x >= this.minX && x <= this.maxX;
isInX(x: number): RangeResult {
if (x < this.minX) return RangeResult.TOO_LOW;
else if (x > this.maxX) return RangeResult.TOO_HIGH;
else return RangeResult.YES;
}
isInY(y: number): boolean {
return y >= this.minY && y <= this.maxY;
}
isIn(x: number, y: number): boolean {
return this.isInX(x) && this.isInY(y);
isInY(y: number): RangeResult {
if (y < this.minY) return RangeResult.TOO_LOW;
else if (y > this.maxY) return RangeResult.TOO_HIGH;
else return RangeResult.YES;
}
}

View File

@@ -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"

View File

@@ -0,0 +1,6 @@
POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-17
Content-Type: text/plain
target area: x=20..30, y=-10..-5
###

6
input/day/17.http Normal file
View File

@@ -0,0 +1,6 @@
POST http://localhost:5001/advent-of-code-2021-911a8/europe-west1/day-17
Content-Type: text/plain
target area: x=48..70, y=-189..-148
###