Z nazvy i za sutjoju zrozumilo, ge poxode vid Advent Calendar, ctco je takoju soboju korobkoju z 24~25 jactcykiv, kotru vidkryvajec ctcodnja z 1 hrudnja i otrymajec jakyjsj minipodarunok, perevagno tsukerku. Moʼ navitj zustrjitcaly na nepravoslavnyx ihrax, de otrymuvaly mali bonusy.

Tut, na adventofcode.com, natomistj ctcodnja znaxodec dvi vidpovidjy, aby otrymaty vidpovidno dvi zironjky. Tut nemaje obmegenj — mogna budj-jakoju movoju tcy svojym sposobom otrymuvaty vidpovidj. Jakctco otrymaly zironjky ranjice za vsjyx, to otrymujete baly i majete zmohu popasty na spysok pocany. Mogna xiba ctco zaznatcyty, ge z nastupnym dnjom vpravy stajutj skladnjicymy.

Je mistsjova docka percostjy, tam je otcky i zironjky vykonanyx vprav. Jak potrapyty: [Leaderboard] → [Private Leaderboard] → You can join a private leaderboard by entering its join code here, de vvodyte 728806-963332f3.

Vidbuvaje sja ctcoroku z 2015. Jakctco vperce potculy, to ne zasmutcujte sja, mogete vykonuvaty v budj-jakyj denj, xiba ctco ne budutj dodatkovi zhadani baly.

Tog po sutji, to zmahannja na alqoritmy. Je vidpovidna spiljnota na Redytji. Ja podumav, a tcomu ne buty ctcosj podibnoho i tut, Ukrajynsjkoju?

Подякували: koala1

Jak i kazav, percyj denj zazvytcaj duge prostenjkyj.

2021#1, Py3.9
``````count = 0
three = list()

with open("input.txt") as file:
arr = [int(item) for item in file]

for i in range(1, len(arr)):
if arr[i] > arr[i - 1]:
count += 1

print("Part One: ", count)

count = 0

for i in range(0, len(arr) - 2):
three.append(sum(arr[i : i + 3]))

for i in range(1, len(three)):
if three[i] > three[i - 1]:
count += 1

print("Part Two: ", count)``````
Подякували: koala1

aoc.py
``````import requests

DATA_FILE_NAME = '{}.txt'
SESSION = 'PASTE-IT-HERE'

def load_data(number: str, *, verbose: bool = False) -> str:

def log(*args, **kwargs):
if verbose:
print(*args, **kwargs)

filename = DATA_FILE_NAME.format(number)
try:
with open(filename) as file:
except OSError:
url = AOC_URL.format(number)
log(f"Sending request to {url}")
request.raise_for_status()
log(f"Data revieved")
with open(filename, 'w') as file:
log(f"Caching retrieved {len(request.text)} bytes to {filename}")
file.write(request.text)
return request.text``````
2021/1.py
``````import aoc

import os

depths = [int(depth) for depth in data.split()]
return sum(1 for a, b in zip(depths, depths[1:]) if a<b)

depths = [int(depth) for depth in data.split()]
sliding_windows = [sum(depths[i:i+3]) for i in range(len(depths)-2)]
return sum(1 for a, b in zip(sliding_windows, sliding_windows[1:]) if a<b)

if __name__ == '__main__':

Понова: додав параметр verbose для load_data, так красивіше.

Зразок виводу
``````Cached data in 1.txt not found
Data revieved
Caching retrieved 9775 bytes to 1.txt
1390
1457``````
Подякували: 2

Kruto, ctce odyn Pajtonist, mo' bude z tcym porivnjuvaty i navitj povtcyty sja u svojoho.

2021#2, Py3.9.5
``````commands = {
"forward": 0,
"up": 0,
"down": 0,
}

with open("input.txt") as file:
for line in file:
position, number = line.split()
commands[position] += int(number)

print("Part One:", commands["forward"] * (commands["down"] - commands["up"]))

aim = 0
depth = 0
forward = 0

with open("input.txt") as file:
for line in file:
position, number = line.split()
number = int(number)

if position == "forward":
forward += number
depth += aim * number
else:
aim += number if position == "down" else -number

print("Part Two:", forward * depth)``````

Ex, ljinjky meni vvantagyty 3.10, tam je podoba do switch.

2021/2.py. Через одрук довелося тести додавати
``````import aoc

import os
import unittest

NUMBER = os.path.basename(__file__).removesuffix('.py')

SHIFTS = {'forward':(1,0),'up':(0,-1),'down':(0,1)}
x, y = 0, 0
for command in data.splitlines():
command, argument = command.split()
value = int(argument)
dx, dy = SHIFTS[command]
x, y = x+dx*value, y+dy*value
return x*y

SHIFTS = {'forward':(1,1,0),'up':(0,0,-1),'down':(0,0,1)}
x, y, aim = 0, 0, 0
for command in data.splitlines():
command, argument = command.split()
value = int(argument)
dx, dy, da = SHIFTS[command]
x, y, aim = x + dx*value, y + dy*aim*value, aim + da*value
return x*y

class TestDay2(unittest.TestCase):
data = '''forward 5
down 5
forward 8
up 3
down 8
forward 2'''

if __name__ == '__main__':
#unittest.main()

У вашому способі collections.defaultdict міг би зекономити пару рядків.

Якщо хтось іще зацікавиться, наведу завдання українською (без обгортки про підводний човен Санти). Кожного дня 2 завдання зі спільними даними, 2 відкривається, якщо навести правильну відповідь на 1.
День 1. 1. Є ряд чисел, треба знайти, скільки разів відбувається збільшення відносно попереднього.
2. Те саме для суми по ковзному вікну довжиною 3.
День 2. 1. Є набір команд "вперед, вгору, вниз" (для підводного човна, ага). Рух починається з (0,0). Треба знайти добуток кінцевих координат.
2. Інша інтерпретація команд: є третій параметр човна, "ціль". "Вгору" та "вниз" зменшують і збільшують ціль, "вперед X" рухає горизонтально на X і вглиб на X*ціль. Знайти добуток кінцевих координат.

Подякували: dot1

На Rust перше навіть лаконічніше. І зверніть увагу на 1.2 - ми цей момент пропустили, а Rust якось ненав'язливо до нього примусив

2021_1.rs
``````fn task1(depths: &[u32]) -> usize
{
depths.windows(2)
.filter(|&w|w[0]<w[1])
.count()
}

{
depths.windows(4)
.filter(|&w|w[0]<w[3])
.count()

}

fn main() {
let input = aoc::get_input_from_ini_with_year("1","2021").unwrap();
let depths:Vec<_> = input.lines()
.map(|line|line.parse::<u32>().unwrap())
.collect();
}``````
2021_2.rs
``````enum Command {
Forward(i32),
Down(i32),
Up(i32)
}

impl Command {
fn new(line: &str) -> Command
{
let mut parts = line.split_whitespace();
let instruction  = parts.next().unwrap();
let value = parts.next().unwrap().parse().unwrap();
use Command::*;
match instruction {
"forward" => Forward(value),
"down"    => Down(value),
"up"      => Up(value),
_         => panic!("{}", instruction)
}
}
}

{
use Command::*;
let (x, y) = commands.iter()
.fold((0,0),|(x,y), command|match command {
Forward(dx) => (x+dx, y),
Down(dy)    => (x,    y+dy),
Up(dy)      => (x,    y-dy)
});
x*y
}

{
use Command::*;
let (x, y, _) = commands.iter()
.fold((0,0,0),|(x,y,aim), command|match command {
Forward(v) => (x+v, y+aim*v, aim),
Down(da)   => (x,   y,       aim+da),
Up(da)     => (x,   y,       aim-da)
});
x*y
}

fn main() {
let input = aoc::get_input_from_ini_with_year("2","2021").unwrap();
let commands:Vec<_> = input.lines()
.map(|line|Command::new(line))
.  collect();
}``````
Подякували: 2

2021/3.py
``````import aoc

import os
import unittest

NUMBER = os.path.basename(__file__).removesuffix('.py')

numbers = data.splitlines()
counter = [sum(int(n[i]) for n in numbers) for i in range(len(numbers[0]))]
gamma = epsilon = ''
for i, c in enumerate(counter):
if c>len(numbers)/2:
gamma += '1'
epsilon += '0'
else:
gamma += '0'
epsilon += '1'

return int(gamma,2)*int(epsilon,2)

numbers = data.splitlines()
oxygen = numbers[:]
for i in range(len(numbers[0])):
counter = sum(int(n[i]) for n in oxygen)
oxygen = [number for number in oxygen if (number[i]=='1')==(counter>=len(oxygen)/2)]
if len(oxygen)==1:
break
co2 = numbers[:]
for i in range(len(numbers[0])):
counter = sum(int(n[i]) for n in co2)
co2 = [number for number in co2 if (number[i]=='0')!=(counter<len(co2)/2)]
if len(co2)==1:
break

return int(oxygen[0],2)*int(co2[0],2)

class TestDay3(unittest.TestCase):
data = '''00100
11110
10110
10111
10101
01111
00111
11100
10000
11001
00010
01010'''

if __name__ == '__main__':
#unittest.main()

Щось з умовами перемудрив, але працює.

Подякували: dot1

День 3. 1. Є набір двійкових чисел однакової довжини (але можуть починатися з 0). Треба знайти добуток двох характеристик. Перша характеристика має на i-й позиції найчастішу цифру на i-й позиції у числах з набору, друга - менш часту.
2. Ті самі числа, але процедура інша. Викидаємо всі числа, у яких на першій позиції не найчастіша цифра; далі, всі, де на другій позиції не найчастіша у залишку і т.д., поки не залишиться одне число, це і буде перша характеристика. Якщо однаково - лишається 1. Друга характеристика - так само за рідшою цифрою, якщо однаково - 0. Знову ж таки добуток.

2021#3 Py3.9.5
``````with open("input.txt") as file:
diagnostic = [[int(i) for i in line.strip()] for line in file]

γ = list()
ε = list()

for i in zip(*diagnostic):
most = max(i, key=i.count)
γ.append(str(most))
ε.append(str(abs(most - 1)))

print("Part One:", int("".join(ε), 2) * int("".join(γ), 2))

def bit_criteria(variation: str) -> int:
variation = 1 if variation == "oxygen" else 0
rating = diagnostic.copy()

for l in range(len(list(zip(*diagnostic)))):
bit = list()

for i in rating[:]:
bit.append(i[l])

ones = bit.count(1)
zeros = bit.count(0)

if ones == zeros:
number = 1 if variation else 0
elif ones > zeros:
number = 1 if variation else 0
else:
number = 0 if variation else 1

for k in rating[:]:
if k[l] != number:
rating.remove(k)

if len(rating) == 1:
rating = rating[0]
break

return int("".join([str(i) for i in rating]), 2)

print("Part Two:", bit_criteria("oxygen") * bit_criteria("carbon dioxide"))``````

Ne moje, ale pobatcyv take i typu zʼoqrazmyv:

2021#3,1
``````r = *map(str.strip,open(0)),
F = lambda f: int(''.join([f(x, key=x.count) for x in zip(*r)]), 2)
print(F(max) * F(min))``````

Desj v mojomu naprjamku, bo teg tcerez max i zip, ale korotce i krasyvice.

День 4. 1. Гра Бінго. Є послідовність чисел і послідовність полів 5x5. Числа називаються по одному, коли число назване, його викреслюють з усіх полів. Визначити, на якому полі першому виникне рядок чи стовпчик із 5-и викреслених чисел і обчислити добуток невикреслених чисел на останнє назване число на цьому полі.
2. Те саме, тільки останнє поле, на якому буде викреслений стовпчик чи рядок.

2021/4.py
``````import aoc

import os

NUMBER = os.path.basename(__file__).removesuffix('.py')

class Bingo:
def __init__(self, data:str):
data = data.splitlines()
self.numbers = [int(x) for x in data[0].split(',')]
self.boards = []
for line in data[1:]:
if not line.strip():
self.boards.append([])
else:
self.boards[-1].append([int(x) for x in line.split()])
self.strikedout = [[[False for _ in line] for line in board] for board in self.boards]
self.winners = []

def strikeout(self, n: int):
for board_idx, board in enumerate(self.boards):
for line_idx, line in enumerate(board):
if n in line:
self.strikedout[board_idx][line_idx][line.index(n)] = True
self.check_winner(board_idx)

def check_winner(self, idx: int):
if idx in self.winners:
return
board = self.strikedout[idx]
for line in board:
if all(line):
self.winners.append(idx)
return
for i in range(len(board[0])):
if all(line[i] for line in board):
self.winners.append(idx)
return

def score(self, idx: int, n: int) -> int:
board = self.boards[idx]
sboard = self.strikedout[idx]
s = 0
for line, sline in zip(board, sboard):
for field, sfield in zip(line, sline):
if not sfield:
s += field
return s*n

for n in self.numbers:
self.strikeout(n)
if criterion(self):
result = self.score(self.winners[-1], n)
self.strikedout = [[[False for _ in line] for line in board] for board in self.boards]
return result
result = self.score(self.winners[-1], self.numbers[-1])
self.strikedout = [[[False for _ in line] for line in board] for board in self.boards]
return result

if __name__ == '__main__':
Подякували: dot1

2021#4, Py3.9.5
``````from collections import defaultdict
from itertools import chain

lines = [i.strip() for i in open("input.txt")]

boards = defaultdict(list)
b = 0

for i in range(2, len(lines)):
if not lines[i]:
b += 1
continue

boards[b].append(list(map(int, lines[i].split())))

for board in boards:
boards[board] += list(zip(*boards[board]))

numbers = list(map(int, lines[0].split(",")))

scores = list()
marked = numbers[:4]
for number in numbers[4:]:
marked.append(number)
for board in list(boards):
for b in boards[board]:
if set(b).issubset(marked):
scores.append(sum(set(chain(*boards[board])) - set(marked)) * marked[-1])
boards.pop(board)
return scores

koala написав:

День 4. 1. Гра Бінго. Є послідовність чисел і послідовність полів 5x5. Числа називаються по одному, коли число назване, його викреслюють з усіх полів. Визначити, на якому полі першому виникне рядок чи стовпчик із 5-и викреслених чисел і обчислити добуток невикреслених чисел на останнє назване число на цьому полі.
2. Те саме, тільки останнє поле, на якому буде викреслений стовпчик чи рядок.

2021/4.py
``````import aoc

import os

NUMBER = os.path.basename(__file__).removesuffix('.py')

class Bingo:
def __init__(self, data:str):
data = data.splitlines()
self.numbers = [int(x) for x in data[0].split(',')]
self.boards = []
for line in data[1:]:
if not line.strip():
self.boards.append([])
else:
self.boards[-1].append([int(x) for x in line.split()])
self.strikedout = [[[False for _ in line] for line in board] for board in self.boards]
self.winners = []

def strikeout(self, n: int):
for board_idx, board in enumerate(self.boards):
for line_idx, line in enumerate(board):
if n in line:
self.strikedout[board_idx][line_idx][line.index(n)] = True
self.check_winner(board_idx)

def check_winner(self, idx: int):
if idx in self.winners:
return
board = self.strikedout[idx]
for line in board:
if all(line):
self.winners.append(idx)
return
for i in range(len(board[0])):
if all(line[i] for line in board):
self.winners.append(idx)
return

def score(self, idx: int, n: int) -> int:
board = self.boards[idx]
sboard = self.strikedout[idx]
s = 0
for line, sline in zip(board, sboard):
for field, sfield in zip(line, sline):
if not sfield:
s += field
return s*n

for n in self.numbers:
self.strikeout(n)
if criterion(self):
result = self.score(self.winners[-1], n)
self.strikedout = [[[False for _ in line] for line in board] for board in self.boards]
return result
result = self.score(self.winners[-1], self.numbers[-1])
self.strikedout = [[[False for _ in line] for line in board] for board in self.boards]
return result

if __name__ == '__main__':

Oho, tcerez klasy i funtsijy, potugno.

Мені пам'яті не вистачає на третій вкладений цикл. Власної, не комп'ютерної. А тут усе тупо моделюється, як в умові написано, думати не треба.

Подякували: dot1

Зовсім трохи бітової арифметики.

2021_3.rs
``````fn task1(numbers: &[usize], size: usize) -> usize
{
let (gamma, epsilon) = (0..size).rev()
.map(|i|numbers.iter()
.filter(|&n|(n>>i)&1==1)
.count())
.fold((0,0),|(gamma, epsilon), count|
(
(gamma  <<1) | (2*count> numbers.len()) as usize,
(epsilon<<1) | (2*count<=numbers.len()) as usize,
));
gamma*epsilon
}

fn find_by_bit_criteria<F>(numbers: &[usize], size: usize, criteria: F) -> usize
where F: Fn(usize, bool) -> bool
{
let mut numbers = Vec::from(numbers);
for i in (0..size).rev() {
if numbers.len() == 1 {
break;
}
let more_ones = numbers.iter()
.filter(|&n|(n>>i)&1==1)
.count()*2 >= numbers.len();
numbers = numbers.into_iter()
.filter(|&n|criteria((n>>i)&1, more_ones))
.collect();
}
numbers[0]
}

fn task2(numbers: &[usize], size: usize) -> usize
{
let oxygen = find_by_bit_criteria(numbers, size,
|digit, more_ones| (digit == 1) == more_ones);
let co2 = find_by_bit_criteria(numbers, size,
|digit, more_ones| (digit == 0) == more_ones);
oxygen * co2
}

fn main() {
let input = aoc::get_input_from_ini_with_year("3","2021").unwrap();
let mut size = 0;
let numbers:Vec<_> = input.lines()
.collect();
}``````

Rust не відпускає, доки не доведеш запис до ідеалу. Умову я десь годину оптимізовував, додавав і викидав Ordering і т.д. Зате яка краса в результаті.

Подякували: 2

А оце я щось розійшовся. І Rust не допоміг
Працює, звісно, але шукав тупу помилку дуже довго.

2021_4.rs
``````const BINGO_SIZE :usize = 5;

struct Board ([[usize; BINGO_SIZE];BINGO_SIZE] );

impl Board {
fn position(&self, value: usize) -> Option<(usize, usize)> {
for row in 0..BINGO_SIZE {
for col in 0..BINGO_SIZE {
if self.0[row][col] == value {
return Some((row, col));
}
}
}
None
}
}

struct BingoSettings
{
calls: Vec<usize>,
boards: Vec<Board>,
}

impl BingoSettings
{
fn from_str(s: &str) -> BingoSettings {
let mut s = s.lines();
let calls = s.next()
.unwrap()
.split(",")
.map(|s|s.parse().unwrap())
.collect();
let mut boards = Vec::new();
let mut idx = 0;
for line in s {
if line.is_empty() {
boards.push(Board([[0, 0, 0, 0, 0, ],
[0, 0, 0, 0, 0, ],
[0, 0, 0, 0, 0, ],
[0, 0, 0, 0, 0, ],
[0, 0, 0, 0, 0, ],]));
idx = 0;
} else {
let mut board = boards.last_mut().unwrap();
for (i, n) in line.split_whitespace().enumerate() {
board.0[idx][i] = n.parse().unwrap();
}
idx = (idx+1) % 5;
}
}
BingoSettings {
calls,
boards,
}
}
}

type StrikeBoard = [[bool; BINGO_SIZE];BINGO_SIZE];

struct Bingo<'a>
{
settings: &'a BingoSettings,
winners: Vec<(usize, usize)>,
striked: Vec<StrikeBoard>,
}

impl Bingo<'_>
{
fn new(settings: &BingoSettings) -> Bingo
{
Bingo {
settings,
winners: Vec::new(),
striked: std::iter::repeat([[false, false, false, false, false, ],
[false, false, false, false, false, ],
[false, false, false, false, false, ],
[false, false, false, false, false, ],
[false, false, false, false, false, ],])
.take(settings.boards.len())
.collect(),
}
}

fn task(&mut self, nwinner:usize) -> usize {
for &call in &self.settings.calls {
self.strikeout(call);
if self.winners.len() == nwinner {
break;
}
}
self.last_winner_score()
}

fn strikeout(&mut self, call: usize) {
for board_idx in 0..self.striked.len() {
if let Some((row, col)) = self.settings.boards[board_idx].position(call) {
self.striked[board_idx][row][col] = true;
self.check_winner(board_idx, row, col, call);
};
}
}

fn check_winner(&mut self, board_idx:usize, row_idx: usize, col_idx:usize, call: usize)
{
if !self.winners.iter().find(|&&(board, _)|board == board_idx).is_some() {
if     (0..BINGO_SIZE).map(|i|self.striked[board_idx][row_idx][i])
.all(|striked|striked)
|| (0..BINGO_SIZE).map(|i|self.striked[board_idx][i][col_idx])
.all(|striked|striked) {
let mut score = 0;
for row in 0..BINGO_SIZE {
for col in 0..BINGO_SIZE {
if !self.striked[board_idx][row][col] {
score += self.settings.boards[board_idx].0[row][col];
}

}
}
self.winners.push((board_idx, call * score));
}
}
}

fn last_winner_score(&self) -> usize {
self.winners.last().unwrap().1
}
}

{
}

{
}

#[cfg(test)]
mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*;
const DATA: &str = "7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1

22 13 17 11  0
8  2 23  4 24
21  9 14 16  7
6 10  3 18  5
1 12 20 15 19

3 15  0  2 22
9 18 13 17  5
19  8  7 25 23
20 11 10 24  4
14 21 16 12  6

14 21 17 24  4
10 16 15  9 19
18  8 23 26 20
22 11 13  6  5
2  0 12  3  7";

#[test]
let bingo = BingoSettings::from_str(&DATA);
}

#[test]
let bingo = BingoSettings::from_str(&DATA);
}
}

fn main() {
let input = aoc::get_input_from_ini_with_year("4","2021").unwrap();
let bingo = BingoSettings::from_str(&input);
}``````

Спершу хотів погратися з інтервалами, потім вирішив, що 1000х1000 - не надто велике поле.

2021/5.py
``````import aoc

import os

NUMBER = os.path.basename(__file__).removesuffix('.py')

def is_non_diagonal(pts: list[list[int]]) -> bool:
diff = [pts[0][0]-pts[1][0], pts[0][1]-pts[1][1]]
return 0 in diff

def task(data: str, *, filter: bool) -> int:
field = [[0]*1000 for _ in range(1000)]
for line in data.splitlines():
line = [[int(x) for x in part.split(',')] for part in line.split('->')]
if filter and not is_non_diagonal(line):
continue
x,y = line[0]
dx = 1 if line[1][0]>x else -1 if line[1][0]<x else 0
dy = 1 if line[1][1]>y else -1 if line[1][1]<y else 0
while True:
field[x][y] += 1
if [x,y]==line[1]:
break
x += dx
y += dy
return sum(1 for line in field for vent in line if vent>1)

if __name__ == '__main__':

Подякували: dot1

А до біса, Rust так само:

2021_5.rs
``````type Line = ((i32, i32),(i32, i32));

const FIELD_SIZE: usize = 1000;

fn parse_vents(lines: &str) -> Vec<Line> {
lines.lines()
.map(|line|{
let mut line = line.split(" -> ");
let mut pt1 = line.next().unwrap().split(',');
let mut pt2 = line.next().unwrap().split(',');
((pt1.next().unwrap().parse().unwrap(), pt1.next().unwrap().parse().unwrap()),
(pt2.next().unwrap().parse().unwrap(), pt2.next().unwrap().parse().unwrap()))
})
.collect()
}

fn is_diagonal(line: &Line) -> bool {
line.0.0 != line.1.0 && line.0.1 != line.1.1
}

fn ordering_to_i32(order: std::cmp::Ordering) -> i32{
match order {
std::cmp::Ordering::Greater =>  1,
std::cmp::Ordering::Equal   =>  0,
std::cmp::Ordering::Less    => -1,
}
}

fn task(vents: &[Line], is_diagonals_needed: bool) -> i32
{
let mut field: Vec<Vec<i32>> = std::iter::repeat(std::iter::repeat(0).take(FIELD_SIZE).collect()).take(FIELD_SIZE).collect();
for vent in vents {
if is_diagonals_needed || !is_diagonal(vent) {
let (mut x, mut y) = vent.0;
let (dx, dy) = (-ordering_to_i32(x.cmp(&vent.1.0)),-ordering_to_i32(y.cmp(&vent.1.1)));
loop {
field[x as usize][y as usize] += 1;
if (x,y) == vent.1 {
break;
}
x += dx;
y += dy;
}
}
}
field.iter().map(|line|line.iter().filter(|&&x|x>1).count() as i32).sum()
}

{
}

{
}

fn main() {
let input = aoc::get_input_from_ini_with_year("5","2021").unwrap();
let vents = parse_vents(&input);
}``````
Подякували: dot1

2021#5 Py3.9.5
``````import re, itertools

vents = list()

with open("input.txt") as file:
for line in file:
x1, y1, x2, y2 = map(int, re.findall(r"\d+", line))
xo = 1 if x1 <= x2 else -1
yo = 1 if y1 <= y2 else -1

# For Part One
# if x1 == x2 or y1 == y2:

for x, y in itertools.zip_longest(
range(x1, x2 + xo, xo),
range(y1, y2 + yo, yo),
fillvalue=x1 if x1 == x2 else y1 if y1 == y2 else None,
):
vents.append((x, y))

overlaps = {}

for e in vents:
overlaps[e] = overlaps.get(e, 0) + 1

points = 0

for _, o in overlaps.items():
if o > 1:
points += 1

Ja vzahalji obijcov sja bez matritsjy, tupo tcerez spyskiv koordynat, qeq. Ja navitj tcomusj, nespodjivano dlja sebe, zadovolenyj svojym rezultatom — vse vidnosno lehko tcytaljno, bez gaxlyvyx perenasytcenj dugok (jakctco propustyty movu pro efektivnistj).

Модифікував

2021/5a.py
``````import aoc

import os
from collections import defaultdict

NUMBER = os.path.basename(__file__).removesuffix('a.py')

def is_non_diagonal(pts: list[list[int]]) -> bool:
diff = [pts[0][0]-pts[1][0], pts[0][1]-pts[1][1]]
return 0 in diff

def task(data: str, *, filter: bool) -> int:
vents = defaultdict(int)
for line in data.splitlines():
line = [[int(x) for x in part.split(',')] for part in line.split('->')]
if filter and not is_non_diagonal(line):
continue
x,y = line[0]
dx = 1 if line[1][0]>x else -1 if line[1][0]<x else 0
dy = 1 if line[1][1]>y else -1 if line[1][1]<y else 0
while True:
vents[(x,y)]+=1
if [x,y]==line[1]:
break
x += dx
y += dy
return sum(1 for count in vents.values() if count>1)