[TASK] Solved Day 17
This commit is contained in:
@@ -23,14 +23,6 @@ class Day17 implements Day {
|
|||||||
// steps = 5 -> 0-1-2-3-4 = -10
|
// steps = 5 -> 0-1-2-3-4 = -10
|
||||||
// steps = 6 -> 0-1-2-3-4-5 = -15
|
// 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 {
|
part1(input: string[]): number | string {
|
||||||
|
|
||||||
const targetArea = TargetArea.parse(input[0]);
|
const targetArea = TargetArea.parse(input[0]);
|
||||||
@@ -41,20 +33,21 @@ class Day17 implements Day {
|
|||||||
|
|
||||||
// Note that the triangle value of x must be at least the minimal x of the target area, otherwise the probe won't
|
// 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:
|
// reach the target area before it's velocity reaches zero:
|
||||||
const vx0_min = this.triangleInvCeil(targetArea.minX);
|
const v_x0_min = Day17.triangleInvCeil(targetArea.minX);
|
||||||
// Calculate the x-position we reach after v_x0 steps:
|
// Calculate the x-position we reach after v_x0 steps:
|
||||||
const x_min = this.triangle(vx0_min);
|
const x_min = Day17.triangle(v_x0_min);
|
||||||
|
|
||||||
if (targetArea.isInX(x_min)) {
|
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),
|
// 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.
|
// 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:
|
// We already know that the y speed when the "probe" comes down is:
|
||||||
// -v_y0 - 1
|
// -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:
|
// 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
|
||||||
const vy0_max = Math.abs(targetArea.minY) - 1;
|
// reaching the maximum height:
|
||||||
return this.triangle(vy0_max);
|
const v_y0_max = Math.abs(targetArea.minY) - 1;
|
||||||
|
return Day17.triangle(v_y0_max);
|
||||||
} else {
|
} else {
|
||||||
throw Error("fuck :)");
|
throw Error("Unsupported target area, only a target area with an x values that is a triangle number is supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,33 +59,144 @@ class Day17 implements Day {
|
|||||||
|
|
||||||
const targetArea = TargetArea.parse(input[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 maxYSteps0 = Day17.triangleInvCeil(Math.abs(targetArea.minY));
|
||||||
|
|
||||||
// TODO implement
|
let solutionCount = 0;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
simulateY(initialSpeedY: number, steps: number): { maxY: number, finalY: number} {
|
|
||||||
|
|
||||||
let curSpeed = initialSpeedY;
|
for (let v_x0 = v_x0_min; v_x0 <= targetArea.maxX; v_x0++) {
|
||||||
let y = 0;
|
const maxSteps = v_x0 <= v_x0_max_static ? 1000 : v_x0_max_static;
|
||||||
let maxY = 0;
|
for (let v_y0 = targetArea.minY; v_y0 < Math.abs(targetArea.minY); v_y0++) {
|
||||||
for (let step = 0; step < steps; step++) {
|
for (let steps = 1; steps < maxSteps; steps++) {
|
||||||
y += curSpeed;
|
const xRangeResult = this.isInXAfterSteps(v_x0, steps, targetArea);
|
||||||
curSpeed--;
|
if (xRangeResult === RangeResult.TOO_HIGH) {
|
||||||
if (y > maxY) {
|
break;
|
||||||
maxY = y;
|
} 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 { finalY: y, maxY: maxY };
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return solutionCount;
|
||||||
|
|
||||||
|
// // We gonna have to semi-brute-force anyway,
|
||||||
|
//
|
||||||
|
// // But first, let's find all x-es that perpetually stay inside the target area
|
||||||
|
// const v_x0_min = Day17.triangleInvCeil(targetArea.minX);
|
||||||
|
// const v_x0_max_static = Day17.triangleInvFloor(targetArea.maxX);
|
||||||
|
//
|
||||||
|
// // const vxStaticCount = (v_x0_max_static - v_x0_min) + 1;
|
||||||
|
// //
|
||||||
|
// // Then we count all y's that end up in the target area after v_x0_min steps
|
||||||
|
// // Note that any
|
||||||
|
// // if (v_x0_min * 2 + 1 > Math.abs(targetArea.maxY) + 1);
|
||||||
|
// let vyStaticCount = 0;
|
||||||
|
// // With a start y speed of 0, we can stay in the target area for at most this many steps:
|
||||||
|
// const maxYSteps0 = Day17.triangleInvCeil(Math.abs(targetArea.minY));
|
||||||
|
//
|
||||||
|
// for (let vy_0 = targetArea.minY; vy_0 < Math.abs(targetArea.minY); vy_0++) {
|
||||||
|
// const minSteps = vy_0 > 0 ? Math.max(v_x0_min, 2 * vy_0 + 1) : v_x0_min;
|
||||||
|
// const maxSteps = vy_0 > 0 ? minSteps + maxYSteps0 : maxYSteps0;
|
||||||
|
// for (let steps = minSteps; steps < maxSteps; steps++) {
|
||||||
|
// const rangeResult = this.isInYAfterSteps(vy_0, steps, targetArea);
|
||||||
|
// if (rangeResult === RangeResult.YES) {
|
||||||
|
// vyStaticCount++;
|
||||||
|
// break;
|
||||||
|
// } else if (rangeResult === RangeResult.TOO_LOW) {
|
||||||
|
// // We've fallen below the target area, no need to scan this range any further
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const staticSolutions = (v_x0_max_static - v_x0_min + 1) * vyStaticCount;
|
||||||
|
//
|
||||||
|
// // Any more numbers of steps will overshoot the x direction
|
||||||
|
// let dynamicSolutions = 0;
|
||||||
|
// for (let steps = 1; steps <= v_x0_max_static; steps++) {
|
||||||
|
// for (let vx_0 = v_x0_min; vx_0 < targetArea.maxX + 1; vx_0++) {
|
||||||
|
// if (this.isInXAfterSteps(vx_0, steps, targetArea) !== RangeResult.YES) continue;
|
||||||
|
// for (let vy_0 = targetArea.minY; vy_0 < Math.abs(targetArea.minY) - 1; vy_0++) {
|
||||||
|
// const rangeResult = this.isInYAfterSteps(vy_0, steps, targetArea);
|
||||||
|
// if (rangeResult === RangeResult.YES) {
|
||||||
|
// dynamicSolutions++;
|
||||||
|
// } else if (rangeResult === RangeResult.TOO_HIGH) {
|
||||||
|
// // We've fallen below the target area
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return dynamicSolutions + staticSolutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
triangle(n: number): number {
|
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;
|
return (n*(n + 1)) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
triangleInvCeil(x: number): number {
|
static triangleInvCeil(x: number): number {
|
||||||
return Math.ceil((Math.sqrt(8 * x + 1) - 1) / 2);
|
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 {
|
class TargetArea {
|
||||||
@@ -126,16 +230,20 @@ class TargetArea {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
isInX(x: number): boolean {
|
isInX(x: number): RangeResult {
|
||||||
return x >= this.minX && x <= this.maxX;
|
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 {
|
isInY(y: number): RangeResult {
|
||||||
return y >= this.minY && y <= this.maxY;
|
if (y < this.minY) return RangeResult.TOO_LOW;
|
||||||
|
else if (y > this.maxY) return RangeResult.TOO_HIGH;
|
||||||
|
else return RangeResult.YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
isIn(x: number, y: number): boolean {
|
isIn(x: number, y: number): boolean {
|
||||||
return this.isInX(x) && this.isInY(y);
|
return this.isInX(x) === RangeResult.YES && this.isInY(y) === RangeResult.YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user