Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b777d5849 | |||
| 587fee299f | |||
| 105ab06cd9 | |||
| c4fb68f809 | |||
| 17c41a0c63 | |||
| 270866387e | |||
| 6adb1ff457 | |||
| e06e536292 | |||
| dd39c90f13 | |||
| 24cd561370 |
@ -1,5 +1,4 @@
|
|||||||
bs4~=0.0.1
|
bs4~=0.0.1
|
||||||
beautifulsoup4~=4.12.2
|
beautifulsoup4~=4.12
|
||||||
fishhook~=0.2.9
|
requests~=2.32
|
||||||
requests~=2.31.0
|
|
||||||
tqdm~=4.66
|
tqdm~=4.66
|
||||||
@ -32,6 +32,7 @@ class AOCDay:
|
|||||||
self._current_test_file = None
|
self._current_test_file = None
|
||||||
self._current_test_solution = None
|
self._current_test_solution = None
|
||||||
self.DP = {}
|
self.DP = {}
|
||||||
|
self.SEEN = set()
|
||||||
self.__main_progress_bar_id = None
|
self.__main_progress_bar_id = None
|
||||||
self.__main_progress_bar_pos = 0
|
self.__main_progress_bar_pos = 0
|
||||||
self.progress_bars = {}
|
self.progress_bars = {}
|
||||||
@ -68,12 +69,14 @@ class AOCDay:
|
|||||||
|
|
||||||
if not measure_runtime or case_count < len(self.inputs[part]) - 1:
|
if not measure_runtime or case_count < len(self.inputs[part]) - 1:
|
||||||
self.DP = {}
|
self.DP = {}
|
||||||
|
self.seen_reset()
|
||||||
answer = self._call_part_func(self.part_func[part])
|
answer = self._call_part_func(self.part_func[part])
|
||||||
else:
|
else:
|
||||||
stopwatch = StopWatch(auto_start=False)
|
stopwatch = StopWatch(auto_start=False)
|
||||||
self.__main_progress_bar_pos = 1
|
self.__main_progress_bar_pos = 1
|
||||||
for _ in tqdm(range(timeit_number), desc=f"Part {part+1}", leave=False):
|
for _ in tqdm(range(timeit_number), desc=f"Part {part+1}", leave=False):
|
||||||
self.DP = {}
|
self.DP = {}
|
||||||
|
self.seen_reset()
|
||||||
stopwatch.start()
|
stopwatch.start()
|
||||||
answer = self._call_part_func(self.part_func[part])
|
answer = self._call_part_func(self.part_func[part])
|
||||||
stopwatch.stop()
|
stopwatch.stop()
|
||||||
@ -284,6 +287,16 @@ class AOCDay:
|
|||||||
pbar = self.progress_bars[bar_id]
|
pbar = self.progress_bars[bar_id]
|
||||||
pbar.update(add)
|
pbar.update(add)
|
||||||
|
|
||||||
|
def seen_reset(self):
|
||||||
|
self.SEEN = set()
|
||||||
|
|
||||||
|
def seen(self, value: Any, auto_add: bool = True) -> bool:
|
||||||
|
if value in self.SEEN:
|
||||||
|
return True
|
||||||
|
if auto_add:
|
||||||
|
self.SEEN.add(value)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def print_solution(
|
def print_solution(
|
||||||
day: int,
|
day: int,
|
||||||
|
|||||||
79
src/tools/climate.py
Normal file
79
src/tools/climate.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
from math import log10
|
||||||
|
|
||||||
|
ABSOLUTE_ZERO_C = -273.15
|
||||||
|
|
||||||
|
|
||||||
|
def celsius_to_fahrenheit(celsius: float) -> float:
|
||||||
|
return celsius * 9 / 5 + 32
|
||||||
|
|
||||||
|
|
||||||
|
def fahrenheit_to_celsius(fahrenheit: float) -> float:
|
||||||
|
return (fahrenheit - 32) / 9 * 5
|
||||||
|
|
||||||
|
|
||||||
|
def celsius_to_kelvin(celsius: float) -> float:
|
||||||
|
return celsius - ABSOLUTE_ZERO_C
|
||||||
|
|
||||||
|
|
||||||
|
def kelvin_to_celsius(kelvin: float) -> float:
|
||||||
|
return kelvin + ABSOLUTE_ZERO_C
|
||||||
|
|
||||||
|
|
||||||
|
def fahrenheit_to_kelvin(fahrenheit: float) -> float:
|
||||||
|
return celsius_to_kelvin(fahrenheit_to_celsius(fahrenheit))
|
||||||
|
|
||||||
|
|
||||||
|
def kelvin_to_fahrenheit(kelvin: float) -> float:
|
||||||
|
return celsius_to_fahrenheit(kelvin_to_celsius(kelvin))
|
||||||
|
|
||||||
|
|
||||||
|
def saturation_vapor_pressure(temperature: float) -> float:
|
||||||
|
"""
|
||||||
|
Saturation vapor pressure for a given temperature (in °C) in hPa
|
||||||
|
"""
|
||||||
|
a = 7.5 if temperature >= 0 else 7.6
|
||||||
|
b = 237.3 if temperature >= 0 else 240.7
|
||||||
|
return 6.1078 * 10 ** ((a * temperature) / (b + temperature))
|
||||||
|
|
||||||
|
|
||||||
|
def vapor_pressure(relative_humidity: float, temperature: float) -> float:
|
||||||
|
"""
|
||||||
|
Vapor pressure for a given relative humidity (in %) and temperature (in °C) in hPa
|
||||||
|
"""
|
||||||
|
return relative_humidity / 100 * saturation_vapor_pressure(temperature)
|
||||||
|
|
||||||
|
|
||||||
|
def relative_humidity(temperature: float, due_point: float) -> float:
|
||||||
|
"""
|
||||||
|
Relative humidity for a given temperature (in °C) in and due point (in °C)
|
||||||
|
"""
|
||||||
|
return 100 * saturation_vapor_pressure(due_point) / saturation_vapor_pressure(temperature)
|
||||||
|
|
||||||
|
|
||||||
|
def absolute_humidity_from_humidity(relative_humidity: float, temperature: float) -> float:
|
||||||
|
"""
|
||||||
|
Absolute humidity for a given relative humidity (in %) and temperature (in °C) in g/m³
|
||||||
|
"""
|
||||||
|
mw = 18.016
|
||||||
|
rs = 8314.3
|
||||||
|
return 10**5 * mw / rs * vapor_pressure(relative_humidity, temperature) / celsius_to_kelvin(temperature)
|
||||||
|
|
||||||
|
|
||||||
|
def absolute_humidity_from_due_point(due_point: float, temperature: float) -> float:
|
||||||
|
"""
|
||||||
|
Absolute humidity for a given due point (in °C) and temperature (in °C) in g/m³
|
||||||
|
"""
|
||||||
|
mw = 18.016
|
||||||
|
rs = 8314.3
|
||||||
|
return 10**5 * mw / rs * saturation_vapor_pressure(due_point) / celsius_to_kelvin(temperature)
|
||||||
|
|
||||||
|
|
||||||
|
def due_point(relative_humidity: float, temperature: float) -> float:
|
||||||
|
"""
|
||||||
|
Due Point for a given relative humidity (in %) and temperature (in °C) in °C
|
||||||
|
"""
|
||||||
|
a = 7.5 if temperature >= 0 else 7.6
|
||||||
|
b = 237.3 if temperature >= 0 else 240.7
|
||||||
|
v = log10(vapor_pressure(relative_humidity, temperature) / 6.1078)
|
||||||
|
|
||||||
|
return b * v / (a - v)
|
||||||
@ -6,6 +6,21 @@ from typing import Union, List, Iterable
|
|||||||
from .tools import minmax
|
from .tools import minmax
|
||||||
|
|
||||||
|
|
||||||
|
NEIGHBOURS = [(0, -1), (1, 0), (0, 1), (-1, 0)]
|
||||||
|
NEIGHBOURS_3D = [(0, -1, 0), (0, 0, -1), (1, 0, 0), (0, 0, -1), (0, 1, 0), (-1, 0, 0)]
|
||||||
|
DIAGONAL_NEIGHBOURS = [(-1, -1), (1, -1), (-1, 1), (1, 1)]
|
||||||
|
DIAGONAL_NEIGHBOURS_3D = [
|
||||||
|
(-1, -1, -1),
|
||||||
|
(1, -1, -1),
|
||||||
|
(-1, -1, 1),
|
||||||
|
(1, -1, 1),
|
||||||
|
(-1, 1, -1),
|
||||||
|
(-1, 1, 1),
|
||||||
|
(1, 1, -1),
|
||||||
|
(1, 1, 1),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DistanceAlgorithm(Enum):
|
class DistanceAlgorithm(Enum):
|
||||||
MANHATTAN = 0
|
MANHATTAN = 0
|
||||||
EUCLIDEAN = 1
|
EUCLIDEAN = 1
|
||||||
@ -157,36 +172,17 @@ class Coordinate(tuple):
|
|||||||
:return: list of Coordinate
|
:return: list of Coordinate
|
||||||
"""
|
"""
|
||||||
if self[2] is None:
|
if self[2] is None:
|
||||||
|
nb_list = [x * dist for x in NEIGHBOURS]
|
||||||
if includeDiagonal:
|
if includeDiagonal:
|
||||||
nb_list = [
|
nb_list += [x * dist for x in DIAGONAL_NEIGHBOURS]
|
||||||
(-dist, -dist),
|
|
||||||
(-dist, 0),
|
|
||||||
(-dist, dist),
|
|
||||||
(0, -dist),
|
|
||||||
(0, dist),
|
|
||||||
(dist, -dist),
|
|
||||||
(dist, 0),
|
|
||||||
(dist, dist),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
nb_list = [(-dist, 0), (dist, 0), (0, -dist), (0, dist)]
|
|
||||||
|
|
||||||
for dx, dy in nb_list:
|
for dx, dy in nb_list:
|
||||||
if minX <= self[0] + dx <= maxX and minY <= self[1] + dy <= maxY:
|
if minX <= self[0] + dx <= maxX and minY <= self[1] + dy <= maxY:
|
||||||
yield self.__class__(self[0] + dx, self[1] + dy)
|
yield self.__class__(self[0] + dx, self[1] + dy)
|
||||||
else:
|
else:
|
||||||
|
nb_list = [x * dist for x in NEIGHBOURS_3D]
|
||||||
if includeDiagonal:
|
if includeDiagonal:
|
||||||
nb_list = [(x, y, z) for x in [-dist, 0, dist] for y in [-dist, 0, dist] for z in [-dist, 0, dist]]
|
nb_list += [x * dist for x in DIAGONAL_NEIGHBOURS_3D]
|
||||||
nb_list.remove((0, 0, 0))
|
|
||||||
else:
|
|
||||||
nb_list = [
|
|
||||||
(-dist, 0, 0),
|
|
||||||
(0, -dist, 0),
|
|
||||||
(dist, 0, 0),
|
|
||||||
(0, dist, 0),
|
|
||||||
(0, 0, dist),
|
|
||||||
(0, 0, -dist),
|
|
||||||
]
|
|
||||||
|
|
||||||
for dx, dy, dz in nb_list:
|
for dx, dy, dz in nb_list:
|
||||||
if minX <= self[0] + dx <= maxX and minY <= self[1] + dy <= maxY and minZ <= self[2] + dz <= maxZ:
|
if minX <= self[0] + dx <= maxX and minY <= self[1] + dy <= maxY and minZ <= self[2] + dz <= maxZ:
|
||||||
|
|||||||
@ -6,8 +6,8 @@ from typing import Any
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Node:
|
class Node:
|
||||||
value: Any
|
value: Any
|
||||||
next: "Node" = None
|
next: Node = None
|
||||||
prev: "Node" = None
|
prev: Node = None
|
||||||
|
|
||||||
|
|
||||||
class LinkedList:
|
class LinkedList:
|
||||||
|
|||||||
@ -4,6 +4,14 @@ from decimal import Decimal, ROUND_HALF_UP
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
|
|
||||||
|
MAXINT32 = 2**32
|
||||||
|
MAXINT64 = 2**64
|
||||||
|
MAXINT32_SIGNED = 2**31
|
||||||
|
MAXINT64_SIGNED = 2**63
|
||||||
|
MAXINT32_UNSIGNED = MAXINT32
|
||||||
|
MAXINT64_UNSIGNED = MAXINT64
|
||||||
|
|
||||||
|
|
||||||
def round_half_up(number: int | float) -> int:
|
def round_half_up(number: int | float) -> int:
|
||||||
"""pythons round() rounds .5 to the *even* number; 0.5 == 0"""
|
"""pythons round() rounds .5 to the *even* number; 0.5 == 0"""
|
||||||
return int(Decimal(number).to_integral(ROUND_HALF_UP))
|
return int(Decimal(number).to_integral(ROUND_HALF_UP))
|
||||||
@ -26,3 +34,11 @@ def mul(ints: Iterable[int]) -> int:
|
|||||||
ret *= x
|
ret *= x
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def magnitude(value: int | float) -> int:
|
||||||
|
return math.floor(math.log10(value))
|
||||||
|
|
||||||
|
|
||||||
|
def concat(value1: int, value2: int) -> int:
|
||||||
|
return value1 * (10 ** (1 + magnitude(value2))) + value2
|
||||||
|
|||||||
@ -2,8 +2,6 @@ import datetime
|
|||||||
import inspect
|
import inspect
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
from fishhook import hook
|
|
||||||
from functools import wraps
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
@ -140,51 +138,3 @@ def human_readable_time_from_ns(ns: int) -> str:
|
|||||||
time_parts.insert(0, "%d%s" % (p, unit))
|
time_parts.insert(0, "%d%s" % (p, unit))
|
||||||
if ns == 0:
|
if ns == 0:
|
||||||
return ", ".join(time_parts)
|
return ", ".join(time_parts)
|
||||||
|
|
||||||
|
|
||||||
def cache(func):
|
|
||||||
saved = {}
|
|
||||||
|
|
||||||
@wraps(func)
|
|
||||||
def new_func(*args):
|
|
||||||
if args in saved:
|
|
||||||
return saved[args]
|
|
||||||
|
|
||||||
result = func(*args)
|
|
||||||
saved[args] = result
|
|
||||||
return result
|
|
||||||
|
|
||||||
return new_func
|
|
||||||
|
|
||||||
|
|
||||||
@hook(list)
|
|
||||||
def intersection(self, *args) -> list:
|
|
||||||
ret = set(self).intersection(*args)
|
|
||||||
return list(ret)
|
|
||||||
|
|
||||||
|
|
||||||
@hook(list)
|
|
||||||
def __and__(self, *args) -> list:
|
|
||||||
return self.intersection(*args)
|
|
||||||
|
|
||||||
|
|
||||||
@hook(str)
|
|
||||||
def intersection(self, *args) -> str:
|
|
||||||
ret = set(self).intersection(*args)
|
|
||||||
return "".join(list(ret))
|
|
||||||
|
|
||||||
|
|
||||||
@hook(str)
|
|
||||||
def __and__(self, *args) -> str:
|
|
||||||
return self.intersection(*args)
|
|
||||||
|
|
||||||
|
|
||||||
@hook(int)
|
|
||||||
def sum_digits(self) -> int:
|
|
||||||
s = 0
|
|
||||||
num = self
|
|
||||||
while num > 0:
|
|
||||||
s += num % 10
|
|
||||||
num //= 10
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|||||||
90
src/tools/types.py
Normal file
90
src/tools/types.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
from collections.abc import Iterable
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
|
||||||
|
class Integer(int):
|
||||||
|
def digits(self) -> Iterable[int]:
|
||||||
|
for x in str(self):
|
||||||
|
yield int(x)
|
||||||
|
|
||||||
|
def digits_sum(self) -> int:
|
||||||
|
return sum(int(x) for x in str(self))
|
||||||
|
|
||||||
|
|
||||||
|
class String(str):
|
||||||
|
def swap(self, x: int, y: int) -> String:
|
||||||
|
x, y = min(x, y), max(x, y)
|
||||||
|
return String(self[:x] + self[y] + self[x + 1 : y] + self[x] + self[y + 1 :])
|
||||||
|
|
||||||
|
def rotate(self, n: int) -> String:
|
||||||
|
if n == 0:
|
||||||
|
return self
|
||||||
|
while n < 0:
|
||||||
|
n += len(self)
|
||||||
|
while n > len(self):
|
||||||
|
n -= len(self)
|
||||||
|
return String(self[-n:] + self[: len(self) - n])
|
||||||
|
|
||||||
|
def reverse(self, start: int = 0, end: int = None) -> String:
|
||||||
|
if end is None:
|
||||||
|
end = len(self) - 1
|
||||||
|
return String(self[:start] + "".join(reversed(self[start : end + 1])) + self[end + 1 :])
|
||||||
|
|
||||||
|
def __getitem__(self, item) -> String:
|
||||||
|
return String(super().__getitem__(item))
|
||||||
|
|
||||||
|
def __floordiv__(self, other: int) -> list[String]:
|
||||||
|
d = ceil(len(self) / other)
|
||||||
|
return [self[i * d:i * d + d] for i in range(other)]
|
||||||
|
|
||||||
|
|
||||||
|
def __truediv__(self, other: int) -> list[String]:
|
||||||
|
if len(self) % other != 0:
|
||||||
|
raise ValueError("String length is Not a multiple of {}".format(other))
|
||||||
|
|
||||||
|
d = len(self) // other
|
||||||
|
return [self[i * d:i * d + d] for i in range(other)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class List(list):
|
||||||
|
def moving_window(self, size: int = 2) -> Iterable[List]:
|
||||||
|
if len(self) % size != 0:
|
||||||
|
raise ValueError("number of list items must be divisible by size")
|
||||||
|
|
||||||
|
for i in range(len(self) // size):
|
||||||
|
this_window = List()
|
||||||
|
for j in range(size):
|
||||||
|
this_window.append(self[i * size + j])
|
||||||
|
|
||||||
|
yield this_window
|
||||||
|
|
||||||
|
|
||||||
|
class Range:
|
||||||
|
def __init__(self, start: int, end: int) -> None:
|
||||||
|
self.start = start
|
||||||
|
self.end = end
|
||||||
|
|
||||||
|
def overlaps(self, other: Range) -> bool:
|
||||||
|
return other.start <= self.start <= other.end or other.start <= self.end <= other.end or (other.start <= self.start and other.end >= self.end) or (self.start <= other.start and self.end >= other.end)
|
||||||
|
|
||||||
|
def merge(self, other: Range) -> Range:
|
||||||
|
if not self.overlaps(other):
|
||||||
|
raise ValueError("Ranges do not overlap")
|
||||||
|
|
||||||
|
return Range(min(self.start, other.start), max(self.end, other.end))
|
||||||
|
|
||||||
|
def __contains__(self, item: int) -> bool:
|
||||||
|
return self.start <= item <= self.end
|
||||||
|
|
||||||
|
def __len__(self) -> int:
|
||||||
|
return self.end - self.start + 1
|
||||||
|
|
||||||
|
def __iter__(self) -> Iterable[int]:
|
||||||
|
for x in range(self.start, self.end + 1):
|
||||||
|
yield x
|
||||||
|
|
||||||
|
def __reversed__(self) -> Iterable[int]:
|
||||||
|
for x in range(self.end, self.start - 1, -1):
|
||||||
|
yield x
|
||||||
Loading…
Reference in New Issue
Block a user