advent-of-code/2022/python/matrix.py

340 lines
8.9 KiB
Python
Raw Normal View History

2022-12-12 07:41:14 +00:00
from copy import deepcopy
2022-12-11 05:44:32 +00:00
from collections import defaultdict
2022-12-14 07:38:19 +00:00
import math
2022-12-11 05:44:32 +00:00
from typing import List, Dict, Tuple
2022-12-12 07:41:14 +00:00
split_word_to_chr_list = lambda y: [w for w in y]
2022-12-09 16:43:00 +00:00
split_word_to_int_list = lambda y: [int(w) for w in y]
split_line_to_int_list = lambda y: [int(w) for w in y.split(" ") if w]
2022-12-14 07:38:19 +00:00
def split_x_out(l):
return [x for _,x in l]
def split_y_out(l):
return [y for y,_ in l]
2022-12-09 16:43:00 +00:00
2022-12-12 07:41:14 +00:00
class colors:
# HEADER = '\033[95m'
BLUE = "\033[94m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
RED = "\033[91m"
ENDC = "\033[0m"
2022-12-12 18:38:46 +00:00
BLINK = "\033[5m"
2022-12-12 07:41:14 +00:00
def apply_to_all(mx, func):
for row_num, row in enumerate(mx):
for col_num, val in enumerate(row):
mx[row_num][col_num] = func(val)
2022-12-09 16:43:00 +00:00
def rotate(m, right=True): # -90
2022-12-11 05:44:32 +00:00
"""
Takes a matrix, and rotates all of the values 90 degrees to the left
"""
2022-12-09 16:43:00 +00:00
x = list(zip(*m[::-1]))
if right:
return x
return [list(reversed(y)) for y in x]
def load_matrix_file(name, func=None):
2022-12-11 05:44:32 +00:00
"""
2022-12-12 07:41:14 +00:00
Open a file and split all space separated word lists to integers as a matrix
2022-12-11 05:44:32 +00:00
"""
2022-12-09 16:43:00 +00:00
with open(name, "r") as f:
my_file = []
for line in f:
my_file.append(line.rstrip())
if func:
return [func(x) for x in my_file]
return [split_word_to_int_list(x) for x in my_file]
2022-12-12 07:41:14 +00:00
def find_in_matrix(mx, what, one=True):
coords = []
for row_num, row in enumerate(mx):
for col_num, val in enumerate(row):
if val == what:
coord = (row_num, col_num)
if one is True:
return coord
else:
coords.append(coord)
return coords
2022-12-09 16:43:00 +00:00
def get_neighbors(matrix, x, y, _dict=False):
neighbors = []
# left
try:
if x - 1 >= 0:
if _dict:
2022-12-12 07:41:14 +00:00
neighbors.append({"x": x - 1, "y": y, "value": matrix[y][x - 1]})
2022-12-09 16:43:00 +00:00
else:
neighbors.append([(x - 1, y), matrix[y][x - 1]])
except IndexError:
pass
# right
try:
if _dict:
2022-12-12 07:41:14 +00:00
neighbors.append({"x": x + 1, "y": y, "value": matrix[y][x + 1]})
2022-12-09 16:43:00 +00:00
else:
neighbors.append([(x + 1, y), matrix[y][x + 1]])
except IndexError:
pass
# up
try:
if y - 1 >= 0:
if _dict:
2022-12-12 07:41:14 +00:00
neighbors.append({"x": x, "y": y - 1, "value": matrix[y - 1][x]})
2022-12-09 16:43:00 +00:00
else:
neighbors.append([(x, y - 1), matrix[y - 1][x]])
except IndexError:
pass
# down
try:
if _dict:
2022-12-12 07:41:14 +00:00
neighbors.append({"x": x, "y": y + 1, "value": matrix[y + 1][x]})
2022-12-09 16:43:00 +00:00
else:
neighbors.append([(x, y + 1), matrix[y + 1][x]])
except IndexError:
pass
return neighbors
2022-12-12 07:41:14 +00:00
def valid_neighbors(matrix, x, y, criteria=None):
if criteria is None:
raise Exception("Please pass in a lambda for criteria")
cur = matrix[y][x]
neighbors = get_neighbors(matrix, x, y, _dict=True)
valid = []
for neighbor in neighbors:
2022-12-12 16:44:03 +00:00
if criteria(cur, neighbor["value"]):
2022-12-12 07:41:14 +00:00
valid.append(neighbor)
return valid
2022-12-09 16:43:00 +00:00
def sum_matrix(mtx):
total = 0
for row in mtx:
total += sum(row)
return total
M_UL, M_U, M_UR = (-1, -1), (0, -1), (1, -1)
M_L, M_R = (-1, 0), (1, 0)
M_DL, M_D, M_DR = (-1, 1), (0, 1), (1, 1)
2022-12-12 07:41:14 +00:00
2022-12-09 16:43:00 +00:00
def get_neighbor_coords(matrix, c, r, diagonals=True):
height = len(matrix)
width = len(matrix[0])
if diagonals:
2022-12-12 07:41:14 +00:00
coords = (M_UL, M_U, M_UR, M_L, M_R, M_DL, M_D, M_DR)
2022-12-09 16:43:00 +00:00
else:
2022-12-12 07:41:14 +00:00
coords = (M_U, M_L, M_R, M_D)
2022-12-09 16:43:00 +00:00
neighbors = []
for _c, _r in coords:
try:
value = matrix[r + _r][c + _c] # Try to get a value error
2022-12-12 07:41:14 +00:00
if r + _r >= 0 and c + _c >= 0:
2022-12-09 16:43:00 +00:00
neighbors.append(
[{"c": c + _c, "r": r + _r}, value]
) # woo, no error, this coord is valid
except IndexError:
pass # okay we out of bounds boizzzz
return neighbors
2022-12-12 07:41:14 +00:00
2022-12-15 06:17:19 +00:00
def line_of_sight_coords(matrix, row, col, distance=None) -> Dict[str, List[Tuple[int, int]]]:
2022-12-11 05:44:32 +00:00
"""
2022-12-12 07:41:14 +00:00
Takes a matrix, a row, and a column
calculates the coordinates to the edge for all four cardinal directions
2022-12-11 05:44:32 +00:00
2022-12-12 07:41:14 +00:00
returns a dict with a list of tuple coordes TRAVELING AWAY from the
requested coordinate
2022-12-11 05:44:32 +00:00
"""
height, width = get_size(matrix)
col_ids = list(range(0, height))
row_ids = list(range(0, width))
2022-12-15 06:17:19 +00:00
if distance:
up_ids, down_ids = list(reversed(col_ids[:col])), col_ids[col + 1 :col+distance+1]
left_ids, right_ids = list(reversed(row_ids[:row])), row_ids[row + 1 :row+distance+1]
else:
up_ids, down_ids = list(reversed(col_ids[:col])), col_ids[col + 1 :]
left_ids, right_ids = list(reversed(row_ids[:row])), row_ids[row + 1 :]
2022-12-11 05:44:32 +00:00
2022-12-12 07:41:14 +00:00
left = [(r, col) for r in left_ids]
right = [(r, col) for r in right_ids]
up = [(row, c) for c in up_ids]
down = [(row, c) for c in down_ids]
2022-12-11 05:44:32 +00:00
return {
2022-12-12 07:41:14 +00:00
"U": up,
"L": left,
"D": down,
"R": right,
2022-12-11 05:44:32 +00:00
}
2022-12-12 07:41:14 +00:00
2022-12-15 06:17:19 +00:00
def line_of_sight(mx, row, col, distance=None):
2022-12-11 05:44:32 +00:00
"""
2022-12-12 07:41:14 +00:00
renders a line of sight coord calculation, into the values
2022-12-11 05:44:32 +00:00
"""
2022-12-15 06:17:19 +00:00
coords = line_of_sight_coords(mx, row, col, distance)
2022-12-11 05:44:32 +00:00
los = defaultdict(list)
for k, ids in coords.items():
for _row, _col in ids:
los[k].append(mx[_row][_col])
return los
2022-12-14 07:38:19 +00:00
def coords_between_points(point1, point2):
y1,x1 = point1
y2,x2 = point2
coords = []
x = 0
y = 0
if x2 < x1:
y = point1[0]
for _x in range(x2, x1+1):
coords.append((y,_x))
elif x1 < x2:
y = point1[0]
for _x in range(x1, x2+1):
coords.append((y,_x))
elif y2 < y1:
x = point1[1]
for _y in range(y2, y1+1):
coords.append((_y,x))
elif y1 < y2:
x = point1[1]
for _y in range(y1, y2+1):
coords.append((_y,x))
return coords
2022-12-11 05:44:32 +00:00
def get_size(matrix):
height = len(matrix)
width = len(matrix[0])
return height, width
2022-12-09 16:43:00 +00:00
2022-12-12 07:41:14 +00:00
2022-12-11 05:44:32 +00:00
def row_col_from_int(matrix, x):
2022-12-12 07:41:14 +00:00
h, w = get_size(matrix)
2022-12-11 05:44:32 +00:00
col = x % w
row = x // h
2022-12-12 07:41:14 +00:00
return row, col
2022-12-09 16:43:00 +00:00
def matrix_of_size(width, height, default=0):
return [[default] * width for x in range(height)]
2022-12-12 07:41:14 +00:00
2022-12-09 16:43:00 +00:00
def set_matrix_dict(m):
for x in range(len(m)):
for y in range(len(m[x])):
m[x][y] = {}
return m
def pmx(*matrices, pad=True, space=True):
2022-12-11 05:44:32 +00:00
"""
2022-12-12 07:41:14 +00:00
print a matrix of integers, zero turns to `.` for clarity
2022-12-11 05:44:32 +00:00
"""
2022-12-09 16:43:00 +00:00
if len(matrices) > 1:
matrices = list(zip(*matrices))
for row in matrices:
r = []
for col in row:
r.append("".join([f"{int(x)or '.'}".rjust(3) for x in col]))
print(" ".join(r))
else:
for row in matrices:
for c in row:
if pad:
f = lambda x: f"{int(x)or '.'}".rjust(2)
if space:
f = lambda x: f"{int(x)or '.'}".rjust(3)
else:
f = lambda x: f"{int(x)or '.'}"
if space:
f = lambda x: f"{int(x)or '.'} "
print("".join([f(x) for x in c]))
2022-12-12 07:41:14 +00:00
2022-12-13 05:47:25 +00:00
def ppmx(*matrices, pad=True, space=True, zero="."):
2022-12-11 05:44:32 +00:00
"""
2022-12-12 07:41:14 +00:00
print a matrix of anything, Falsy values turns to `.` for clarity
2022-12-11 05:44:32 +00:00
"""
2022-12-14 07:38:19 +00:00
out = []
2022-12-09 16:43:00 +00:00
if len(matrices) > 1:
matrices = list(zip(*matrices))
for row in matrices:
r = []
for col in row:
2022-12-12 17:09:28 +00:00
r.append("".join([f"{x or zero}".rjust(3) for x in col]))
2022-12-14 07:38:19 +00:00
out.append(" ".join(r))
2022-12-09 16:43:00 +00:00
else:
for row in matrices:
for c in row:
if pad:
2022-12-12 17:09:28 +00:00
f = lambda x: f"{x or zero}".rjust(2)
2022-12-09 16:43:00 +00:00
if space:
2022-12-12 17:09:28 +00:00
f = lambda x: f"{x or zero}".rjust(3)
2022-12-09 16:43:00 +00:00
else:
2022-12-12 17:09:28 +00:00
f = lambda x: f"{x or zero}"
2022-12-09 16:43:00 +00:00
if space:
2022-12-12 17:09:28 +00:00
f = lambda x: f"{x or zero} "
2022-12-14 07:38:19 +00:00
out.append("".join([f(x) for x in c]))
return "\n".join(out)
def view_matrix(matrix, y1,x1, y2,x2):
lines = ppmx(matrix, pad=0,space=0).split("\n")
for line in lines[y1:y2+1]:
print(line[x1:x2])
2022-12-12 07:41:14 +00:00
2022-12-12 18:38:46 +00:00
def highlight(matrix, red=[], green=[], blue=[], blink_green=[]):
2022-12-12 07:41:14 +00:00
"""
print a matrix of anything, Falsy values turns to `.` for clarity
"""
mx = deepcopy(matrix)
for (y, x) in red:
2022-12-13 05:47:25 +00:00
if (y, x) in blue or (y, x) in green or (y, x) in blink_green:
2022-12-12 18:38:46 +00:00
continue
new = f"{colors.RED}{mx[y][x]}{colors.ENDC}"
mx[y][x] = new
2022-12-12 07:41:14 +00:00
for (y, x) in green:
2022-12-13 05:47:25 +00:00
if (y, x) in blue or (y, x) in blink_green:
2022-12-12 18:38:46 +00:00
continue
2022-12-12 07:41:14 +00:00
new = f"{colors.GREEN}{mx[y][x]}{colors.ENDC}"
mx[y][x] = new
for (y, x) in blue:
2022-12-13 05:47:25 +00:00
if (y, x) in blink_green:
2022-12-12 18:38:46 +00:00
continue
2022-12-12 07:41:14 +00:00
new = f"{colors.BLUE}{mx[y][x]}{colors.ENDC}"
mx[y][x] = new
2022-12-12 18:38:46 +00:00
for (y, x) in blink_green:
new = f"{colors.BLINK}{colors.GREEN}{mx[y][x]}{colors.ENDC}"
mx[y][x] = new
2022-12-15 19:33:20 +00:00
print(ppmx(mx, pad=False, space=True, zero="0"))
2022-12-15 06:17:19 +00:00