121

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)
}

122 Востаннє редагувалося koala (18.12.2022 12: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 :)
Зараз ще відрефакторю трохи

123 Востаннє редагувалося koala (18.12.2022 15: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)
}

124

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
}

125 Востаннє редагувалося dot (21.12.2022 17: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

126

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);
    }
}

127

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

128

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

129

Re: Advent of Code

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

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

130 Востаннє редагувалося dot (24.12.2022 15: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.

131

Re: Advent of Code

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

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

132

Re: Advent of Code

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

133

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 має бути змержений у основну гілку, позбудуся однієї залежності.

134

Re: Advent of Code

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

135

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

136

Re: Advent of Code

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

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

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

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

Re: Advent of Code

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

138

Re: Advent of Code

А хто 2018 рік робив? У мене таке враження, що там задачі дещо складніші. Чи то в мене загальне зниження когнітивних здібностей?

139

Re: Advent of Code

Так, новий рік, нові задачі. Хтось ще робить?

140

Re: Advent of Code

Перейшов на бібліотеку prse для парсингу вхідних даних. Штука, по-перше, украй проста, а по-друге, украй потужна; на відміну від аналогів (scan_fmt, який я використовував до того, і sscanf), не залежить від Regex-ів, уміє читати &str (позичені стрічки), масиви, вектори і ітератори; але, на жаль, не розуміє зайвих пробілів.
Трохи промучився, але зробив

читання даних для четвертого дня
            let (_num, win, matches): (&str, _, _) =
                prse::try_parse!(line, "Card {}: {: :0} | {: :0}")?;

            let win: HashSet<usize> = win
                .filter(|i| i != &Ok(""))
                .map(|i| Ok(i?.parse()?))
                .collect::<AocResult<_>>()?;
            let matches = matches
                .filter(|i| i != &Ok(""))
                .map(|i| Ok(i?.parse()?))
                .collect::<AocResult<_>>()?;

            Ok(win.intersection(&matches).count())

Зайві пробіли створюють порожні значення, які воно намагається читати як числа, якщо сканувати напряму; тому довелося додати фільтр.

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