···77from utils import parse_line, parse_nums, mul, all_unique, factors, memoize, primes, resolve_mapping
88from utils import chunks, parts, gcd, lcm, print_grid, min_max_xy
99from utils import new_table, transposed, rotated, firsts, lasts
1010-from utils import md5, sha256, VOWELS, CONSONANTS, HASH
1010+from utils import md5, sha256, VOWELS, CONSONANTS, HASH, polygon_perimeter, polygon_area
1111from utils import Point, DIRS, DIRS_4, DIRS_8, N, NE, E, SE, S, SW, W, NW
1212# Itertools Functions:
1313# product('ABCD', repeat=2) AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD
+22
2023/utils.py
···435435 return (abs(x) + abs(y) + abs(z)) // 2
436436437437438438+def polygon_perimeter(points):
439439+ """Given a set of bounding box points, returns the perimeter of the polygon."""
440440+ return sum(a.dist_manhattan(b) for a, b in zip(points, points[1:] + [points[0]]))
441441+442442+443443+def polygon_area(points):
444444+ """Given a set of integer bounding box points, returns the total area of the polygon."""
445445+ # Use shoelace formula to compute internal area.
446446+ area = 0
447447+448448+ for a, b in zip(points, points[1:] + [points[0]]):
449449+ area += (b.x + a.x) * (b.y - a.y)
450450+451451+ area = int(abs(area / 2.0))
452452+453453+ # Calculate perimeter.
454454+ perimeter = polygon_perimeter(points)
455455+456456+ # Account for outer perimeter strip in final area computation.
457457+ return area + (perimeter // 2) + 1
458458+459459+438460@total_ordering
439461class Point:
440462 """Simple 2-dimensional point."""