day20: raw (ugly) version

This commit is contained in:
Stefan Harmuth 2020-12-20 10:18:12 +01:00
parent 5accf0f63b
commit 10ffd11c46
5 changed files with 2070 additions and 3 deletions

View File

@ -1,5 +1,7 @@
def printSolution(day, part, solution, test=False): def printSolution(day, part, solution, test=None):
if test: if test:
print("(TEST) ", end="") print("(TEST) ", end="")
print("Solution to day %s, part %s: %s" % (day, part, solution)) print("Solution to day %s, part %s: %s" % (day, part, solution))
if test:
print("Expected: %s (%s)" % (test, "correct" if test == solution else "WRONG"))

223
day20.py Normal file
View File

@ -0,0 +1,223 @@
import aoclib
import math
import re
DAY = 20
TEST_SOLUTION_PART1 = 20899048083289
TEST_SOLUTION_PART2 = 273
class Tile:
def __init__(self, raw_input):
self.image_id = int(raw_input[0][5:-1])
self.image = raw_input[1:]
def getPossibleBorderList(self):
return [
self.image[0],
"".join([x[0] for x in self.image]),
"".join([x[-1] for x in self.image]),
self.image[-1],
"".join(reversed(self.image[0])),
"".join(reversed([x[0] for x in self.image])),
"".join(reversed([x[-1] for x in self.image])),
"".join(reversed(self.image[-1])),
]
def getBorderlessImage(self):
return ["".join(x for x in y[1:-1]) for y in self.image[1:-1]]
def getCurrentBorders(self):
# top right bottom left
return [
self.image[0],
"".join([x[-1] for x in self.image]),
self.image[-1],
"".join([x[0] for x in self.image])
]
def rotateRight(self):
self.image = rotateRight(self.image)
def rotateLeft(self):
self.image = rotateLeft(self.image)
def flipHorizontally(self):
self.image = flipHorizontally(self.image)
def flipVertically(self):
self.image = flipVertically(self.image)
def rotateRight(image):
return ["".join([x[line_no] for x in reversed(image)]) for line_no in range(len(image))]
def rotateLeft(image):
return ["".join([x[line_no] for x in image]) for line_no in reversed(range(len(image)))]
def flipHorizontally(image):
return list(reversed(image))
def flipVertically(image):
return ["".join(x for x in reversed(y)) for y in image]
def getAllPossibleBorders(tile_dict, exception):
all_possible_borders = []
for tile_id, tile in tile_dict.items():
if tile_id != exception:
all_possible_borders.extend(tile.getPossibleBorderList())
return all_possible_borders
def getCornerPieces(tile_dict):
corner_pieces = []
for tile_id, tile in tile_dict.items():
impossible_borders = 0
all_other_borders = getAllPossibleBorders(tile_dict, tile_id)
for border in tile.getPossibleBorderList():
if border not in all_other_borders:
impossible_borders += 1
# we need to find the tiles where 2 borders (and their reverse) don't match any other border
if impossible_borders == 4:
corner_pieces.append(tile_id)
return corner_pieces
def part1(test_mode=False):
my_input = aoclib.getMultiLineInputAsArray(day=DAY, test=test_mode)
tile_dict = {}
for tile_input in my_input:
tile = Tile(tile_input)
tile_dict[tile.image_id] = tile
return math.prod(getCornerPieces(tile_dict))
def part2(test_mode=False):
my_input = aoclib.getMultiLineInputAsArray(day=DAY, test=test_mode)
tile_dict = {}
for tile_input in my_input:
tile = Tile(tile_input)
tile_dict[tile.image_id] = tile
borderlen = int(math.sqrt(len(tile_dict)))
all_pieces = list(tile_dict.keys())
corner_piece = getCornerPieces(tile_dict)[0]
all_pieces.remove(corner_piece)
# first - find correct orientation of corner piece
possible_borders = getAllPossibleBorders(tile_dict, corner_piece)
found = False
for _ in range(2):
for _ in range(2):
for _ in range(4):
if tile_dict[corner_piece].getCurrentBorders()[0] not in possible_borders \
and tile_dict[corner_piece].getCurrentBorders()[3] not in possible_borders:
found = True
break
tile_dict[corner_piece].rotateRight()
if found:
break
else:
tile_dict[corner_piece].flipHorizontally()
if found:
break
else:
tile_dict[corner_piece].flipVertically()
# flip diagonally to match with example from aoc webpage
tile_dict[corner_piece].rotateRight()
tile_dict[corner_piece].flipVertically()
full_image = []
for y in range(borderlen):
full_image.append([])
for x in range(borderlen):
if x == 0 and y == 0:
full_image[y].append(corner_piece)
continue
# find matching piece with correct orientation for the piece to the left (or the top if y > 0 and x == 0)
if x == 0: # check against bottom border of upper piece
check_border = tile_dict[full_image[y - 1][x]].getCurrentBorders()[2]
else:
check_border = tile_dict[full_image[y][x - 1]].getCurrentBorders()[1]
for possible_tile in all_pieces:
if check_border in tile_dict[possible_tile].getPossibleBorderList():
# found my piece ... now orientate it correctly
oriented = False
for _ in range(2):
for _ in range(2):
for _ in range(4):
if (x == 0 and tile_dict[possible_tile].getCurrentBorders()[0] == check_border)\
or (x != 0 and tile_dict[possible_tile].getCurrentBorders()[3] == check_border):
oriented = True
full_image[y].append(possible_tile)
all_pieces.remove(possible_tile)
break
tile_dict[possible_tile].rotateRight()
if oriented:
break
else:
tile_dict[possible_tile].flipVertically()
if oriented:
break
else:
tile_dict[possible_tile].flipHorizontally()
# now that we have the full picture, assemble the borderless full image
borderless_image = []
full_hash_count = 0
for y in full_image:
for image_line in range(8):
borderless_image.append("".join([tile_dict[x].getBorderlessImage()[image_line] for x in y]))
full_hash_count += borderless_image[-1].count('#')
# and finally scan for our sea monster
# seamonster_line_1 = re.compile(r"..................#.")
seamonster_line_2 = re.compile(r"#....##....##....###")
seamonster_line_3 = re.compile(r".#..#..#..#..#..#...")
seamonster_hashcount = 15 # amount of '#' in the sea monster
seamonster_count = 0
for _ in range(2):
for _ in range(2):
for _ in range(4):
for x in range(len(borderless_image) - 2):
if s_2 := re.split(seamonster_line_2, borderless_image[x+1]):
if s_3 := re.split(seamonster_line_3, borderless_image[x+2]):
# yes, this is somewhat optimistic, but it results in the correct answer
if len(s_2) > 1 and len(s_2) == len(s_3):
seamonster_count += len(s_2) - 1
if seamonster_count == 0:
borderless_image = rotateRight(borderless_image)
else:
break
if seamonster_count == 0:
borderless_image = flipHorizontally(borderless_image)
else:
break
if seamonster_count == 0:
borderless_image = flipVertically(borderless_image)
else:
break
print(seamonster_count)
return full_hash_count - seamonster_count * seamonster_hashcount

1727
inputs/20 Normal file

File diff suppressed because it is too large Load Diff

107
inputs/20_test Normal file
View File

@ -0,0 +1,107 @@
Tile 2311:
..##.#..#.
##..#.....
#...##..#.
####.#...#
##.##.###.
##...#.###
.#.#.#..##
..#....#..
###...#.#.
..###..###
Tile 1951:
#.##...##.
#.####...#
.....#..##
#...######
.##.#....#
.###.#####
###.##.##.
.###....#.
..#.#..#.#
#...##.#..
Tile 1171:
####...##.
#..##.#..#
##.#..#.#.
.###.####.
..###.####
.##....##.
.#...####.
#.##.####.
####..#...
.....##...
Tile 1427:
###.##.#..
.#..#.##..
.#.##.#..#
#.#.#.##.#
....#...##
...##..##.
...#.#####
.#.####.#.
..#..###.#
..##.#..#.
Tile 1489:
##.#.#....
..##...#..
.##..##...
..#...#...
#####...#.
#..#.#.#.#
...#.#.#..
##.#...##.
..##.##.##
###.##.#..
Tile 2473:
#....####.
#..#.##...
#.##..#...
######.#.#
.#...#.#.#
.#########
.###.#..#.
########.#
##...##.#.
..###.#.#.
Tile 2971:
..#.#....#
#...###...
#.#.###...
##.##..#..
.#####..##
.#..####.#
#..#.#..#.
..####.###
..#.#.###.
...#.#.#.#
Tile 2729:
...#.#.#.#
####.#....
..#.#.....
....#..#.#
.##..##.#.
.#.####...
####.#.#..
##.####...
##..#.##..
#.##...##.
Tile 3079:
#.#.#####.
.#..######
..#.......
######....
####.#..#.
.#...#.##.
#.#####.##
..#.###...
..#.......
..#.###...

12
main.py
View File

@ -58,7 +58,11 @@ for lib in sorted(imported):
if not flags.part or flags.part == 1: if not flags.part or flags.part == 1:
if not flags.timeit: if not flags.timeit:
aoclib.printSolution(day, 1, globals()[lib].part1(test_mode=flags.test), test=flags.test) solution = globals()[lib].part1(test_mode=flags.test)
if flags.test:
aoclib.printSolution(day, 1, solution, test=globals()[lib].TEST_SOLUTION_PART1)
else:
aoclib.printSolution(day, 1, solution)
else: else:
exec_time = timeit.timeit( exec_time = timeit.timeit(
'globals()[lib].part1(test_mode=flags.test)', 'globals()[lib].part1(test_mode=flags.test)',
@ -69,7 +73,11 @@ for lib in sorted(imported):
if not flags.part or flags.part == 2: if not flags.part or flags.part == 2:
if not flags.timeit: if not flags.timeit:
aoclib.printSolution(day, 2, globals()[lib].part2(test_mode=flags.test), test=flags.test) solution = globals()[lib].part2(test_mode=flags.test)
if flags.test:
aoclib.printSolution(day, 2, solution, test=globals()[lib].TEST_SOLUTION_PART2)
else:
aoclib.printSolution(day, 2, solution)
else: else:
exec_time = timeit.timeit( exec_time = timeit.timeit(
'globals()[lib].part2(test_mode=flags.test)', 'globals()[lib].part2(test_mode=flags.test)',