101 Востаннє редагувалося koala (09.12.2022 09:53:32)

Re: Advent of Code

Умова: є мотузка з сегментів на дискретній площині. Коли тягнуть голову мотузки на 1 клітинку, кожен наступний сегмент, що відірвався від попереднього, робить 1 крок по діагоналі, горизонталі чи вертикалі у його бік (пріоритет у діагоналі). Для заданого маршруту знайти кількість точок, відвіданих хвостом, якщо довжина мотузки: 1. 2 сегменти; 2. 10 сегментів.

2022#9, Rust
pub fn parse_input(input: &str) -> Vec<(char, usize)> {
    input
        .lines()
        .map(|l| (l.chars().next().unwrap(), l[2..].parse().unwrap()))
        .collect()
}

#[derive(PartialEq, Eq, Hash, Clone, Copy)]
struct Rope(i32, i32);

impl Rope {
    fn pull(&mut self, dir: char) {
        match dir {
            'L' => self.0 -= 1,
            'R' => self.0 += 1,
            'U' => self.1 += 1,
            'D' => self.1 -= 1,
            _ => unimplemented!(),
        }
    }
    fn step(&mut self, after: Rope) {
        if (self.0 - after.0).abs() > 1 || (self.1 - after.1).abs() > 1 {
            self.0 += num::signum(after.0 - self.0);
            self.1 += num::signum(after.1 - self.1);
        }
    }
}
use std::collections::HashSet;

fn run_rope(path: &[(char, usize)], len: usize) -> usize {
    let mut visited = HashSet::new();
    let mut rope = vec![Rope(0, 0); len];
    for &(dir, num) in path {
        for _ in 0..num {
            rope[0].pull(dir);
            for i in 1..len {
                let after = rope[i - 1];
                rope[i].step(after);
            }
            visited.insert(rope[len - 1]);
        }
    }
    visited.len()
}

pub fn task1(path: &[(char, usize)]) -> usize {
    run_rope(path, 2)
}

pub fn task2(path: &[(char, usize)]) -> usize {
    run_rope(path, 10)
}

Більше б таких задач :)

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

102

Re: Advent of Code

Perśu ćastynu ustyh zrobyty vrancê do vymyku. Druhu dorobyv śćojno.

2022#09
motions = open("input.txt").read().split("\n")
routes = {"R": (1, 0), "L": (-1, 0), "U": (0, 1), "D": (0, -1)}


def touching(knots, i):
    if not knots[i]:
        return (0, 0)

    px, py = knots[i - 1]
    mx, my = knots[i]
    kx, ky = knots[i]

    for x in (mx - 1, mx, mx + 1):
        for y in (my - 1, my, my + 1):
            if x == px and y == py:
                return knots[i]

    if kx == px:
        ky += 1 if py - ky > 0 else -1

    if ky == py:
        kx += 1 if px - kx > 0 else -1

    if kx == mx and ky == my:
        kx += 1 if px - kx > 0 else -1
        ky += 1 if py - ky > 0 else -1

    return (kx, ky)


def simulation(knots: int = 1) -> int:
    knots = [(0, 0)] + [None for _ in range(knots)]
    history = set()

    for motion in motions:
        route, steps = motion.split(" ")

        for _ in range(int(steps)):
            knots[0] = list(map(lambda a, b: a + b, knots[0], routes[route]))

            for i in range(1, len(knots), 1):
                knots[i] = touching(knots, i)

            history.add(knots[-1])

    return len(history)


print(simulation())  # Part One: 6470
print(simulation(9))  # Part Two: 2658

103 Востаннє редагувалося dot (09.12.2022 15:45:59)

Re: Advent of Code

koala написав:

Умова: є мотузка з сегментів на дискретній площині. Коли тягнуть голову мотузки на 1 клітинку, кожен наступний сегмент, що відірвався від попереднього, робить 1 крок по діагоналі, горизонталі чи вертикалі у його бік (пріоритет у діагоналі). Для заданого маршруту знайти кількість точок, відвіданих хвостом, якщо довжина мотузки: 1. 2 сегменти; 2. 10 сегментів.

2022#9, Rust
pub fn parse_input(input: &str) -> Vec<(char, usize)> {
    input
        .lines()
        .map(|l| (l.chars().next().unwrap(), l[2..].parse().unwrap()))
        .collect()
}

#[derive(PartialEq, Eq, Hash, Clone, Copy)]
struct Rope(i32, i32);

impl Rope {
    fn pull(&mut self, dir: char) {
        match dir {
            'L' => self.0 -= 1,
            'R' => self.0 += 1,
            'U' => self.1 += 1,
            'D' => self.1 -= 1,
            _ => unimplemented!(),
        }
    }
    fn step(&mut self, after: Rope) {
        if (self.0 - after.0).abs() > 1 || (self.1 - after.1).abs() > 1 {
            self.0 += num::signum(after.0 - self.0);
            self.1 += num::signum(after.1 - self.1);
        }
    }
}
use std::collections::HashSet;

fn run_rope(path: &[(char, usize)], len: usize) -> usize {
    let mut visited = HashSet::new();
    let mut rope = vec![Rope(0, 0); len];
    for &(dir, num) in path {
        for _ in 0..num {
            rope[0].pull(dir);
            for i in 1..len {
                let after = rope[i - 1];
                rope[i].step(after);
            }
            visited.insert(rope[len - 1]);
        }
    }
    visited.len()
}

pub fn task1(path: &[(char, usize)]) -> usize {
    run_rope(path, 2)
}

pub fn task2(path: &[(char, usize)]) -> usize {
    run_rope(path, 10)
}

Більше б таких задач :)

Ehe, zadaćka pryjmna. Tam mytj, koly Rast rôvnyj abo koroće za Pajton. Baću, moźna dobrjaće pokraśćyty moju funkciju touching, ale poky lênjky. Dorećy, vêtaju zô vzjattjem — sota povêdoma.

104

Re: Advent of Code

Тим часом він одне завдання зафейлив і відстав на кілька днів.

105 Востаннє редагувалося koala (12.12.2022 10:39:49)

Re: Advent of Code

Я повернувся (і до AoC теж).
Умова: є комп'ютер з двома командами (noop і addx) і одним регістром x. noop виконується 1 такт, addx - 2 такти. Такти нумеруються з 1, початкове значення x=1.
1. Знайти суму #такта * x в моменти #такта =20+40n.
2. Є екран 6x40. По рядках пробігає спрайт (позиції 0..39 включно) синхронно з комп'ютером. Якщо під час такту t спрайт знаходиться в межах x-1..x+1 - на екрані у відповідному місці #, якщо ні - . Прочитати літери з екрана.

2022#10, Rust
pub fn parse_input(input: &str) -> Vec<Option<i32>> {
    input
        .lines()
        .map(|line| {
            line.split_whitespace()
                .skip(1)
                .next()
                .map(|v| v.parse().unwrap())
        })
        .collect()
}

struct Computer<'code> {
    code: &'code [Option<i32>],
    x: i32,
    ip: usize,
    wait: bool,
}

impl<'code> Computer<'code> {
    fn new(code: &'code [Option<i32>]) -> Computer<'code> {
        Computer {
            code, //code.iter().copied().collect(),
            x: 1,
            ip: 0,
            wait: false,
        }
    }
    fn clock(&mut self) {
        if self.wait {
            self.wait = false;
            self.x += self.code[self.ip].unwrap();
            self.ip += 1;
        } else {
            match self.code[self.ip] {
                Some(_) => self.wait = true,
                None => self.ip += 1,
            }
        }
    }
}

pub fn task1(input: &[Option<i32>]) -> i32 {
    let mut computer = Computer::new(input);
    (1..240)
        .filter_map(|cycle| {
            let r = if cycle % 40 == 20 {
                Some(cycle * computer.x)
            } else {
                None
            };
            computer.clock();
            r
        })
        .sum()
}

use itertools::Itertools;

pub fn task2(input: &[Option<i32>]) -> String {
    let mut computer = Computer::new(input);
    (0..6)
        .map(|_row| {
            (0..40)
                .map(|pixel| {
                    let lit = (computer.x - pixel).abs() <= 1;
                    computer.clock();
                    if lit {
                        '#'
                    } else {
                        ' '
                    }
                })
                .collect::<String>()
        })
        .join("\n")
}
Подякували: dot1

106

Re: Advent of Code

Умова: мавпи вкрали речі і граються ними. Кожній речі присвоєний рівень хвилювання (за цю річ). У кожної мавпи є: номер, список речей, формула перетворення рівня хвилювання (new=old+n,new=old*n чи new=old*old), формула перевірки (чи ділиться рівень хвилювання на число t) і номери мавп, яким річ перекидається залежно від формули перевірки.
Кожна послідовно взята за номером мавпа бере речі по одній, оглядає (при цьому рівень хвилювання зростає за формулою) і кидає іншій, поки в останньої не закінчаться речі. Знайти добуток двох найбільших кількостей оглядів у мавп.
1. Змоделювати 20 циклів перекидання, рівень хвилювання після застосування формули перетворення ділити націло на 3.
2. Змоделювати 10_000 циклів перекидання, рівень хвилювання не ділити.

2022#11, Rust
#[derive(Clone)]
pub enum Operation {
    Add(usize),
    Mult(usize),
    Square,
}

impl Operation {
    fn apply(&self, item: usize) -> usize {
        match self {
            Operation::Add(x) => item + x,
            Operation::Mult(x) => item * x,
            Operation::Square => item * item,
        }
    }
}

#[derive(Clone)]
pub struct Monkey {
    items: std::collections::VecDeque<usize>,
    operation: Operation,
    test: usize,
    if_true: usize,
    if_false: usize,
    inspected: usize,
}

pub fn parse_input(input: &str) -> Vec<Monkey> {
    let mut monkeys = vec![];
    let mut input = input.lines();
    loop {
        input.next();
        let items = input
            .next()
            .unwrap()
            .split(": ")
            .nth(1)
            .unwrap()
            .split(", ")
            .map(|item| item.parse().unwrap())
            .collect();

        let operator: String;
        let operand: String;
        text_io::scan!(
            input.next().unwrap().bytes() =>
            "  Operation: new = old {} {}",
            operator,
            operand
        );
        let operation = if operator == "+" {
            Operation::Add(operand.parse().unwrap())
        } else if operand == "old" {
            Operation::Square
        } else {
            Operation::Mult(operand.parse().unwrap())
        };

        let test;
        text_io::scan!(input.next().unwrap().bytes() => "  Test: divisible by {}", test);

        let if_true;
        text_io::scan!(input.next().unwrap().bytes() => "    If true: throw to monkey {}", if_true);

        let if_false;
        text_io::scan!(
            input.next().unwrap().bytes() =>
            "    If false: throw to monkey {}",
            if_false
        );

        monkeys.push(Monkey {
            items,
            operation,
            test,
            if_true,
            if_false,
            inspected: 0,
        });

        if input.next().is_none() {
            break;
        }
    }
    monkeys
}

fn simulate(monkeys: &[Monkey], time: usize, div3: bool) -> usize {
    let mut monkeys = monkeys.to_owned();
    let common: usize = monkeys.iter().map(|m| m.test).product();
    for _round in 0..time {
        for i in 0..monkeys.len() {
            while let Some(item) = monkeys[i].items.pop_front() {
                let item = if div3 {
                    monkeys[i].operation.apply(item) / 3
                } else {
                    monkeys[i].operation.apply(item) % common
                };
                monkeys[i].inspected += 1;
                let target = if item % monkeys[i].test == 0 {
                    monkeys[i].if_true
                } else {
                    monkeys[i].if_false
                };
                monkeys[target].items.push_back(item);
            }
        }
    }
    let mut inspected: Vec<_> = monkeys.iter().map(|m| m.inspected).collect();
    inspected.sort();
    inspected[inspected.len() - 1] * inspected[inspected.len() - 2]
}

pub fn task1(monkeys: &[Monkey]) -> usize {
    simulate(monkeys, 20, true)
}

pub fn task2(monkeys: &[Monkey]) -> usize {
    simulate(monkeys, 10000, false)
}
Подякували: dot1

107

Re: Advent of Code

Умова: є прямокутна мапа висот з малих літер від a до z. На ній також позначені початок маршруту (S) і кінець (E). Висота S=a, E=z. Переходити на сусідню клітку можна лише якщо її висота не більша за поточну +1 (тобто з k можна перейти на l чи a, але не на n). Треба знайти:
1. Довжину найкоротшого шляху з S до E.
2. Найкоротшу довжину можливого шляху з будь-якого a до E.

2022#12, Rust
pub fn parse_input(input: &str) -> Vec<Vec<u8>> {
    input.lines().map(|line| line.bytes().collect()).collect()
}

fn value(f: u8) -> u8 {
    if f == b'S' {
        b'a'
    } else if f == b'E' {
        b'z'
    } else {
        f
    }
}

fn can_move(
    map: &[Vec<u8>],
    path: &[Vec<usize>],
    field: (i32, i32),
    neighbor: (i32, i32),
) -> bool {
    if neighbor.0 < 0
        || neighbor.0 >= map.len().try_into().unwrap()
        || neighbor.1 < 0
        || neighbor.1 >= map[0].len().try_into().unwrap()
        || path[neighbor.0 as usize][neighbor.1 as usize] != 0
    {
        false
    } else {
        let field = value(map[field.0 as usize][field.1 as usize]);
        let neighbor = value(map[neighbor.0 as usize][neighbor.1 as usize]);
        neighbor <= field + 1
    }
}

fn shortest_way(start: (i32, i32), map: &[Vec<u8>]) -> Option<usize> {
    let mut path = vec![vec![0; map[0].len()]; map.len()];
    let mut to_check = vec![start];
    for step in 1.. {
        let mut new_check = vec![];
        for field in to_check {
            for shift in &[(1i32, 0i32), (0, 1), (-1, 0), (0, -1)] {
                let neighbor = (field.0 + shift.0, field.1 + shift.1);
                if can_move(&map, &path, field, neighbor) {
                    if map[neighbor.0 as usize][neighbor.1 as usize] == b'E' {
                        return Some(step);
                    }
                    path[neighbor.0 as usize][neighbor.1 as usize] = step;
                    new_check.push(neighbor);
                }
            }
        }
        if new_check.is_empty() {
            return None;
        }
        to_check = new_check;
    }
    unreachable!()
}

pub fn task1(map: &[Vec<u8>]) -> usize {
    let start = map
        .iter()
        .enumerate()
        .filter_map(|(i, line)| {
            line.iter()
                .position(|&symbol| symbol == b'S')
                .map(|j| (i as i32, j as i32))
        })
        .next()
        .unwrap();
    shortest_way(start, map).unwrap()
}

pub fn task2(map: &[Vec<u8>]) -> usize {
    map.iter()
        .enumerate()
        .map(|(i, line)| {
            line.iter()
                .enumerate()
                .filter_map(|(j, &symbol)| {
                    if symbol == b'S' || symbol == b'a' {
                        shortest_way((i as i32, j as i32), map)
                    } else {
                        None
                    }
                })
                .min()
        })
        .min()
        .unwrap()
        .unwrap()
}

108

Re: Advent of Code

Є потік пар "пакетів" - вкладених масивів чисел (на кшталт "[[1],[2,3,4]]"). Пакети порівнюються послідовно таким чином:
- якщо мають різні числа - за величиною чисел;
- якщо число порівнюється з масивом, то число "загортається" в масив перед порівнянням;
- якщо масиви мають різну довжину і всі однакові елементи перед тим - то порівнюються довжини.
1. Знайти суму індексів (від 1) неправильно впорядкованих пар
2. Додати до пакетів ще два ([[2]] і [[6]]), відсортувати усі пакети і знайти добуток індексів (від 1) цих двох доданих пакетів.

2022#13, Rust
use serde_json::Value;

pub fn parse_input(input: &str) -> Vec<Value> {
    input
        .lines()
        .filter_map(|line| {
            if line.is_empty() {
                None
            } else {
                Some(serde_json::from_str(line).unwrap())
            }
        })
        .collect()
}

fn cmp(left: &Value, right: &Value) -> std::cmp::Ordering {
    match (left, right) {
        (Value::Number(left), Value::Number(right)) => {
            left.as_i64().unwrap().cmp(&right.as_i64().unwrap())
        }
        (Value::Number(_), Value::Array(_)) => {
            cmp(&Value::Array(vec![left.clone()]), right)
        }
        (Value::Array(_), Value::Number(_)) => {
            cmp(left, &Value::Array(vec![right.clone()]))
        }
        (Value::Array(left), Value::Array(right)) => {
            if let Some(c) = left
                .iter()
                .zip(right.iter())
                .map(|(l, r)| cmp(l, r))
                .find(|&c| c != std::cmp::Ordering::Equal)
            {
                c
            } else {
                left.len().cmp(&right.len())
            }
        }
        _ => unimplemented!(),
    }
}

pub fn task1(input: &[Value]) -> usize {
    input
        .chunks(2)
        .enumerate()
        .filter_map(|(i, pair)| {
            if cmp(&pair[0], &pair[1]) == std::cmp::Ordering::Less {
                Some(i + 1)
            } else {
                None
            }
        })
        .sum()
}

pub fn task2(input: &[Value]) -> usize {
    let mut packets = input.to_vec();
    let start: Value = serde_json::from_str("[[2]]").unwrap();
    let end: Value = serde_json::from_str("[[6]]").unwrap();
    packets.push(start.clone());
    packets.push(end.clone());
    packets.sort_by(cmp);
    let start = packets.iter().position(|v| v == &start).unwrap() + 1;
    let end = packets.iter().position(|v| v == &end).unwrap() + 1;
    start * end
}

Викинув якийсь старий пакет json, поставив стандартний serde_json. Особливо приємно, що функція порівняння, яку я написав для першого завдання, зайшла і для другого.

109 Востаннє редагувалося koala (14.12.2022 01:07:07)

Re: Advent of Code

Зробив 2019-13 (код комп'ютера див. вище... хоча, здається, його ще довелося потім трохи модифікувати)
Умова: програма для комп'ютера видає "зображення" гри на кшталт арканоїда у вигляді трійок (x,y,t) - координат і типу комірки.
1. Порахувати кількість блоків з кодом 2 - це я в 2019 зробив.
2. Замінивши перший байт пам'яті комп'ютера перед запуском на 2, зіграти в арканоїд. На вхід очікується -1, 0 або 1 - ліво, на місці або право відповідно. Також комп'ютер іноді видає рахунок у вигляді (-1, 0, рахунок). Коли будуть збиті усі блоки, який буде рахунок?

2019-13, Rust
use super::computer::Computer;

pub fn parse_input(input: &str) -> Vec<isize> {
    Computer::prepare_code(input)
}

use std::collections::HashMap;

const EMPTY: isize = 0;
const WALL: isize = 1;
const BLOCK: isize = 2;
const PADDLE: isize = 3;
const BALL: isize = 4;

pub fn task1(code: &[isize]) -> usize {
    let mut computer = Computer::new(code);
    computer.run();
    let mut grid = HashMap::new();
    while let (Some(x), Some(y), Some(t)) =
        (computer.read(), computer.read(), computer.read())
    {
        grid.insert((x, y), t);
    }
    grid.values().filter(|&&v| v == BLOCK).count()
}

pub fn task2(code: &[isize]) -> isize {
    //pre-run to calculate parameters of field
    let mut computer = Computer::new(code);
    computer.run();
    let mut grid = HashMap::new();
    while let (Some(x), Some(y), Some(t)) =
        (computer.read(), computer.read(), computer.read())
    {
        grid.insert((x, y), t);
    }
    let width = grid.keys().map(|&p| p.0).max().unwrap() + 1;
    //let height = grid.keys().map(|&p| p.1).max().unwrap();
    let last_row = code
        .windows(width as usize)
        .enumerate()
        .fold(None, |last, (i, wnd)| {
            if wnd[0] == WALL
                && wnd[1..wnd.len() - 1].iter().all(|&x| x == EMPTY)
                && wnd[wnd.len() - 1] == WALL
            {
                Some(i)
            } else {
                last
            }
        })
        .unwrap();
    let mut blocks: HashMap<_, _> =
        grid.iter().filter(|(_, &t)| t == BLOCK).collect();

    let mut code = code.to_vec();
    code[0] = 2;
    for i in 1..width - 1 {
        code[last_row + i as usize] = WALL;
    }
    let mut computer = Computer::new(&code);
    let mut score = 0;
    loop {
        computer.run();
        while let (Some(x), Some(y), Some(t)) =
            (computer.read(), computer.read(), computer.read())
        {
            if x >= 0 {
                if t != BLOCK {
                    blocks.remove(&(x, y));
                }
            } else {
                score = t;
            }
        }
        computer.write(0);
        if blocks.is_empty() {
            break;
        }
    }
    score
}
Нотатки

Спершу спробував додати ncurses і реально "пограти", але платформа шириною 1 клітинку, а блоків у мене 341. Тому я "хакнув" програму, домалювавши знизу стінку. Гадаю, правильний варіант - рухати платформу за м'ячем, але хай уже колись іншим разом :)

110

Re: Advent of Code

Умова: є мапа блоків, на які зверху (з однієї точки) сиплеться пісок. Кожна піщинка намагається спершу "впасти" униз, потім униз-ліворуч, потім униз-праворуч.
1. Скільки піщинок впаде, доки вони досягнуть дна?
2. Скільки піщинок впаде, доки взагалі можна буде додавати піщинки?

2022#14, Rust
pub fn parse_input(input: &str) -> Vec<Vec<u8>> {
    let input = input
        .lines()
        .map(|line| {
            line.split(" -> ")
                .map(|pair| {
                    let (x, y);
                    text_io::scan!(pair.bytes()=>"{},{}", x, y);
                    (x, y)
                })
                .collect::<Vec<_>>()
        })
        .collect::<Vec<_>>();
    let (max_x, max_y) = input.iter().flat_map(|line| line.iter()).fold(
        (i32::MIN, i32::MIN),
        |(max_x, max_y), &(x, y)| {
            (std::cmp::max(max_x, x), std::cmp::max(max_y, y))
        },
    );
    let width = (2 * max_x) as usize;
    let height = (max_y + 1) as usize;
    let mut map = vec![vec![b' '; width]; height];
    for line in input {
        for pair in line.windows(2) {
            if pair[0].0 == pair[1].0 {
                let range = if pair[0].1 <= pair[1].1 {
                    pair[0].1..=pair[1].1
                } else {
                    pair[1].1..=pair[0].1
                };
                for y in range {
                    map[y as usize][pair[0].0 as usize] = b'#';
                }
            } else {
                let range = if pair[0].0 <= pair[1].0 {
                    pair[0].0..=pair[1].0
                } else {
                    pair[1].0..=pair[0].0
                };
                for x in range {
                    map[pair[0].1 as usize][x as usize] = b'#';
                }
            }
        }
    }
    map
}

pub fn task1(map: &[Vec<u8>]) -> usize {
    let mut map = map.to_vec();
    for sandgrain in 0.. {
        assert_eq!(map[0][500], b' ');
        let (mut x, mut y) = (500, 0);
        loop {
            if y == map.len() - 1 {
                return sandgrain;
            } else if map[y + 1][x] == b' ' {
            } else if map[y + 1][x - 1] == b' ' {
                x -= 1;
            } else if map[y + 1][x + 1] == b' ' {
                x += 1;
            } else {
                map[y][x] = b'o';
                break;
            }
            y += 1;
        }
    }
    unreachable!()
}

pub fn task2(map: &[Vec<u8>]) -> usize {
    let mut map = map.to_vec();
    map.push(vec![b' '; map[0].len()]);
    for sandgrain in 0.. {
        if map[0][500] != b' ' {
            return sandgrain;
        }
        let (mut x, mut y) = (500, 0);
        loop {
            if y == map.len() - 1 {
                map[y][x] = b'o';
                break;
            } else if map[y + 1][x] == b' ' {
            } else if map[y + 1][x - 1] == b' ' {
                x -= 1;
            } else if map[y + 1][x + 1] == b' ' {
                x += 1;
            } else {
                map[y][x] = b'o';
                break;
            }
            y += 1;
        }
    }
    unreachable!()
}

111 Востаннє редагувалося koala (15.12.2022 23:19:19)

Re: Advent of Code

За цей код мені дещо соромно, але в релізній збірці на моєму Ryzen 5 1600 вкладається в 4 секунди, а це головне, правильно? Я абсолютно певен, що цей код можна дуже сильно оптимізувати

Умова: є набір сенсорів і маячків на цілочисельній площині. Сенсори показують координати свої і найближчого до себе (за манхеттенською відстанню) маячка.
1. На прямій y=2_000_000 знайти кількість клітин, де не може бути маячка (тобто вони ближче до якогось сенсора, ніж його маячок).
2. У квадраті (x=0..4_000_000, y=0..4_000_000) знайти єдину точку, про яку сенсори нічого не кажуть.

2022#15, Rust
pub fn parse_input(input: &str) -> Vec<((i32, i32), (i32, i32))> {
    input.lines()
         .map(|line|{let (x,y,bx,by);
            text_io::scan!(line.bytes()=>"Sensor at x={}, y={}: closest beacon is at x={}, y={}",x,y,bx,by);
            ((x,y),(bx,by))
     }).collect()
}

fn generate_row(
    sensors: &[((i32, i32), (i32, i32))],
    row_y: i32,
) -> Vec<(i32, i32)> {
    let mut list: Vec<_> = sensors
        .iter()
        .filter_map(|((x, y), (bx, by))| {
            let dist = (bx - x).abs() + (by - y).abs();
            let dy = dist - (row_y - y).abs();
            if dy >= 0 {
                Some((x - dy, x + dy))
            } else {
                None
            }
        })
        .collect();
    list.sort_by_key(|&(left, _right)| left);
    let mut i = 0;
    while i < list.len() - 1 {
        if list[i + 1].0 <= list[i].1 + 1 {
            list[i].1 = std::cmp::max(list[i].1, list[i + 1].1);
            list.remove(i + 1);
        } else {
            i += 1;
        }
    }
    list
}

pub fn task1(sensors: &[((i32, i32), (i32, i32))]) -> i32 {
    const ROW_Y: i32 = 2_000_000;
    let list = generate_row(sensors, ROW_Y);

    let mut beacons: Vec<_> = sensors
        .iter()
        .filter_map(|&(_, (bx, by))| if by == ROW_Y { Some(bx) } else { None })
        .collect();
    beacons.sort();
    beacons.dedup();

    list.iter()
        .map(|(left, right)| right - left + 1)
        .sum::<i32>()
        - beacons.len() as i32
}

pub fn task2(sensors: &[((i32, i32), (i32, i32))]) -> usize {
    const SIZE: i32 = 4_000_000;
    for row_y in 0..=SIZE {
        let mut list = generate_row(sensors, row_y);
        loop {
            let last = list.len() - 1;
            if list[last].0 > SIZE {
                list.remove(last);
            } else if list[last].0 > SIZE {
                list[last].0 = SIZE;
                break;
            } else {
                break;
            }
        }
        loop {
            if list[0].1 < 0 {
                list.remove(0);
            } else if list[0].0 < 0 {
                list[0].0 = 0;
                break;
            } else {
                break;
            }
        }
        if list.len() == 1 {
            if list[0].0 > 0 {
                return row_y as usize;
            } else if list[0].1 < SIZE {
                return SIZE as usize * SIZE as usize + row_y as usize;
            }
        } else {
            return (list[0].1 as usize + 1) * SIZE as usize + row_y as usize;
        }
    }
    unreachable!();
}

112

Re: Advent of Code

Так, почалося.
#16 - які будуть ідеї? Познаходити довжини шляхів між ненульовими кранами і далі перебором лише серед них?

113

Re: Advent of Code

Тетрис!

114

Re: Advent of Code

Схоже, на цьому AoC для мене завершується...

2022#17.1, Rust
pub fn parse_input(input: &str) -> &str {
    input.trim()
}

const TUBE_WIDTH: usize = 7;

#[rustfmt::skip]
const FIGURES: &[&[&[u8]]] = &[
    &["####".as_bytes()],

    &[".#.".as_bytes(), 
      "###".as_bytes(), 
      ".#.".as_bytes()],

    &["###".as_bytes(),
      "..#".as_bytes(), 
      "..#".as_bytes()],

    &["#".as_bytes(), 
      "#".as_bytes(), 
      "#".as_bytes(), 
      "#".as_bytes()],

    &["##".as_bytes(), 
      "##".as_bytes()],
];

const EMPTY_LINE: &[u8] = ".......".as_bytes();

struct Tetris {
    tube: Vec<Vec<u8>>,
    figure: &'static [&'static [u8]],
    x: i32,
    y: i32,
    count: usize,
}

impl Tetris {
    fn new() -> Tetris {
        Tetris {
            tube: vec![EMPTY_LINE.to_vec(); 4],
            figure: &FIGURES[0],
            x: 2,
            y: 3,
            count: 0,
        }
    }
    fn add_figure(&mut self) {
        self.count += 1;
        self.figure = &FIGURES[self.count % 5];
        self.x = 2;
        let free_lines = self.tube.len() - self.tower_height();
        if free_lines < 3 + self.figure.len() {
            self.tube.resize(
                self.tube.len() + 3 + self.figure.len() - free_lines,
                EMPTY_LINE.to_vec(),
            );
        }
        self.y = (self.tower_height() + 3).try_into().unwrap();
    }
    fn move_figure(&mut self, dir: u8) {
        let x = self.x
            + match dir {
                b'>' => 1,
                b'<' => -1,
                _ => unimplemented!("Invalid move {}", dir),
            };
        if (0..=(TUBE_WIDTH - self.figure[0].len()) as i32).contains(&x)
            && self.fits(x as usize, self.y as usize)
        {
            self.x = x;
        }
        if self.y > 0 && self.fits(self.x as usize, (self.y - 1) as usize) {
            self.y -= 1;
        } else {
            self.fix_figure();
            self.add_figure();
        }
    }
    fn fits(&self, x: usize, y: usize) -> bool {
        self.figure.iter().enumerate().all(|(fy, fline)| {
            fline.iter().enumerate().all(|(fx, &fchar)| {
                fchar != b'#' || self.tube[y + fy][x + fx] != b'#'
            })
        })
    }
    fn fix_figure(&mut self) {
        for f_y in 0..self.figure.len() {
            for f_x in 0..self.figure[f_y].len() {
                if self.figure[f_y][f_x] == b'#' {
                    self.tube[self.y as usize + f_y][self.x as usize + f_x] =
                        b'#';
                }
            }
        }
    }
    fn tower_height(&self) -> usize {
        self.tube.len()
            - self
                .tube
                .iter()
                .rev()
                .take_while(|row| !row.contains(&b'#'))
                .count()
    }
    fn play(&mut self, moves: &str, limit: usize) -> usize {
        for &mov in moves.as_bytes().iter().cycle() {
            self.move_figure(mov);
            if self.count >= limit {
                break;
            }
        }
        self.tower_height()
    }
    fn print_top_n(&self, n: usize) {
        println!("-----------------");
        for i in 0..n.min(self.tube.len()) {
            let line = std::str::from_utf8(&self.tube[self.tube.len() - 1 - i])
                .unwrap();
            println!("{}", line);
        }
    }
}

pub fn task1(input: &str) -> usize {
    let mut tetris = Tetris::new();
    tetris.play(&input, 2022)
}

115 Востаннє редагувалося koala (18.12.2022 13:32:32)

Re: Advent of Code

2022#18, Rust
pub fn parse_input(input: &str) -> Vec<(usize, usize, usize)> {
    input
        .lines()
        .map(|line| {
            let mut line = line.split(',');
            (
                line.next().unwrap().parse().unwrap(),
                line.next().unwrap().parse().unwrap(),
                line.next().unwrap().parse().unwrap(),
            )
        })
        .collect()
}

pub fn task1(input: &[(usize, usize, usize)]) -> usize {
    let mut space = vec![vec![vec![false; 20]; 20]; 20];
    for &(x, y, z) in input {
        space[x][y][z] = true;
    }
    input.len() * 6
        - input
            .iter()
            .map(|&(x, y, z)| {
                let mut res = 0;
                if x > 0 && space[x - 1][y][z] {
                    res += 1
                }
                if y > 0 && space[x][y - 1][z] {
                    res += 1
                }
                if z > 0 && space[x][y][z - 1] {
                    res += 1
                }
                if x < space.len() - 1 && space[x + 1][y][z] {
                    res += 1
                }
                if y < space[x].len() - 1 && space[x][y + 1][z] {
                    res += 1
                }
                if z < space[x][y].len() - 1 && space[x][y][z + 1] {
                    res += 1
                }
                res
            })
            .sum::<usize>()
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum State {
    Empty,
    Outer,
    Lava,
}

fn floodfill(space: &mut [Vec<Vec<State>>]) {
    let mut stack = vec![(0, 0, 0)];
    while let Some((x, y, z)) = stack.pop() {
        if space[x][y][z] == State::Empty {
            space[x][y][z] = State::Outer;
            if x > 0 {
                stack.push((x - 1, y, z));
            }
            if y > 0 {
                stack.push((x, y - 1, z));
            }
            if z > 0 {
                stack.push((x, y, z - 1));
            }
            if x < space.len() - 1 {
                stack.push((x + 1, y, z));
            }
            if y < space[x].len() - 1 {
                stack.push((x, y + 1, z));
            }
            if z < space[x][y].len() - 1 {
                stack.push((x, y, z + 1));
            }
        }
    }
}

pub fn task2(input: &[(usize, usize, usize)]) -> usize {
    let mut space = vec![vec![vec![State::Empty; 22]; 22]; 22]; //adding planes around
    for &(x, y, z) in input {
        space[x + 1][y + 1][z + 1] = State::Lava;
    }

    floodfill(&mut space);

    input.len() * 6
        - input
            .iter()
            .map(|&(x, y, z)| {
                let (x, y, z) = (x + 1, y + 1, z + 1);
                let mut res = 0;
                if x > 0 && space[x - 1][y][z] != State::Outer {
                    res += 1
                }
                if y > 0 && space[x][y - 1][z] != State::Outer {
                    res += 1
                }
                if z > 0 && space[x][y][z - 1] != State::Outer {
                    res += 1
                }
                if x < space.len() - 1 && space[x + 1][y][z] != State::Outer {
                    res += 1
                }
                if y < space[x].len() - 1 && space[x][y + 1][z] != State::Outer
                {
                    res += 1
                }
                if z < space[x][y].len() - 1
                    && space[x][y][z + 1] != State::Outer
                {
                    res += 1
                }
                res
            })
            .sum::<usize>()
}

Ні, трохи Вастл знизив градус. Хоча наївний рекурсивний алгоритм рекомендував подивитися Stack Overflow :)
Зараз ще відрефакторю трохи

116 Востаннє редагувалося koala (18.12.2022 16:22:39)

Re: Advent of Code

Відрефакторив.
Умова така: є координати (цілі числа) кубів 1х1х1.
1. Знайти площу зовнішніх граней кубів (тобто (1,1,1) і (1,1,2) мають спільну площу 10).
2. Знайти площу зовнішніх граней кубів, не враховуючи внутрішніх порожнин.

2022#18, Rust, beautified
pub fn parse_input(input: &str) -> Vec<(usize, usize, usize)> {
    input
        .lines()
        .map(|line| {
            let mut line = line.split(',');
            (
                line.next().unwrap().parse().unwrap(),
                line.next().unwrap().parse().unwrap(),
                line.next().unwrap().parse().unwrap(),
            )
        })
        .collect()
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum State {
    Empty,
    Outer,
    Lava,
}

struct Space {
    cubes: Vec<Vec<Vec<State>>>,
    lava: Vec<(usize, usize, usize)>,
}

impl Space {
    fn from_lava(lava: &[(usize, usize, usize)]) -> Space {
        let (max_x, max_y, max_z) =
            lava.iter()
                .fold((0, 0, 0), |(max_x, max_y, max_z), &(x, y, z)| {
                    (max_x.max(x), max_y.max(y), max_z.max(z))
                });
        let lava: Vec<_> = lava
            .iter()
            .map(|&(x, y, z)| (x + 1, y + 1, z + 1))
            .collect();
        //+1 for len, +2 for empty planes around
        let mut cubes =
            vec![vec![vec![State::Empty; max_z + 3]; max_y + 3]; max_x + 3];
        for &(x, y, z) in &lava {
            cubes[x][y][z] = State::Lava;
        }

        Space { cubes, lava }
    }
    fn get_neighbors(
        &self,
        x: usize,
        y: usize,
        z: usize,
    ) -> impl Iterator<Item = (usize, usize, usize)> + '_ {
        const SHIFTS: &[(usize, usize, usize)] =
            &[(1, 0, 0), (0, 1, 0), (0, 0, 1)];
        SHIFTS
            .iter()
            .filter_map(move |&(dx, dy, dz)| {
                if let (Some(x), Some(y), Some(z)) =
                    (x.checked_sub(dx), y.checked_sub(dy), z.checked_sub(dz))
                {
                    Some((x, y, z))
                } else {
                    None
                }
            })
            .chain(SHIFTS.iter().filter_map(move |&(dx, dy, dz)| {
                if x + dx < self.cubes.len()
                    && y + dy < self.cubes[x].len()
                    && z + dz < self.cubes[x][y].len()
                {
                    Some((x + dx, y + dy, z + dz))
                } else {
                    None
                }
            }))
    }
    fn count_lava_neighbors(&self, state: State) -> usize {
        self.lava
            .iter()
            .map(|&(x, y, z)| {
                self.get_neighbors(x, y, z)
                    .filter(|&(dx, dy, dz)| self.cubes[dx][dy][dz] == state)
                    .count()
            })
            .sum()
    }
    fn floodfill(&mut self) {
        let mut stack = vec![(0, 0, 0)];
        while let Some((x, y, z)) = stack.pop() {
            if self.cubes[x][y][z] == State::Empty {
                self.cubes[x][y][z] = State::Outer;
                stack.extend(self.get_neighbors(x, y, z));
            }
        }
    }
}

pub fn task1(lava: &[(usize, usize, usize)]) -> usize {
    let space = Space::from_lava(lava);
    space.count_lava_neighbors(State::Empty)
}

pub fn task2(lava: &[(usize, usize, usize)]) -> usize {
    let mut space = Space::from_lava(lava);
    space.floodfill();
    space.count_lava_neighbors(State::Outer)
}

117

Re: Advent of Code

Світло нарешті (після більш ніж доби темряви) увімкнули, тому доробив 17-2. Мороки було багато, кілька ідей не спрацювали, але результат є. Хоча чистити тепер код ліньки.

Умова: є спрощений тетріс на 5 фігур і зациклена "програма", яка буде наступна фігура і в який бік буде її зсунуто (не менше 3 разів).
1. Знайти висоту вежі, утвореної 2_022 фігурами.
2. Знайти висоту вежі, утвореної 1_000_000_000_000 фігур.

2022#17, Rust
pub fn parse_input(input: &str) -> &str {
    input.trim()
}

const TUBE_WIDTH: usize = 7;

#[rustfmt::skip]
const FIGURES: &[&[&[u8]]] = &[
    &["####".as_bytes()],

    &[".#.".as_bytes(), 
      "###".as_bytes(), 
      ".#.".as_bytes()],

    &["###".as_bytes(),
      "..#".as_bytes(), 
      "..#".as_bytes()],

    &["#".as_bytes(), 
      "#".as_bytes(), 
      "#".as_bytes(), 
      "#".as_bytes()],

    &["##".as_bytes(), 
      "##".as_bytes()],
];

const EMPTY_LINE: &[u8] = ".......".as_bytes();

struct Tetris<'commands> {
    tube: std::collections::VecDeque<Vec<u8>>,
    figure_idx: usize,
    commands: &'commands [u8],
    commands_idx: usize,
    skipped: usize,
}

impl<'commands> Tetris<'commands> {
    fn new(commands: &str) -> Tetris {
        Tetris {
            tube: std::collections::VecDeque::new(),
            figure_idx: 0,
            commands: commands.as_bytes(),
            commands_idx: 0,
            skipped: 0,
        }
    }
    fn play(&mut self) {
        let mut x = 2;
        let mut y = -4;
        loop {
            let next_x = x + match self.commands[self.commands_idx] {
                b'>' => 1,
                b'<' => -1,
                dir => unimplemented!("Invalid move {}", dir),
            };
            self.commands_idx += 1;
            if self.commands_idx == self.commands.len() {
                self.commands_idx = 0;
            }
            if self.fits(next_x, y) {
                x = next_x;
            }
            let next_y = y + 1;
            if self.fits(x, next_y) {
                y = next_y;
            } else {
                break;
            }
        }
        self.fix_figure(x, y);
        self.figure_idx += 1;
        if self.figure_idx == FIGURES.len() {
            self.figure_idx = 0;
        }
    }
    fn fits(&self, x: i32, y: i32) -> bool {
        if x < 0 || x as usize + FIGURES[self.figure_idx][0].len() > TUBE_WIDTH
        {
            return false;
        }
        if y < 0 {
            return true;
        }
        if y >= self.tube.len() as i32 {
            return false;
        }
        FIGURES[self.figure_idx]
            .iter()
            .enumerate()
            .all(|(fy, fline)| {
                fline.iter().enumerate().all(|(fx, &fchar)| {
                    fchar != b'#'
                        || y - (fy as i32) < 0
                        || self.tube[(y - fy as i32) as usize]
                            [(x + fx as i32) as usize]
                            != b'#'
                })
            })
    }
    fn fix_figure(&mut self, x: i32, y: i32) {
        for f_y in 0..FIGURES[self.figure_idx].len() {
            if (y - f_y as i32) < 0 || self.tube.is_empty() {
                self.tube.push_front(EMPTY_LINE.to_vec());
            }
            for f_x in 0..FIGURES[self.figure_idx][f_y].len() {
                if FIGURES[self.figure_idx][f_y][f_x] == b'#' {
                    self.tube[(y - f_y as i32).max(0) as usize]
                        [(x as usize + f_x) as usize] = b'#';
                }
            }
        }
    }
    fn tower_height(&self) -> usize {
        self.skipped + self.tube.len()
    }
    fn print_top_n(&self, n: usize) {
        println!("-----------------");
        for line in self.tube.iter().take(n) {
            println!("{}", std::str::from_utf8(line).unwrap());
        }
    }
    fn gaps(&self) -> impl Iterator<Item = usize> + '_ {
        (0..TUBE_WIDTH).map(|i| {
            self.tube
                .iter()
                .map(|line| line[i])
                .take_while(|&x| x != b'#')
                .count()
        })
    }
}

impl PartialEq for Tetris<'_> {
    fn eq(&self, other: &Self) -> bool {
        self.figure_idx == other.figure_idx
            && self.commands_idx == other.commands_idx
            && self.gaps().cmp(other.gaps()) == std::cmp::Ordering::Equal
    }
}

pub fn task1(input: &str) -> usize {
    let mut tetris = Tetris::new(input);
    for _ in 0..2022 {
        tetris.play();
    }
    tetris.tower_height()
}

pub fn task2(input: &str) -> usize {
    let mut hare = Tetris::new(input);
    let mut tortoise = Tetris::new(input);

    //Floyd's algorithm
    //First meet
    for _ in 0.. {
        hare.play();
        hare.play();
        tortoise.play();
        if hare == tortoise {
            break;
        }
    }
    //Find the interval until looping starts
    let mut start = 0;
    hare = Tetris::new(input);
    for i in 0.. {
        hare.play();
        tortoise.play();
        if hare == tortoise {
            start = i;
            break;
        }
    }
    //Find the period
    let mut period = 0;
    for i in 0.. {
        hare.play();
        if hare == tortoise {
            period = i + 1;
            break;
        }
    }

    //Let hare run one last time
    hare = Tetris::new(input);
    for _ in 0..start {
        hare.play();
    }
    let start_height = hare.tower_height();
    for _ in 0..period {
        hare.play();
    }
    let period_height = hare.tower_height() - start_height;
    let remains = (1_000_000_000_000 - start) % period;
    for _ in 0..remains {
        hare.play();
    }
    let remains_height = hare.tower_height() - start_height - period_height;
    start_height
        + (1_000_000_000_000 - start - remains) / period * period_height
        + remains_height
}

118 Востаннє редагувалося dot (21.12.2022 18:02:13)

Re: Advent of Code

Transformator zdox i osj śćojno povernuly elektriku. Tak, ja bez elektriky buv z 9-ho hrudnja (navêtj ćas zapamjatav: 22 hodyny), xex. Sprobuju, zvêsno, nadoluźyty, ale nićoho ne garantuju.

Подякували: lucas-kane1

119

Re: Advent of Code

Умова: мавпи вигукують число чи імена двох інших мавп і операцію, на кшталт

root: pppw + sjmn
dbpl: 5
cczh: sllz + lgvd

У другому випадку значення дорівнює операції над тим, що вигукують інші мавпи.
1. Знайти, що вигукує мавпа root.
2. Операція root замінюється на =, а мавпа humn - ви. Що ви маєте вигукнути, щоб операція root була true?

2022#21, Rust
type Yell = u64;

pub enum Monkey {
    Value(Yell),
    Operation(String, char, String),
}

use std::collections::HashMap;

impl Monkey {
    fn new(input: &str) -> Monkey {
        if let Ok(value) = input.parse() {
            Monkey::Value(value)
        } else {
            let (left, op, right): (String, char, String);
            text_io::scan!(input.bytes()=>"{} {} {}",left, op, right);
            Monkey::Operation(left, op, right)
        }
    }
    fn yell(&self, monkeys: &HashMap<String, Monkey>) -> Yell {
        match self {
            Monkey::Value(yell) => *yell,
            Monkey::Operation(left, op, right) => {
                let left = monkeys[left].yell(monkeys);
                let right = monkeys[right].yell(monkeys);
                match op {
                    '+' => left + right,
                    '-' => left - right,
                    '*' => left * right,
                    '/' => left / right,
                    _ => unimplemented!(),
                }
            }
        }
    }
    fn has_humn(&self, monkeys: &HashMap<String, Monkey>) -> bool {
        match self {
            Monkey::Value(_) => false,
            Monkey::Operation(left, _, right) => {
                left == "humn"
                    || right == "humn"
                    || monkeys[left].has_humn(monkeys)
                    || monkeys[right].has_humn(monkeys)
            }
        }
    }
    fn find_humn(
        &self,
        monkeys: &HashMap<String, Monkey>,
        value: Yell,
    ) -> Yell {
        println!("Looking for humn to ouput {}", value);
        match self {
            Monkey::Operation(left, op, right) => {
                if left == "humn" || monkeys[left].has_humn(monkeys) {
                    let right = monkeys[right].yell(monkeys);
                    let to_left = match op {
                        '+' => value - right,
                        '-' => value + right,
                        '*' => value / right,
                        '/' => value * right,
                        _ => unimplemented!(),
                    };
                    monkeys[left].find_humn(monkeys, to_left)
                } else {
                    let left = monkeys[left].yell(monkeys);
                    let to_right = match op {
                        '+' => value - left,
                        '-' => left - value,
                        '*' => value / left,
                        '/' => left / value,
                        _ => unimplemented!(),
                    };
                    monkeys[right].find_humn(monkeys, to_right)
                }
            }
            Monkey::Value(_) => value, //this means it's humn
        }
    }
}

pub fn parse_input(input: &str) -> HashMap<String, Monkey> {
    input
        .lines()
        .map(|line| {
            let mut parts = line.split(": ");
            let name = parts.next().unwrap();
            let yell = parts.next().unwrap();
            (name.to_owned(), Monkey::new(yell))
        })
        .collect()
}

pub fn task1(map: &HashMap<String, Monkey>) -> Yell {
    map[&"root".to_owned()].yell(map)
}

pub fn task2(map: &HashMap<String, Monkey>) -> Yell {
    if let Monkey::Operation(left, _, right) = &map["root"] {
        if map[left].has_humn(map) {
            map[left].find_humn(map, map[right].yell(map))
        } else {
            map[right].find_humn(map, map[left].yell(map))
        }
    } else {
        unreachable!()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    const INPUT: &str = "root: pppw + sjmn
dbpl: 5
cczh: sllz + lgvd
zczc: 2
ptdq: humn - dvpt
dvpt: 3
lfqf: 4
humn: 5
ljgn: 2
sjmn: drzm * dbpl
sllz: 4
pppw: cczh / lfqf
lgvd: ljgn * ptdq
drzm: hmdt - zczc
hmdt: 32";

    #[test]
    fn test_task1() {
        assert_eq!(task1(&parse_input(INPUT)), 152);
    }

    #[test]
    fn test_task2() {
        assert_eq!(task2(&parse_input(INPUT)), 301);
    }
}

120

Re: Advent of Code

2022#10, Python 3.8.10
instructions: str = open("input.txt").read().split("\n")


def execution(points: list, draw: bool = True):
    x: int = 1
    cycles: int = 1
    output = str() if draw else int()

    for instruction in instructions:
        instruction = instruction.split(" ")

        for _ in instruction:
            if cycles in points:
                if draw:
                    points.pop(0)
                else:
                    output += x * cycles

            if draw:
                if cycles - 1 - points[0] in range(x - 1, x + 1 + 1):
                    output += "#"
                else:
                    output += "."

            cycles += 1

        if len(instruction) > 1:
            x += int(instruction[1])

    if draw:
        for h in range(high):
            print(output[h * wide : h * wide + wide])
    else:
        print(output)


execution([20, 60, 100, 140, 180, 220], False)  # Part One: 15360

wide: int = 40
high: int = 6
execution([x for x in range(0, wide * high, wide)])  # Part Two: PHLHJGZA
Подякували: koala1