def test_lattice_midpoint(self):
     self.assertEqual(self.p1.lattice_midpoint(
         self.p2), LatticePoint(1, -2))
     self.assertEqual(self.p2.lattice_midpoint(
         self.p3), LatticePoint(0, -2))
     self.assertEqual(self.p3.lattice_midpoint(
         self.p4), LatticePoint(-2, 3))
     self.assertEqual(self.p4.lattice_midpoint(
         self.p1), LatticePoint(-1, 3))
Exemple #2
0
class InfiniteGrid(ExpandableGrid):
    _adj = [
        LatticePoint(-1, -1),
        LatticePoint(0, -1),
        LatticePoint(1, -1),
        LatticePoint(-1, 0),
        LatticePoint(0, 0),
        LatticePoint(1, 0),
        LatticePoint(-1, 1),
        LatticePoint(0, 1),
        LatticePoint(1, 1)
    ]

    def get(self, pt: LatticePoint, default: str = '.') -> Location:
        if not isinstance(pt, LatticePoint):
            raise TypeError(
                f'Grid accessor must be of type Point, type {type(pt)} provided'
            )
        if pt not in self:
            return Location(pt.x, pt.y, loc_type=Location.OPEN, rep=default)
        return self.grid[pt.y - self.offset.y][pt.x - self.offset.x]

    def get_pixel_details(self, pt: LatticePoint, which: int) -> str:
        rv = ''
        char = '.' if which % 2 == 0 else '#'
        for adj in InfiniteGrid._adj:
            rv += '1' if self.get(pt + adj, char).rep == '#' else '0'
        return int(rv, 2)
Exemple #3
0
    def from_list_of_strings(cls, rows: List[str], wall_char: str = '#',
                             offset: LatticePoint = LatticePoint(0, 0)):
        """Build a grid from a list of strings of equal length"""

        bounds = LatticePoint(len(rows[0]), len(rows))
        grid = cls.blank(bounds, offset)
        for x in range(bounds.x):
            for y in range(bounds.y):
                is_wall = Location.IMPASSABLE if rows[y][x] == wall_char else Location.OPEN
                loc = Location(x+offset.x, y+offset.y, is_wall, rows[y][x])
                grid[LatticePoint(x+offset.x, y+offset.y)] = loc
        return grid
Exemple #4
0
    def blank(cls, bounds: LatticePoint, offset: LatticePoint = LatticePoint(0, 0)):
        """Return a blank grid of the given size "bounds" """

        grid = []
        for y in range(bounds.y):
            row = []
            for x in range(bounds.x):
                row += [Location(x+offset.x, y+offset.y, Location.OPEN, '.')]
            grid = [row] + grid
        return cls(grid, offset=offset)
Exemple #5
0
    def copy(self):
        """This method returns a deep copy of self"""

        grid = []
        for y in range(self.offset.y, self.offset.y+self.height):
            row = []
            for x in range(self.offset.x, self.offset.x+self.width):
                row += [self[LatticePoint(x, y)].copy()]
            grid += [row]

        new_grid = type(self)(grid)
        new_grid.offset = self.offset
        return new_grid
Exemple #6
0
    def char_positions(self, chars: Iterable[str]) -> Dict[str, List[LatticePoint]]:
        """
        Return a list of points for each character passed in the "chars" list
        which represents the list of positions in which that character can be
        found on the grid
        """

        mapping: Dict[str, List[LatticePoint]] = {char: [] for char in chars}
        for x in range(self.offset.x, self.offset.x+self.width):
            for y in range(self.offset.y, self.offset.y+self.height):
                pt = LatticePoint(x, y)
                if self[pt].rep in mapping:
                    mapping[self[pt].rep].append(pt)
        return mapping
Exemple #7
0
    def overlay(self, other: 'Grid', empty_char: str = '.'):
        """Overlay the self grid over top of another grid"""

        if other.bounds[0] not in self or other.bounds[1]-LatticePoint(1, 1) not in self:
            raise ValueError('Other grid not fully within bounds')
        # if self.size != other.size or self.offset != other.offset:
        #     raise ValueError('Other grid not fully within bounds')

        new = self.copy()
        for loc in other:
            loc: Location
            if loc.rep != empty_char:
                new[loc] = loc
        return new
Exemple #8
0
    def conditional_walls(self, predicate_function: Callable[[LatticePoint], bool],
                          char: str) -> 'Grid':
        """
        This method can be used to add walls based on the results of a
        function which takes in a Point and returns a boolean for whether
        there should be a wall at that point
        """

        new = self.copy()
        for y in range(self.offset.y, self.offset.y+new.height):
            for x in range(self.offset.x, self.offset.x+new.width):
                pt = LatticePoint(x, y)
                if predicate_function(pt):
                    new[pt] = Location(x, y, Location.IMPASSABLE, char)
        return new
Exemple #9
0
def basin_size(grid: Grid, point: Location, seen: Set) -> int:
    q = Queue()
    q.put(point)

    size = 0
    while not q.empty():
        pt: Location = q.get()
        if pt in seen or pt.rep == 9:
            continue
        seen.add(pt)
        size += 1

        for adj in pt.get_adjacent_points(lower_bound=LatticePoint(0, 0),
                                          upper_bound=grid.size):
            q.put(grid[adj])
    return size
Exemple #10
0
    def from_list_of_locations(cls, locations: List[Location]):
        """Build a grid of sufficient size for all of the included locations"""

        min_, max_ = LatticePoint(
            10**64, 10**64), LatticePoint(-10**64, -10**64)
        for location in locations:
            if location.x < min_.x:
                min_.x = location.x
            if location.x > max_.x:
                max_.x = location.x
            if location.y < min_.y:
                min_.y = location.y
            if location.y > max_.y:
                max_.y = location.y

        grid = cls.blank(max_-min_+LatticePoint(1, 1), min_)
        for location in locations:
            grid[location] = location
        return grid
 def test_floordiv(self):
     self.assertEqual(self.p1//4, LatticePoint(0, 0))
     self.assertEqual(self.p2//1, LatticePoint(1, -5))
     self.assertEqual(self.p3//2, LatticePoint(0, 1))
     self.assertEqual(self.p4//-1, LatticePoint(3, -5))
Exemple #12
0
 def size(self) -> LatticePoint:
     """This property represents the width and height of the grid"""
     return LatticePoint(self.width, self.height)
 def test_random(self):
     self.assertTrue(LatticePoint.random(lower_bound=self.p1,
                                         upper_bound=LatticePoint(5, 5)
                                         ).in_bounds(self.p1, LatticePoint(6, 6)))
 def test_add(self):
     self.assertEqual(self.p1+self.p1, LatticePoint(2, 2))
     self.assertEqual(self.p2+self.p3, LatticePoint(1, -3))
     self.assertEqual(self.p2+self.p4, LatticePoint(-2, 0))
     self.assertEqual(self.p1+self.p3, LatticePoint(1, 3))
 def setUp(self):
     self.p1 = LatticePoint(1, 1)
     self.p2 = LatticePoint(1, -5)
     self.p3 = LatticePoint(0, 2)
     self.p4 = LatticePoint(-3, 5)
 def test_down(self):
     self.assertEqual(self.p1.down(), LatticePoint(1, 0))
     self.assertEqual(self.p2.down(), LatticePoint(1, -6))
     self.assertEqual(self.p3.down(), LatticePoint(0, 1))
     self.assertEqual(self.p4.down(), LatticePoint(-3, 4))
Exemple #17
0
def risk_level(grid: Grid, point: Location) -> int:
    for adj in point.get_adjacent_points(lower_bound=LatticePoint(0, 0),
                                         upper_bound=grid.size):
        if grid[adj].rep <= point.rep:
            return 0
    return point.rep + 1
 def test_right(self):
     self.assertEqual(self.p1.right(), LatticePoint(2, 1))
     self.assertEqual(self.p2.right(), LatticePoint(2, -5))
     self.assertEqual(self.p3.right(), LatticePoint(1, 2))
     self.assertEqual(self.p4.right(), LatticePoint(-2, 5))
Exemple #19
0
# Written as a solution for Advent of Code 2021

# https://adventofcode.com/2021/day/5

from typing import List

from fishpy.geometry import LatticePoint, LineSegment

with open('2021/05/input.txt') as f:
    lines = f.read().rstrip().split('\n')
    straights: List[LineSegment] = []
    diagonals: List[LineSegment] = []
    for line in lines:
        left = line.split(' -> ')[0].split(',')
        right = line.split(' -> ')[1].split(',')
        left_point = LatticePoint(int(left[0]), int(left[1]))
        right_point = LatticePoint(int(right[0]), int(right[1]))
        segment = LineSegment(left_point, right_point)
        if left_point.x == right_point.x or left_point.y == right_point.y:
            straights.append(segment)
        else:
            diagonals.append(segment)

    grid = [[0 for _ in range(1000)] for _ in range(1000)]
    for segment in straights:
        for pt in segment.lattice_points_along():
            grid[pt.y][pt.x] += 1
    count_1 = sum(1 for row in grid for col in row if col >= 2)

    for segment in diagonals:
        for pt in segment.lattice_points_along():
 def test_sub(self):
     self.assertEqual(self.p1-self.p1, LatticePoint(0, 0))
     self.assertEqual(self.p3-self.p2, LatticePoint(-1, 7))
     self.assertEqual(self.p4-self.p2, LatticePoint(-4, 10))
     self.assertEqual(self.p1-self.p3, LatticePoint(1, -1))
 def setUp(self):
     self.p1 = LatticePoint(2, 4)
 def test_truediv(self):
     self.assertEqual(self.p3/2, LatticePoint(0, 1))
     self.assertEqual(LatticePoint(10, 10)//2, LatticePoint(5, 5))
     self.assertRaises(ValueError, LatticePoint.__truediv__, self.p1, 2)
     self.assertRaises(ValueError, LatticePoint.__truediv__, self.p4, 5)
 def test_mul(self):
     self.assertEqual(self.p1*3, LatticePoint(3, 3))
     self.assertEqual(self.p2*-1, LatticePoint(-1, 5))
     self.assertEqual(self.p3*2, LatticePoint(0, 4))
     self.assertRaises(TypeError, LatticePoint.__mul__, self.p4, 0.5)
 def test_neg(self):
     self.assertEqual(-self.p1, LatticePoint(-1, -1))
     self.assertEqual(-self.p2, LatticePoint(-1, 5))
     self.assertEqual(-self.p3, LatticePoint(0, -2))
     self.assertEqual(-self.p4, LatticePoint(3, -5))
Exemple #25
0
 def __init__(self, grid: List[List[Location]], offset: LatticePoint = LatticePoint(0, 0)):
     self.grid = grid
     self.offset = offset
     self._iter = LatticePoint(0, 0)
 def test_up(self):
     self.assertEqual(self.p1.up(), LatticePoint(1, 2))
     self.assertEqual(self.p2.up(), LatticePoint(1, -4))
     self.assertEqual(self.p3.up(), LatticePoint(0, 3))
     self.assertEqual(self.p4.up(), LatticePoint(-3, 6))
 def test_abs(self):
     self.assertEqual(abs(-self.p1), LatticePoint(1, 1))
     self.assertEqual(abs(self.p2), LatticePoint(1, 5))
     self.assertEqual(abs(-self.p3), LatticePoint(0, 2))
     self.assertEqual(abs(self.p4), LatticePoint(3, 5))
 def test_left(self):
     self.assertEqual(self.p1.left(), LatticePoint(0, 1))
     self.assertEqual(self.p2.left(), LatticePoint(0, -5))
     self.assertEqual(self.p3.left(), LatticePoint(-1, 2))
     self.assertEqual(self.p4.left(), LatticePoint(-4, 5))
Exemple #29
0
from typing import Callable

from fishpy.geometry import LatticePoint, Vector2D
from fishpy.geometry.vector2d import DOWN, RIGHT, UP

direction_map = {
    'up': UP,
    'down': DOWN,
    'forward': RIGHT,
}

with open('2021/02/input.txt') as f:
    directions = f.read().rstrip().split('\n')

position = LatticePoint(0, 0)
get_vector: Callable[[str], Vector2D] = lambda string: direction_map[
    string.split()[0]] * int(string.split()[1])
for entry in map(get_vector, directions):
    position += entry

print(f'Final depth of first half: {position.x * (-position.y)}')

aim = 0
position = LatticePoint(0, 0)
for entry in directions:
    direction, scale = tuple(entry.split())
    if direction == 'forward':
        position += RIGHT * int(scale)
        position += DOWN * aim * int(scale)
    if direction == 'up':
Exemple #30
0
    flashed: Dict[Location, bool] = {pt: False for pt in grid}

    for loc in grid:
        loc: Location
        loc.rep += 1

    while True:
        positions = grid.char_positions([10, 11, 12, 13, 14, 15, 16])
        flattened = [
            grid[position] for value in positions
            for position in positions[value] if not flashed[grid[position]]
        ]
        if not flattened:
            break
        for loc in flattened:
            flashed[loc] = True
            for adj in loc.get_adjacent_points(diagonals=True,
                                               lower_bound=LatticePoint(0, 0),
                                               upper_bound=grid.size):
                grid[adj].rep += 1

    if sum(flashed.values()) == len(flashed):
        # Raise iterations to 230 to get answer to part 2
        print(i)
        break

    for loc in flashed:
        if flashed[loc]:
            flashes += 1
            loc.rep = 0
print(flashes)