Solved 2024/14

This commit is contained in:
FrederikBaerentsen 2024-12-14 10:46:57 +01:00
parent 3c31e0e9f3
commit 3fca76a62c
7 changed files with 608 additions and 6 deletions

125
2022/08/8.md Normal file
View File

@ -0,0 +1,125 @@
## \-\-- Day 8: Treetop Tree House \-\--
The expedition comes across a peculiar patch of tall trees all planted
carefully in a grid. The Elves explain that a previous expedition
planted these trees as a reforestation effort. Now, they\'re curious if
this would be a good location for a [tree
house](https://en.wikipedia.org/wiki/Tree_house).
First, determine whether there is enough tree cover here to keep a tree
house *hidden*. To do this, you need to count the number of trees that
are *visible from outside the grid* when looking directly along a row or
column.
The Elves have already launched a
[quadcopter](https://en.wikipedia.org/wiki/Quadcopter)
to generate a map with the height of each tree ([your puzzle
input]{title="The Elves have already launched a quadcopter (your puzzle input)."}).
For example:
30373
25512
65332
33549
35390
Each tree is represented as a single digit whose value is its height,
where `0` is the shortest and `9` is the tallest.
A tree is *visible* if all of the other trees between it and an edge of
the grid are *shorter* than it. Only consider trees in the same row or
column; that is, only look up, down, left, or right from any given tree.
All of the trees around the edge of the grid are *visible* - since they
are already on the edge, there are no trees to block the view. In this
example, that only leaves the *interior nine trees* to consider:
- The top-left `5` is *visible* from the left and top. (It isn\'t
visible from the right or bottom since other trees of height `5` are
in the way.)
- The top-middle `5` is *visible* from the top and right.
- The top-right `1` is not visible from any direction; for it to be
visible, there would need to only be trees of height *0* between it
and an edge.
- The left-middle `5` is *visible*, but only from the right.
- The center `3` is not visible from any direction; for it to be
visible, there would need to be only trees of at most height `2`
between it and an edge.
- The right-middle `3` is *visible* from the right.
- In the bottom row, the middle `5` is *visible*, but the `3` and `4`
are not.
With 16 trees visible on the edge and another 5 visible in the interior,
a total of `21` trees are visible in this arrangement.
Consider your map; *how many trees are visible from outside the grid?*
Your puzzle answer was `1690`.
The first half of this puzzle is complete! It provides one gold star: \*
## \-\-- Part Two \-\-- {#part2}
Content with the amount of tree cover available, the Elves just need to
know the best spot to build their tree house: they would like to be able
to see a lot of *trees*.
To measure the viewing distance from a given tree, look up, down, left,
and right from that tree; stop if you reach an edge or at the first tree
that is the same height or taller than the tree under consideration. (If
a tree is right on the edge, at least one of its viewing distances will
be zero.)
The Elves don\'t care about distant trees taller than those found by the
rules above; the proposed tree house has large
[eaves](https://en.wikipedia.org/wiki/Eaves) to keep it
dry, so they wouldn\'t be able to see higher than the tree house anyway.
In the example above, consider the middle `5` in the second row:
30373
25512
65332
33549
35390
- Looking up, its view is not blocked; it can see `1` tree (of height
`3`).
- Looking left, its view is blocked immediately; it can see only `1`
tree (of height `5`, right next to it).
- Looking right, its view is not blocked; it can see `2` trees.
- Looking down, its view is blocked eventually; it can see `2` trees
(one of height `3`, then the tree of height `5` that blocks its
view).
A tree\'s *scenic score* is found by *multiplying together* its viewing
distance in each of the four directions. For this tree, this is `4`
(found by multiplying `1 * 1 * 2 * 2`).
However, you can do even better: consider the tree of height `5` in the
middle of the fourth row:
30373
25512
65332
33549
35390
- Looking up, its view is blocked at `2` trees (by another tree with a
height of `5`).
- Looking left, its view is not blocked; it can see `2` trees.
- Looking down, its view is also not blocked; it can see `1` tree.
- Looking right, its view is blocked at `2` trees (by a massive tree
of height `9`).
This tree\'s scenic score is `8` (`2 * 2 * 1 * 2`); this is the ideal
spot for the tree house.
Consider each tree on your map. *What is the highest scenic score
possible for any tree?*
Answer:
Although it hasn\'t changed, you can still [get your puzzle
input](8/input).

155
2022/08/solution.py Normal file
View File

@ -0,0 +1,155 @@
#!/bin/python3
import sys,time,re
from pprint import pprint
sys.path.insert(0, '../../')
from fred import list2int,get_re,nprint,lprint,loadFile,nprint,get_value_in_direction,grid_valid,toGrid,addTuples
start_time = time.time()
input_f = 'test'
#########################################
# #
# Part 1 #
# #
#########################################
def part1():
grid = toGrid(input_f)
nprint(grid)
directions = {
'up': (-1, 0),
'down': (1, 0),
'left': (0, -1),
'right': (0, 1),
}
visible = []
for r,row in enumerate(grid):
for c,col in enumerate(row):
if r == 0 or r == len(grid)-1 or c == 0 or c == len(row)-1:
visible.append((r,c))
else:
#print(r,c)
cur = get_value_in_direction(grid,(r,c))
x = []
test = {}
length = 0
notVisible = False
(nr,nc) = (r,c)
#print((r,c),cur)
for d in directions.keys():
#print(d)
if d == 'up':
length = r
if d == 'down':
length = len(grid)-r-1
if d == 'left':
length = c
if d == 'right':
length = len(row)-c-1
new = get_value_in_direction(grid,(nr,nc),d,length,'list')
if int(max(new)) >= int(cur):
test[d] = 'hidden'
else:
test[d] = 'visible'
# while notVisible:
# print((r,c),d,(nr,nc))
# new = get_value_in_direction(grid,(nr,nc),d)
# #print(new,(nr,nc))
# if new is not None:
# if cur > int(new) :
# print(cur,'>',new)
# notVisible = False
# (nr,nc) = addTuples((nr,nc),directions[d])
# if not grid_valid(nr,nc,grid):
# break
# nprint(grid,(r,c),str(cur),positions=visible)
# input()
#if not notVisible:
# visible.append((r,c))
#print((r,c),test)
#nprint(grid,(r,c),cur)
if 'visible' in test.values():
visible.append((r,c))
#print(x)
#input()
nprint(grid,positions=visible)
return len(visible)
start_time = time.time()
#print('Part 1:',part1(), '\t\t', round((time.time() - start_time)*1000), 'ms')
#########################################
# #
# Part 2 #
# #
#########################################
def part2():
grid = toGrid(input_f)
nprint(grid)
directions = {
'up': (-1, 0),
'down': (1, 0),
'left': (0, -1),
'right': (0, 1),
}
visible = []
for r,row in enumerate(grid):
for c,col in enumerate(row):
if r == 0 or r == len(grid)-1 or c == 0 or c == len(row)-1:
visible.append((r,c))
else:
#print(r,c)
cur = get_value_in_direction(grid,(r,c))
x = []
test = {}
length = 0
score = 0
view_distance = 0
notVisible = False
(nr,nc) = (r,c)
print((r,c),cur)
for d in directions.keys():
#print(d)
if d == 'up':
length = r
if d == 'down':
length = len(grid)-r-1
if d == 'left':
length = c
if d == 'right':
length = len(row)-c-1
new = get_value_in_direction(grid,(nr,nc),d,length,'list')
print('->>>',new)
if isinstance(new,list):
for idx,i in enumerate(new):
if int(i) >= int(cur):
print(i,cur,idx)
view_distance = (idx+1)
else:
if int(new) <= int(cur):
print(new,cur,1)
view_distance = +1
print('View distance',view_distance,d,'<---')
#if int(max(new)) >= int(cur):
# test[d] = 'hidden'
#else:
# test[d] = 'visible'
nprint(grid,(r,c),cur)
input()
if 'visible' in test.values():
visible.append((r,c))
nprint(grid,positions=visible)
return len(visible)
start_time = time.time()
print('Part 2:',part2(), '\t\t', round((time.time() - start_time)*1000), 'ms')

View File

@ -2,7 +2,7 @@ import sys
from pprint import pprint
with open(sys.argv[1],'r') as f:
with open('test_input','r') as f:
lines = [[tree for tree in lines.rstrip('\n')] for lines in f]

177
2024/14/14.md Normal file
View File

@ -0,0 +1,177 @@
## \-\-- Day 14: Restroom Redoubt \-\--
One of The Historians needs to use the bathroom; fortunately, you know
there\'s a bathroom near an unvisited location on their list, and so
you\'re all quickly teleported directly to the lobby of Easter Bunny
Headquarters.
Unfortunately, EBHQ seems to have \"improved\" bathroom security *again*
after your last [visit](/2016/day/2). The area outside the bathroom is
swarming with robots!
To get The Historian safely to the bathroom, you\'ll need a way to
predict where the robots will be in the future. Fortunately, they all
seem to be moving on the tile floor in predictable *straight lines*.
You make a list (your puzzle input) of all of the robots\' current
*positions* (`p`) and *velocities* (`v`), one robot per line. For
example:
p=0,4 v=3,-3
p=6,3 v=-1,-3
p=10,3 v=-1,2
p=2,0 v=2,-1
p=0,0 v=1,3
p=3,0 v=-2,-2
p=7,6 v=-1,-3
p=3,0 v=-1,-2
p=9,3 v=2,3
p=7,3 v=-1,2
p=2,4 v=2,-3
p=9,5 v=-3,-3
Each robot\'s position is given as `p=x,y` where `x` represents the
number of tiles the robot is from the left wall and `y` represents the
number of tiles from the top wall (when viewed from above). So, a
position of `p=0,0` means the robot is all the way in the top-left
corner.
Each robot\'s velocity is given as `v=x,y` where `x` and `y` are given
in *tiles per second*. Positive `x` means the robot is moving to the
*right*, and positive `y` means the robot is moving *down*. So, a
velocity of `v=1,-2` means that each second, the robot moves `1` tile to
the right and `2` tiles up.
The robots outside the actual bathroom are in a space which is `101`
tiles wide and `103` tiles tall (when viewed from above). However, in
this example, the robots are in a space which is only `11` tiles wide
and `7` tiles tall.
The robots are good at navigating over/under each other (due to a
combination of springs, extendable legs, and quadcopters), so they can
share the same tile and don\'t interact with each other. Visually, the
number of robots on each tile in this example looks like this:
1.12.......
...........
...........
......11.11
1.1........
.........1.
.......1...
These robots have a unique feature for maximum bathroom security: they
can *teleport*. When a robot would run into an edge of the space
they\'re in, they instead *teleport to the other side*, effectively
wrapping around the edges. Here is what robot `p=2,4 v=2,-3` does for
the first few seconds:
Initial state:
...........
...........
...........
...........
..1........
...........
...........
After 1 second:
...........
....1......
...........
...........
...........
...........
...........
After 2 seconds:
...........
...........
...........
...........
...........
......1....
...........
After 3 seconds:
...........
...........
........1..
...........
...........
...........
...........
After 4 seconds:
...........
...........
...........
...........
...........
...........
..........1
After 5 seconds:
...........
...........
...........
.1.........
...........
...........
...........
The Historian can\'t wait much longer, so you don\'t have to simulate
the robots for very long. Where will the robots be after `100` seconds?
In the above example, the number of robots on each tile after 100
seconds has elapsed looks like this:
......2..1.
...........
1..........
.11........
.....1.....
...12......
.1....1....
To determine the safest area, count the *number of robots in each
quadrant* after 100 seconds. Robots that are exactly in the middle
(horizontally or vertically) don\'t count as being in any quadrant, so
the only relevant robots are:
..... 2..1.
..... .....
1.... .....
..... .....
...12 .....
.1... 1....
In this example, the quadrants contain `1`, `3`, `4`, and `1` robot.
Multiplying these together gives a total *safety factor* of `12`.
Predict the motion of the robots in your list within a space which is
`101` tiles wide and `103` tiles tall. *What will the safety factor be
after exactly 100 seconds have elapsed?*
Your puzzle answer was `230686500`.
The first half of this puzzle is complete! It provides one gold star: \*
## \-\-- Part Two \-\-- {#part2}
During the bathroom break, someone notices that these robots seem
awfully similar to ones built and used at the North Pole. If they\'re
the same type of robots, they should have a hard-coded [Easter
egg]{title="This puzzle was originally going to be about the motion of space rocks in a fictitious arcade game called Meteoroids, but we just had an arcade puzzle."}:
very rarely, most of the robots should arrange themselves into *a
picture of a Christmas tree*.
*What is the fewest number of seconds that must elapse for the robots to
display the Easter egg?*
Answer:
Although it hasn\'t changed, you can still [get your puzzle
input](14/input).

135
2024/14/solution.py Normal file
View File

@ -0,0 +1,135 @@
#!/bin/python3
import sys,time,re
from pprint import pprint
sys.path.insert(0, '../../')
from fred import list2int,get_re,lprint,loadFile,addTuples,grid_valid
start_time = time.time()
# input_f = 'test'
# size_r = 7
# size_c = 11
input_f = 'input'
size_r = 103
size_c = 101
grid = [['.']*size_c]*size_r
def nprint(grid,pos,x):
for r in range(size_r):
for c in range(size_c):
if (c,r) == pos:
print(x,end='')
else:
print(grid[r][c],end='')
print()
#########################################
# #
# Part 1 #
# #
#########################################
def part1():
instructions = loadFile(input_f)
for idx,inst in enumerate(instructions):
match = get_re(r"^p=(-?\d+),(-?\d+) v=(-?\d+),(-?\d+)",inst)
instructions[idx] = [(int(match.group(1)),int(match.group(2))),(int(match.group(3)),int(match.group(4)))]
coordinates = {}
initial = {}
for idx,inst in enumerate(instructions):
#inst = [(2,4),(2,-3)]
#print(inst)
#print('Initial state')
pos = inst[0]
vel = inst[1]
if pos not in initial:
initial[pos] = 0
initial[pos] += 1
#nprint(grid,pos,'1')
length = 100
for i in range (0,length):
pos = addTuples(pos,vel)
#print('After',i+1,'seconds')
if pos[0] < 0:
pos = (pos[0]+size_c,pos[1])
if pos[0] >= size_c:
pos = (pos[0]-size_c,pos[1])
if pos[1] < 0:
pos = (pos[0],pos[1]+size_r)
if pos[1] >= size_r:
pos = (pos[0],pos[1]-size_r)
#print('Position inside grid: ', grid_valid(pos[1],pos[0],grid))
#nprint(grid,pos,'1')
#print(pos)
#input()
if pos not in coordinates:
coordinates[pos] = 0
coordinates[pos] += 1
#print('End State')
#nprint(grid,pos,'1')
#input()
#pprint(coordinates)
# print(instructions)
# print()
# print(initial)
# print()
# for r in range(size_r):
# for c in range(size_c):
# if (c,r) in initial.keys():
# print(initial[(c,r)],end='')
# else:
# print(grid[r][c],end='')
# print()
# print('----------------------')
# print(coordinates)
# for r in range(size_r):
# for c in range(size_c):
# if (c,r) in coordinates.keys():
# print(coordinates[(c,r)],end='')
# else:
# print(grid[r][c],end='')
# print()
center = (int((size_r-1)/2),int((size_c-1)/2))
TL = 0 #top left
BL = 0 #bottom left
TR = 0 #top right
BR = 0 #bottom right
for v in coordinates:
if v[0] < center[1] and v[1] < center[0]:
#print(v,'top left',coordinates[v])
TL += coordinates[v]
if v[0] > center[1] and v[1] < center[0]:
#print(v,'top right',coordinates[v])
TR += coordinates[v]
if v[0] > center[1] and v[1] > center[0]:
#print(v,'bot right',coordinates[v])
BR += coordinates[v]
if v[0] < center[1] and v[1] > center[0]:
#print(v,'bot left',coordinates[v])
BL += coordinates[v]
#print(center)
return TL*TR*BR*BL
start_time = time.time()
print('Part 1:',part1(), '\t\t', round((time.time() - start_time)*1000), 'ms')
#########################################
# #
# Part 2 #
# #
#########################################
def part2():
return
start_time = time.time()
print('Part 2:',part2(), '\t\t', round((time.time() - start_time)*1000), 'ms')

Binary file not shown.

18
fred.py
View File

@ -1,5 +1,6 @@
import sys,re,heapq
from itertools import permutations
from termcolor import colored
def loadFile(input_f):
"""
@ -242,7 +243,7 @@ def getCenter(grid):
return (int(len(grid) / 2), int(len(grid[0]) / 2))
def nprint(grid, cur: set = None, sign: str = None):
def nprint(grid, cur: set = None, sign: str = None, positions:list = None):
"""
Prints a grid, highlighting the current position if specified.
@ -251,7 +252,8 @@ def nprint(grid, cur: set = None, sign: str = None):
cur (set, optional): A set containing the (row, col) indices of the current position.
Defaults to None.
sign (str, optional): The sign to highlight the current position with. Defaults to None.
positions (list, optional): A list of sets containing the (row, col) indices of positions to color.
Defaults to None.
Returns:
None
@ -262,6 +264,8 @@ def nprint(grid, cur: set = None, sign: str = None):
raise TypeError("Cur must be a tuple with (row, column) indices.")
if sign is not None and not isinstance(sign, str):
raise TypeError("Sign must be a string.")
if positions is not None and not isinstance(positions, list):
raise TypeError("Positions must be a list.")
for idx, i in enumerate(grid):
for jdx, j in enumerate(i):
@ -269,7 +273,13 @@ def nprint(grid, cur: set = None, sign: str = None):
if len(sign) > 1:
print(sign[0] + grid[idx][jdx] + sign[1], end='') # Print with sign
else:
print(sign, end=' ') # Print sign
print(colored(sign,'blue'), end=' ') # Print sign
else:
if positions is not None:
if (idx,jdx) in positions:
print(colored(grid[idx][jdx],'red'),end=' ')
else:
print(grid[idx][jdx], end=' ')
else:
print(grid[idx][jdx], end=' ') # Regular grid element
print()
@ -417,7 +427,7 @@ def get_value_in_direction(grid:list, position:set, direction:str=None, length:i
else:
return None
else:
for step in range(length):
for step in range(1,length+1):
new_x, new_y = x + step * dx, y + step * dy
# Check for out-of-bounds
if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[new_x]):