-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,094 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import "stdlib/io.jou" | ||
|
||
|
||
def cost(a: int, b: int) -> int: | ||
return 3*a + b | ||
|
||
|
||
class ClawMachine: | ||
a_vector: int[2] | ||
b_vector: int[2] | ||
prize: int[2] | ||
|
||
def is_solution(self, a: int, b: int) -> bool: | ||
return ( | ||
a * self->a_vector[0] + b * self->b_vector[0] == self->prize[0] | ||
and a * self->a_vector[1] + b * self->b_vector[1] == self->prize[1] | ||
) | ||
|
||
# Returns smallest possible cost to win, or -1 if cannot win | ||
def optimize(self) -> int: | ||
best_cost = -1 | ||
|
||
for a = 0; a <= 100; a++: | ||
for b = 0; b <= 100; b++: | ||
if self->is_solution(a, b) and (best_cost == -1 or cost(a, b) < best_cost): | ||
best_cost = cost(a, b) | ||
|
||
return best_cost | ||
|
||
|
||
def main() -> int: | ||
f = fopen("sampleinput.txt", "r") | ||
assert f != NULL | ||
|
||
total_price = 0 | ||
|
||
ax: int | ||
ay: int | ||
bx: int | ||
by: int | ||
px: int | ||
py: int | ||
|
||
while True: | ||
ret = fscanf(f, "Button A: X+%d, Y+%d\n", &ax, &ay) | ||
if ret != 2: | ||
# End of file reached | ||
break | ||
|
||
ret = fscanf(f, "Button B: X+%d, Y+%d\n", &bx, &by) | ||
assert ret == 2 | ||
ret = fscanf(f, "Prize: X=%d, Y=%d\n", &px, &py) | ||
assert ret == 2 | ||
|
||
m = ClawMachine{a_vector = [ax, ay], b_vector = [bx, by], prize = [px, py]} | ||
solution = m.optimize() | ||
if solution != -1: | ||
total_price += solution | ||
|
||
printf("%d\n", total_price) # Output: 480 | ||
|
||
fclose(f) | ||
return 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
import "stdlib/io.jou" | ||
import "stdlib/math.jou" | ||
import "stdlib/mem.jou" | ||
|
||
|
||
def gcd(a: long, b: long) -> long: | ||
assert a > 0 and b > 0 | ||
|
||
while a > 0 and b > 0: | ||
# Euclidean algorithm: Reduce the bigger number modulo smaller number. | ||
if a > b: | ||
a %= b | ||
else: | ||
b %= a | ||
|
||
# Return whichever number isn't zero. | ||
return llmax(a, b) | ||
|
||
|
||
# Returns the shortest vector in the same direction as the given vector. | ||
def shortest_vector(v: long[2]) -> long[2]: | ||
unwanted_scaling = gcd(llabs(v[0]), llabs(v[1])) | ||
return [v[0] / unwanted_scaling, v[1] / unwanted_scaling] | ||
|
||
|
||
# Determines if two vectors go in the same direction. | ||
# | ||
# This would be easy with determinant, but I don't want to use them because | ||
# calculating determinant might overflow 64-bit long data type. I don't think | ||
# that would happen, but it could be really hard to debug. | ||
def same_direction(v1: long[2], v2: long[2]) -> bool: | ||
s1 = shortest_vector(v1) | ||
s2 = shortest_vector(v2) | ||
return s1[0] == s2[0] and s1[1] == s2[1] | ||
|
||
|
||
# Solves the following linear equations for x and y: | ||
# | ||
# ax + by = c | ||
# dx + ey = f | ||
# | ||
# Assumes that there is a unique solution (may be negative or a fraction). | ||
# According to linear algebra, this is same as saying that (a,d) and (b,e) | ||
# vectors go in different directions. | ||
# | ||
# Return value is [x, y]. If solution is not an integer, returns [-1,-1]. | ||
def solve_linear_unique_2x2_system(a: long, b: long, c: long, d: long, e: long, f: long) -> long[2]: | ||
if a < 0: | ||
a *= -1 | ||
b *= -1 | ||
c *= -1 | ||
if d < 0: | ||
d *= -1 | ||
e *= -1 | ||
f *= -1 | ||
|
||
# Gaussian elimination + Euclidean algorithm | ||
while a != 0 and d != 0: | ||
if a > d: | ||
# Subtract second equation from first n times | ||
n = a/d | ||
a -= n*d | ||
b -= n*e | ||
c -= n*f | ||
else: | ||
# Subtract first equation from second n times | ||
n = d/a | ||
d -= n*a | ||
e -= n*b | ||
f -= n*c | ||
|
||
if a != 0: | ||
memswap(&a, &d, sizeof(a)) | ||
memswap(&b, &e, sizeof(b)) | ||
memswap(&c, &f, sizeof(c)) | ||
|
||
# Due to uniqueness assumption, equations must now look like this: | ||
# | ||
# by = c (b != 0) | ||
# dx + ey = f (d != 0) | ||
assert a == 0 | ||
assert b != 0 | ||
assert d != 0 | ||
|
||
y = c / b | ||
x = (f - e*y) / d | ||
|
||
if a*x + b*y == c and d*x + e*y == f: | ||
# Solution happens to consist of integers | ||
return [x, y] | ||
else: | ||
return [-1L, -1L] | ||
|
||
|
||
def cost(a: long, b: long) -> long: | ||
return 3*a + b | ||
|
||
|
||
# Solves a 1-dimensional variant of the problem. Specifically: | ||
# | ||
# a*(button A presses) + b*(button B presses) = prize | ||
# | ||
# Returns [button A presses, button B presses]. | ||
def optimize_1d(a: long, b: long, prize: long) -> long[2]: | ||
assert a > 0 | ||
assert b > 0 | ||
assert prize > 0 | ||
|
||
if prize % gcd(a, b) != 0: | ||
# The prize is not reachable. Any combination of a and b | ||
# either doesn't reach the prize or jumps beyond the prize. | ||
return [-1L, -1L] | ||
|
||
# Figure out which button press moves the claw the most per token. | ||
# Three B button presses cost as many tokens as one A button press. | ||
# A is better if it moves the claw more for the same amount of tokens. | ||
a_is_better = a > 3*b | ||
|
||
# Start by pressing the better button as much as we can without going | ||
# beyond the prize. Use the other button to move the rest of the way. | ||
if a_is_better: | ||
a_presses = prize / a | ||
b_presses = (prize - a*a_presses) / b | ||
else: | ||
b_presses = prize / b | ||
a_presses = (prize - b*b_presses) / a | ||
|
||
while a_presses >= 0 and b_presses >= 0: | ||
if a*a_presses + b*b_presses == prize: | ||
# Done! Solution found!!! | ||
return [a_presses, b_presses] | ||
|
||
# Decrease the amount of better button presses. This makes the | ||
# solution worse but may be necessary to land exactly at the claw. | ||
# | ||
# This seems to never happen with the inputs given in AoC. | ||
if a_is_better: | ||
a_presses-- | ||
b_presses = (prize - a*a_presses) / b | ||
else: | ||
b_presses-- | ||
a_presses = (prize - b*b_presses) / a | ||
|
||
# No solution | ||
return [-1L, -1L] | ||
|
||
|
||
class ClawMachine: | ||
a_vector: long[2] | ||
b_vector: long[2] | ||
prize: long[2] | ||
|
||
# Returns cheapest button presses to win, or [-1, -1] if cannot win. | ||
def optimize(self) -> long[2]: | ||
if same_direction(self->a_vector, self->b_vector): | ||
# The machine moves along a line. | ||
if not same_direction(self->a_vector, self->prize): | ||
# The prize is not on the line. | ||
return [-1L, -1L] | ||
# Consider only the x coordinates. | ||
return optimize_1d(self->a_vector[0], self->b_vector[0], self->prize[0]) | ||
else: | ||
# According to linear algebra, there is a unique way to reach the | ||
# prize. However, it may involve negative or non-integer amounts of | ||
# button presses :) | ||
solution = solve_linear_unique_2x2_system( | ||
self->a_vector[0], self->b_vector[0], self->prize[0], | ||
self->a_vector[1], self->b_vector[1], self->prize[1], | ||
) | ||
a_presses = solution[0] | ||
b_presses = solution[1] | ||
if a_presses >= 0 and b_presses >= 0: | ||
return [a_presses, b_presses] | ||
else: | ||
return [-1L, -1L] | ||
|
||
|
||
|
||
def main() -> int: | ||
f = fopen("sampleinput.txt", "r") | ||
assert f != NULL | ||
|
||
total_price = 0L | ||
|
||
ax: long | ||
ay: long | ||
bx: long | ||
by: long | ||
px: long | ||
py: long | ||
|
||
while True: | ||
ret = fscanf(f, "Button A: X+%lld, Y+%lld\n", &ax, &ay) | ||
if ret != 2: | ||
# End of file reached | ||
break | ||
|
||
ret = fscanf(f, "Button B: X+%lld, Y+%lld\n", &bx, &by) | ||
assert ret == 2 | ||
ret = fscanf(f, "Prize: X=%lld, Y=%lld\n", &px, &py) | ||
assert ret == 2 | ||
|
||
px += 10000000000000L | ||
py += 10000000000000L | ||
|
||
m = ClawMachine{a_vector = [ax, ay], b_vector = [bx, by], prize = [px, py]} | ||
solution = m.optimize() | ||
if solution[0] >= 0 and solution[1] >= 0: | ||
total_price += cost(solution[0], solution[1]) | ||
|
||
printf("%lld\n", total_price) # Output: 875318608908 | ||
|
||
fclose(f) | ||
return 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
Button A: X+94, Y+34 | ||
Button B: X+22, Y+67 | ||
Prize: X=8400, Y=5400 | ||
|
||
Button A: X+26, Y+66 | ||
Button B: X+67, Y+21 | ||
Prize: X=12748, Y=12176 | ||
|
||
Button A: X+17, Y+86 | ||
Button B: X+84, Y+37 | ||
Prize: X=7870, Y=6450 | ||
|
||
Button A: X+69, Y+23 | ||
Button B: X+27, Y+71 | ||
Prize: X=18641, Y=10279 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import "stdlib/io.jou" | ||
|
||
|
||
enum Quadrant: | ||
TopLeft | ||
TopRight | ||
BottomLeft | ||
BottomRight | ||
SomeKindOfMiddle | ||
|
||
|
||
global width: int | ||
global height: int | ||
|
||
|
||
def determine_quadrant(x: int, y: int) -> Quadrant: | ||
assert 0 <= x and x < width | ||
assert 0 <= y and y < height | ||
assert width % 2 == 1 | ||
assert height % 2 == 1 | ||
|
||
mid_x = (width - 1) / 2 | ||
mid_y = (height - 1) / 2 | ||
|
||
if x < mid_x: | ||
if y < mid_y: | ||
return Quadrant::TopLeft | ||
if y > mid_y: | ||
return Quadrant::BottomLeft | ||
|
||
if x > mid_x: | ||
if y < mid_y: | ||
return Quadrant::TopRight | ||
if y > mid_y: | ||
return Quadrant::BottomRight | ||
|
||
assert x == mid_x or y == mid_y | ||
return Quadrant::SomeKindOfMiddle | ||
|
||
|
||
def main() -> int: | ||
# sampleinput size | ||
width = 11 | ||
height = 7 | ||
|
||
# actual input size | ||
#width = 101 | ||
#height = 103 | ||
|
||
f = fopen("sampleinput.txt", "r") | ||
assert f != NULL | ||
|
||
qcounts: int[5] = [0, 0, 0, 0, 0] | ||
|
||
px: int | ||
py: int | ||
vx: int | ||
vy: int | ||
while fscanf(f, "p=%d,%d v=%d,%d\n", &px, &py, &vx, &vy) == 4: | ||
x = (px + 100*vx) % width | ||
y = (py + 100*vy) % height | ||
qcounts[determine_quadrant(x, y) as int]++ | ||
|
||
fclose(f) | ||
|
||
# Output: 12 | ||
printf("%d\n", qcounts[0] * qcounts[1] * qcounts[2] * qcounts[3]) | ||
|
||
return 0 |
Oops, something went wrong.