121

Re: Advent of Code

2022#11, Python 3.8.10
import copy, math, operator, re

monkeys = []

for line in open("input.txt").readlines():
    line = line.strip()

    if line.startswith("Monkey"):
        monkeys.append([])
        continue

    if line.startswith("Start"):
        monkeys[-1].append(list(map(int, re.findall("\d+", line))))
        continue

    if line.startswith(("Test", "If")):
        monkeys[-1].append(int(re.search(r"\d+", line).group()))
        continue

    if line == "":
        continue

    monkeys[-1].append(line.split("= old ")[-1].split(" "))

ops = {
    "+": operator.add,
    "-": operator.sub,
    "*": operator.mul,
    "/": operator.truediv,
}


def operation(old, new):
    op, val = new
    val = old if val == "old" else int(val)
    return ops[op](old, val)


def inspecting(isridiculous: bool = True):
    inspectors = copy.deepcopy(monkeys)
    activities = [0] * len(inspectors)
    magic = math.prod([inspector[2] for inspector in inspectors]) if isridiculous else 3

    for _ in range(10_000 if isridiculous else 20):
        for m, (worries, new, isdiv, yeah, nope) in enumerate(inspectors):
            if len(worries) == 0:
                continue

            activities[m] += len(worries)

            for worry in worries:
                worry = operation(worry, new)
                worry = worry % magic if isridiculous else worry // magic
                inspectors[nope if worry % isdiv else yeah][0].append(worry)

            inspectors[m][0] = []

    activities = sorted(activities)
    print(activities[-1] * activities[-2])

inspecting(False) # Part One: 56120
inspecting() # Part Two: 24389045529

122

Re: Advent of Code

Бачу купу зайвих continue, можна було elif-ами обійтися.
А взагалі я погано розумію, як вам не ліньки запам'ятовувати, що у вас у inspector[2]. Чи не логічніше клас проголосити з нормальними назвами полів?

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

123 Востаннє редагувалося dot (24.12.2022 16:29:19)

Re: Advent of Code

koala написав:

Бачу купу зайвих continue, можна було elif-ами обійтися.

Ja prosto ne pamjatav, ćy spracjovuje nastupnyj elif, jakśćo otrymaly true. Ja ćomusj spryjmav ce jak (ne pajtonovsjki) switch + case, de nastupnyj case moźe spracjuvaty, jakśćo tut true. Ale perevêrjaty menê bulo lênjky, ale śćojno pohljanuv — ne spracjovuje. Djakuju, śćo ukazaly, teper maty jmu na uvazê.

Pererobyv cej fragment, nićoho osoblyvoho:

    if line.startswith("Monkey"):
        monkeys.append([])
    elif line.startswith("Start"):
        monkeys[-1].append(list(map(int, re.findall("\d+", line))))
    elif line.startswith(("Test", "If")):
        monkeys[-1].append(int(re.search(r"\d+", line).group()))
    elif line != "":
        monkeys[-1].append(line.split("= old ")[-1].split(" "))

А взагалі я погано розумію, як вам не ліньки запам'ятовувати, що у вас у inspector[2]. Чи не логічніше клас проголосити з нормальними назвами полів?

Zvôdsy (pravda, to daljśe), kek:

for m, (worries, new, isdiv, yeah, nope) in enumerate(inspectors):

Ale, tak, zhôdnyj, śćo to ne duźe po ludzjky. A peretvorjuvaty v slovnyk poky ne xoću. Śće moźna stvoryty okremi masyvy, teź varijant.

124

Re: Advent of Code

Я 2017 рік пропустив, зараз проходжу. Схоже, дарма, це, видається, був найлегший для мене - я вже 21 день зробив, усе нормально іде. Щоб не спамити, ось мій гітхаб.

Подякували: dot, leofun012

125

Re: Advent of Code

2017-23-2. Нарешті щось складне. Десь я читав, що таке якимись хитрими матрицями робиться. Може, на тижні пошукаю.

126

Re: Advent of Code

До 2017 не дійшов, натомість перерефакторив код на предмет коректної обробки помилок за допомогою thiserror (не хочу використовувати anyhow). Закликаю всіх, хто йтиме по моїх стопах, не повторювати помилок і, щойно проєкт стає більшим за 300-500 рядків (а краще - коли стає ясно, що проєкт стане більшим), додавати обробку Result.
Зробив колись пропущений

2015#22, Rust
use crate::*;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::collections::HashSet;

#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct Game {
    hp: i32,
    mp: i32,
    shield: i32,
    poison: i32,
    recharge: i32,
    mana_spent: i32,
    boss_hp: i32,
    boss_dmg: i32,
}

pub fn parse_input(input: &str) -> Result<Game> {
    let (hp, dmg) = scan_fmt::scan_fmt!(
        input,
        "Hit Points: {}
Damage: {}",
        i32,
        i32
    )
    .map_err(|_| task_error!("Wrong input format"))?;
    Ok(Game::new(hp, dmg))
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
enum Spell {
    MagicMissile,
    Drain,
    Shield,
    Poison,
    Recharge,
}

const PRICES: Lazy<HashMap<Spell, i32>> = Lazy::new(|| {
    HashMap::from([
        (Spell::MagicMissile, 53),
        (Spell::Drain, 73),
        (Spell::Shield, 113),
        (Spell::Poison, 173),
        (Spell::Recharge, 229),
    ])
});

impl Game {
    fn new(hp: i32, dmg: i32) -> Game {
        Game {
            hp: 50,
            mp: 500,
            shield: 0,
            poison: 0,
            recharge: 0,
            mana_spent: 0,
            boss_hp: hp,
            boss_dmg: dmg,
        }
    }
    fn effects(&mut self) {
        if self.shield > 0 {
            self.shield -= 1;
        }
        if self.poison > 0 {
            self.boss_hp -= 3;
            self.poison -= 1;
        }
        if self.recharge > 0 {
            self.mp += 101;
            self.recharge -= 1;
        }
    }
    fn player_action(&mut self, action: Spell, hard_mode: bool) -> bool {
        if hard_mode {
            self.hp -= 1;
        }
        if self.lose() {
            return false;
        }
        let price = PRICES[&action];
        if price > self.mp {
            return false;
        }
        match action {
            Spell::MagicMissile => {
                self.boss_hp -= 4;
            }
            Spell::Drain => {
                self.hp += 2;
                self.boss_hp -= 2;
            }
            Spell::Shield if self.shield == 0 => {
                self.shield = 6;
            }
            Spell::Poison if self.poison == 0 => {
                self.poison = 6;
            }
            Spell::Recharge if self.recharge == 0 => {
                self.recharge = 5;
            }
            _ => return false,
        }
        self.mp -= price;
        self.mana_spent += price;
        true
    }
    fn boss_action(&mut self) {
        if self.shield == 0 {
            self.hp -= self.boss_dmg;
        } else {
            self.hp -= self.boss_dmg - 7;
        }
    }
    fn win(&self) -> bool {
        self.hp > 0 && self.boss_hp <= 0
    }
    fn lose(&self) -> bool {
        self.hp <= 0
    }
}

pub fn task(&game: &Game, hard_mode: bool) -> Result<i32> {
    let mut situations = HashSet::from([game]);
    let mut best_mana = None;
    while !situations.is_empty() {
        let mut new_situations = HashSet::new();
        for game in situations {
            for action in [
                Spell::MagicMissile,
                Spell::Drain,
                Spell::Shield,
                Spell::Poison,
                Spell::Recharge,
            ] {
                let mut game = game;
                game.effects();
                if game.player_action(action, hard_mode) {
                    game.effects();
                    if game.win() {
                        best_mana = Some(best_mana.map_or_else(
                            || game.mana_spent,
                            |x: i32| x.min(game.mana_spent),
                        ));
                    }
                    game.boss_action();
                    if !game.lose()
                        && best_mana
                            .map_or_else(|| true, |x| x > game.mana_spent)
                    {
                        new_situations.insert(game);
                    }
                }
            }
        }
        situations = new_situations;
    }
    best_mana.ok_or(task_error!("No solution found"))
}

pub fn task1(game: &Game) -> Result<i32> {
    task(game, false)
}

pub fn task2(game: &Game) -> Result<i32> {
    task(game, true)
}

У червні once_cell має бути змержений у основну гілку, позбудуся однієї залежності.

127

Re: Advent of Code

https://replace.org.ua/uploads/images/931/f1a694cb1d3187919df1441628bef1ed.jpg

128

Re: Advent of Code

2016#11, Rust
use crate::*;

use itertools::Itertools;
use once_cell::sync::Lazy;
use std::collections::{HashMap, HashSet};

#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub struct Position {
    state: i32,
}

impl Position {
    fn get_item(&self, n: usize) -> i32 {
        self.state >> (n * 2 + 2) & 0x3
    }
    fn set_item(&mut self, n: usize, val: i32) {
        let mask = 0x3 << (n * 2 + 2);
        self.state &= !mask;
        self.state |= (val & 0x3) << (n * 2 + 2);
    }
    fn get_elev(&self) -> i32 {
        self.state & 0x3
    }
    fn set_elev(&mut self, val: i32) {
        self.state &= !0x3;
        self.state |= val & 0x3;
    }

    fn is_done(&self, size: usize) -> bool {
        self.get_elev() == 3
            && (0..2 * size)
                .map(|i| self.get_item(i))
                .all(|item| item == 3)
    }
    fn is_valid(&self, size: usize) -> bool {
        for floor in 0..=3 {
            let mut has_generator = false;
            for i in 0..size {
                if self.get_item(2 * i + 1) == floor {
                    has_generator = true;
                    break;
                }
            }
            if has_generator {
                for i in 0..size {
                    if self.get_item(2 * i) == floor
                        && self.get_item(2 * i + 1) != floor
                    {
                        return false;
                    }
                }
            }
        }
        true
    }
    fn get_elevator_floor(&self, size: usize) -> Vec<usize> {
        let elev = self.get_elev();
        (0..2 * size)
            .filter(|&i| self.get_item(i) == elev)
            .collect()
    }
}

pub fn parse_input(input: &str) -> Result<(usize, Position)> {
    let mut position = Position { state: 0 };
    let mut name_map = HashMap::new();
    for line in input.lines() {
        let (floor, list) = scan_fmt::scan_fmt!(
            line,
            "The {/first|second|third|fourth/} floor contains {/[^.]*/}.{e}",
            String,
            String
        )?;
        if list == "nothing relevant" {
            continue;
        }
        let floor = match floor.as_str() {
            "first" => 0,
            "second" => 1,
            "third" => 2,
            "fourth" => 3,
            other => return Err(task_error!("Unknown floor '{other}'")),
        };
        static SPLIT_REGEX: Lazy<regex::Regex> =
            Lazy::new(|| regex::Regex::new(r"(, and )|(, )|( and )").unwrap());
        for item in SPLIT_REGEX.split(&list) {
            let name_map_len = name_map.len();
            if let Ok(generator) =
                scan_fmt::scan_fmt!(item, "a {} generator", String)
            {
                let idx = *name_map.entry(generator).or_insert(name_map_len);
                position.set_item(2 * idx + 1, floor);
            } else {
                let microchip = scan_fmt::scan_fmt!(
                    item,
                    "a {}-compatible microchip",
                    String
                )
                .map_err(|_| {
                    task_error!("List '{list}', item '{item}' fails")
                })?;
                let idx = *name_map.entry(microchip).or_insert(name_map_len);
                position.set_item(2 * idx, floor);
            }
        }
    }
    Ok((name_map.len(), position))
}

pub fn task(input: &Position, size: usize) -> Result<usize> {
    let mut visited = HashSet::new();
    let mut current = HashSet::from([input.clone()]);
    for step in 1.. {
        let mut new = HashSet::new();
        for pos in &current {
            let elevator_floor: Vec<usize> = pos.get_elevator_floor(size);
            for direction in [-1, 1] {
                let new_floor = pos.get_elev() + direction;
                if (0..=3).contains(&new_floor) {
                    for num_items in 1..=2 {
                        for moving in
                            elevator_floor.iter().combinations(num_items)
                        {
                            let mut new_pos = pos.clone();
                            for &item in moving {
                                new_pos.set_item(item, new_floor);
                            }
                            new_pos.set_elev(new_floor);
                            if new_pos.is_done(size) {
                                return Ok(step);
                            }
                            if new_pos.is_valid(size)
                                && !visited.contains(&new_pos)
                            {
                                new.insert(new_pos);
                            }
                        }
                    }
                }
            }
        }
        if new.is_empty() {
            break;
        }
        visited.extend(current);
        current = new;
    }
    Err(task_error!("Solution not found"))
}

pub fn task1((size, position): &(usize, Position)) -> Result<usize> {
    task(position, *size)
}

pub fn task2((size, position): &(usize, Position)) -> Result<usize> {
    task(&position, *size + 2)
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_task1() {
        let input = "The first floor contains a hydrogen-compatible microchip and a lithium-compatible microchip.
The second floor contains a hydrogen generator.
The third floor contains a lithium generator.
The fourth floor contains nothing relevant.";
        let position = parse_input(input);
        assert_eq!(task1(&position.unwrap()).unwrap(), 11);
    }
}

Трохи помучився, і все одно остаточний код не дуже оптимізований, але заміна масиву (де кожне значення могло бути від 0 до 3) на бітове поле прискорило розв'язок удесятеро (22 секунди проти 241 на початковому).

Подякували: Chemist-i, leofun012

129

Re: Advent of Code

Виявив у себе пару психологічних блоків у розв'язуванні Advent of Code. Я підсвідомо вважаю нечесним:
1. Шукати розв'язок у мережі.
Я знаю не все, а AoC давно розв'язаний і обговорений. Знайти відповідь - легше легкого, та ще й з роз'ясненнями. Але я довго намагаюся зробити сам. Дуже довго.
2. Аналізувати вхідні дані вручну.
Ну типу я ж маю програму написати, правильно? Ну то треба написати код, що аналізуватиме дані, а не самому дані переробляти.

Зробив 2016-22-2. Там треба в п'ятнашки грати. Ну як у п'ятнашки - треба одну фішку з одного кута в інший перетягнути, решта фішок не має значення, але є ще кілька нерухомих фішок, які все псують. На полі приблизно 30х30. Питання - мінімальна кількість кроків. Поле задане купою значень, які досить легко обчислюються в фішки.  І от якось не виходило навіть почати: повний перебір не зробиш, а придумати критерій наближення через нерухомі фішки не виходило. Змусив себе все ж погуглити - і виявилося, що треба було вивести поле (воно дуже просте) і руками на ньому порахувати. Саме "треба", автор відповів, що це очікуваний розв'язок. Одразу два блоки зачепило.

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

130 Востаннє редагувалося koala (13.05.2023 08:11:44)

Re: Advent of Code

Зробив нарешті 2017-23. Крута матрична оптимізація, ага. Тупо вкрай неефективний пошук простих чисел, трохи повагався, додати в асемблер достроковий вихід із циклу чи переписати, переписав основний цикл руцями в своєму коді. Ну, і пара дрібних пасток на граничних випадках і з умовою. На одну з них, правда, поліз глянути на Reddit, але то вже я перестарався у боротьбі з блоками :)