def follow(field: CoordinateField, pos): '''Finds and yields all positions and their values, following 'pos' downwards. ''' last_pos = add(pos, (0, -1)) # Inital direction is: down while True: # Will be left manually char = field[pos] # Char at curren position yield pos[0], pos[1], char if is_road(char): # Move forward the same direction next_pos = add(pos, direction(last_pos, pos)) # Finish if out of coordinate field or nothing (space) found: if not field.in_field(next_pos) or \ (not is_road(field[next_pos]) and field[next_pos] != '+'): return # Finished else: if char != '+': raise Exception('Unexpected character: %s' % char) # Encountered a '+'. Need to choose new direction: for possible in field.adjectents(pos, diagonals=False): nchar = field[possible] if possible != last_pos and is_road(nchar): next_pos = possible break else: raise Exception('No direction found in which to turn.') last_pos, pos = pos, next_pos
def flipped_horizontal(grid: CoordinateField): '''Returns a copy of given 'grid' that is flipped horizontally.''' copy = grid.empty_copy() for x, y, value in grid.items(): copy[grid.max_x - x, y] = value # x-axis iterated backwards return copy
def test_adjecents_without_diagonals(self): field = CoordinateField() expected = [ (-1, 0), (0, -1), (0, 1), # Own position is not yielded (1, 0) ] for pos_res, pos_expected in zip(field.adjectents((0, 0), False), expected): self.assertEqual(pos_res, pos_expected)
def load_route(puzzle_input) -> CoordinateField: '''Puts the content of 'puzzle_input' into a CordinateField and returns it. ''' field = CoordinateField() highest_x, highest_y = 0, 0 for y, line in enumerate(puzzle_input.split('\n')): highest_y = y for x, char in enumerate(line): highest_x = max(highest_x, x) field[x, y] = char field.set_size(0, highest_x, 0, highest_y) return field
def find_entry_position(field: CoordinateField) -> tuple: '''Determines and returns entry point of the coordinate field.''' for x, y, value in field.items(): if is_road(value) and y == 0: return x, y else: raise Exception('No entry position found!')
def solve_part_2(puzzle_input): field = CoordinateField(0, 127, 0, 127) # 128x128 # Filling coordinate field with 1s and 0s: for y, bits in enumerate(disk_bits(puzzle_input)): for x, bit in enumerate(map(int, bits)): field[x, y] = bit return groups(field)
def count_group(field: CoordinateField, pos: tuple) -> int: '''Finds a group recursivly and returns the amount of found coordinates.''' if field[pos] != 1: # Skip grouped (= 'None') and 0s return 0 del field[pos] # Remove found/grouped numbers found = 1 # Search for adjecent coordinates recursivly: for adjecent in field.adjectents(pos, diagonals=False): found += count_group(field, adjecent) return found
def test_invalid_value_access(self): field = CoordinateField(0, 100, 0, 100) # (Includes borders, too) # Try to get coordinates outside the border: self.assertRaises(KeyError, field.__getitem__, [50, 101]) self.assertRaises(KeyError, field.__getitem__, [-1, 50]) # Requesting wrong types for coordinates for wrong in ('abc', True, b'abc', list(), set(), dict(), 0.1): try: self.assertRaises(TypeError, field.__getitem__, [wrong, 1]) self.assertRaises(TypeError, field.__getitem__, [1, wrong]) self.assertRaises(TypeError, field.__getitem__, [wrong, wrong]) self.assertRaises(TypeError, field.__getitem__, wrong) except AssertionError as error: print('Failed with type: %s' % type(wrong).__name__) raise error
def load_cluster(puzzle_input: str) -> tuple: '''Returns a tuple with the coordinatefield and the start position (as list). ''' cluster = CoordinateField() start_width, start_height = 0, 0 puzzle_input = puzzle_input.replace(' ', '') # To work with the test input for y, line in enumerate(puzzle_input.split('\n')): start_height += 1 if start_width == 0: start_width = len(line) for x, _ in filter((lambda x_v: x_v[1] == '#'), enumerate(line)): cluster[x, y] = True return cluster, [start_width // 2, start_height // 2]
def test_valid_value_access(self): # Setting and getting different values, working of reset() and # default None type field = CoordinateField() # Set and check different values values: for test_value in self.NO_INT_EXAMPLES: field.clear() # Delete all existing values field[0][0] = test_value field[1, 1] = test_value field[[-1, -1]] = test_value self.assertEqual(field[0, 0], test_value) self.assertEqual(field[[1, 1]], test_value) self.assertEqual(field[-1][-1], test_value) field.clear() # Should return None if no value was set: self.assertEqual(field[0, 0], None) # Should return a SecondDimension when requesting only one axis self.assertIsInstance(field[0], SecondDimension)