41

Re: Advent of Code

Те саме на Rust, з математикою щось у мене тепер зовсім поганенько. Там ніби Факі в бік вершини рухався, мо допоможе?

2021_7.rs
fn task<F>(crabs: &[i32], fuel: F) -> i32
where
    F: Fn(i32, i32) -> i32,
{
    let total = |pos| crabs.iter().map(|&crab| fuel(crab, pos)).sum();
    let mut pos = (crabs.last().unwrap() - crabs.first().unwrap()) / 2;
    let mut step = pos / 2;
    loop {
        let dist = total(pos);
        let dist_l = total(pos - 1);
        let dist_r = total(pos + 1);
        if dist <= dist_l && dist <= dist_r {
            return dist;
        } else if dist_r < dist {
            pos += step;
        } else {
            pos -= step;
        }
        step = std::cmp::max(1, step / 2);
    }
}

fn task1(crabs: &[i32]) -> i32 {
    task(crabs, |crab, pos| (pos - crab).abs())
}

fn task2(crabs: &[i32]) -> i32 {
    task(crabs, |crab, pos| {
        (pos - crab).abs() * ((pos - crab).abs() + 1) / 2
    })
}

fn main() {
    let input = aoc::get_input_from_ini_with_year("7", "2021").unwrap();
    let crabs: Vec<_> = input.split(',').map(|x| x.parse().unwrap()).collect();
    println!("Result1: {}", task1(&crabs));
    println!("Result2: {}", task2(&crabs));
}

42

Re: Advent of Code

2021#8, Py3.9.5
import re

data = list()

with open("input.txt") as file:
    for line in file:
        entry = ["".join(sorted(i)) for i in re.findall(r"\w+", line)]
        data.append((entry[:10], entry[10:]))

#  tttt
# l    r
# t    t
#  mmmm
# l    r
# b    b
#  bbbb

count = 0
sum = 0

for patterns, outputs in data:
    digits = dict()
    fives = list()
    sixs = list()

    # get basic numbers as 1, 4, 7, 8
    # and collect digits by segments
    for pattern in patterns:
        if len(pattern) == 2:
            digits["1"] = list(pattern)
        elif len(pattern) == 4:
            digits["4"] = list(pattern)
        elif len(pattern) == 3:
            digits["7"] = list(pattern)
        elif len(pattern) == 7:
            digits["8"] = list(pattern)
        elif len(pattern) == 5:
            fives.append(list(pattern))
        else:
            sixs.append(list(pattern))

    for one in digits["1"]:
        # get 6, rt
        for six in sixs[:]:
            if one not in six:
                digits["6"] = six
                rt = one
                sixs.remove(six)
                break
        if len(six) == 2:
            break

    # get rb
    for one in digits["1"]:
        if one is not rt:
            rb = one
            break

    # get 2, 3, 5
    for one in digits["1"]:
        for five in fives:
            if rt not in five:
                digits["5"] = five
            elif rb not in five:
                digits["2"] = five
            else:
                digits["3"] = five

    lb = (set(digits["6"]) - set(digits["5"])).pop()

    # get 0, 9
    for six in sixs:
        if lb in six:
            digits["0"] = six
        else:
            digits["9"] = six

    digits = dict(zip(["".join(i) for i in digits.values()], digits.keys()))
    numbers = str()

    for output in outputs:
        if digits[output] in ["1", "4", "7", "8"]:
            count += 1
        numbers += digits[output]
    sum += int(numbers)

print("Part One:", count)
print("Part Two:", sum)

43 Востаннє редагувалося dot (08.12.2021 13:14:08)

Re: Advent of Code

Odyn tcuvak zrobyv druhu tcastynu krasyvo. Teg tak ± podumuvav zrobyty tcerez set i issubset, ale mij tupyj mozok tcomusj tupo zatcepyv sja za «spercu sortonuty rjadok i tak trymaj!».

44

Re: Advent of Code

Я поки що обломився з другою частиною :( Часу мало.

45 Востаннє редагувалося koala (09.12.2021 00:40:50)

Re: Advent of Code

Зробив. Змусив себе. Довго, марудно і некрасиво. Можна значно краще. Але сильно ліньки.

2021/8.py
import aoc

import os

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

DIGIT_SIZES = {0:6, 1:2, 2:5, 3:5, 4:4, 5:5, 6:6, 7:3, 8:7, 9:6}
SIZE_DIGITS = {size:[digit for digit in DIGIT_SIZES if DIGIT_SIZES[digit]==size] for size in DIGIT_SIZES.values()}


# 1 - 2, 4-4, 7-3, 8-7
# 5:
#  2&1 = 1, !2&4 = 2,  2&7 = 2
# !3&1 = 2,  3&4 = 3, !3&7 = 3
#  5&1 = 1,  5&4 = 3,  5&7 = 2
# 6:
#  0&1 = 2,  0&4 = 3,  0&7 = 3
# !6&1 = 1,  6&4 = 3, !6&7 = 2
#  9&1 = 2, !9&4 = 4,  9&7 = 3

def task1(data: str) -> int:
    result = 0
    for line in data.splitlines():
         out = line.split('|')[1].split()
         result += sum(1 for word in out if len(SIZE_DIGITS[len(word)]) == 1 )
    return result

def deduct(digit_len, known):
    if digit_len==5:
        return next({2,3,5}-set(digits.values()))
    elif digit_len==6:
        return next({0,6,9}-set(digits.values()))
    assert False

def task2(data: str) -> int:
    result = 0
    for line in data.splitlines():
        inp, out = [part.split() for part in line.split('|')]
        line = [''.join(sorted(digit)) for digit in inp+out]
        out =  [''.join(sorted(digit)) for digit in out]
        digits = {}
        for comb in line:
            if len(SIZE_DIGITS[len(comb)])==1:
                digits[comb] = SIZE_DIGITS[len(comb)][0]
                digits[SIZE_DIGITS[len(comb)][0]] = comb

        line = list({comb for comb in line if comb not in digits})
        for comb in line:
            digit = None
            if len(comb)==5:
                 if len(set(comb)&set(digits[4]))==2:
                     digit = 2
                 elif len(set(comb)&set(digits[1]))==2:
                     digit = 3
                 else:
                     digit = 5
            else: # len(comb)==6:
                 if len(set(comb)&set(digits[1]))==1:
                     digit = 6
                 elif len(set(comb)&set(digits[4]))==4:
                     digit = 9
                 else:
                     digit = 0
            if not digit is None:
                digits[comb] = digit
                digits[digit] = comb

        for comb in out:
            if not comb in digits:
                digits[comb] = deduct(len(comb), digits)
         
        result += sum(10**(3-i)*digits[out_digit] for i, out_digit in enumerate(out))
    return result
    
if __name__ == '__main__':
    data = aoc.load_data(NUMBER, verbose=True)
    print('Task1:', task1(data))
    print('Task2:', task2(data))

День 8: 1. Є 7-сегментні цифри з переплутаними контактами. Заданий набір спостережень за такими цифрами у вигляді назв сегментів (від a до g). Визначити, скільки разів вмикалися цифри 1, 4, 7, 8 (які можна визначити за відомою кількістю сегментів). 2. Серед спостережень виокремлені 4-цифрові значення. Знайти суму цих значень.

46

Re: Advent of Code

2021/9.py
import aoc

import os

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

def task1(data: str) -> int:
    data = [[int(height) for height in line] for line in data.splitlines()]
    result = 0
    for i, line in enumerate(data):
        for j, height in enumerate(line):
            if (    (i==0              or height<data[i-1][j])
                and (j==0              or height<data[i][j-1])
                and (i==len(data)-1    or height<data[i+1][j])
                and (j==len(data[0])-1 or height<data[i][j+1])
                ):
                result += height+1
    return result

def floodfill(cave, y, x):
    if y<0 or x<0 or y==len(cave) or x==len(cave[0]) or cave[y][x]==9:
        return 0
    cave[y][x] = 9
    return (  floodfill(cave, y+1, x) + floodfill(cave, y-1, x)
            + floodfill(cave, y, x+1) + floodfill(cave, y, x-1)) + 1

def task2(data: str) -> int:
    data = [[int(height) for height in line] for line in data.splitlines()]
    basins = []
    for i in range(len(data)):
        for j in range(len(data[0])):
            basins.append(floodfill(data, i, j))
    basins.sort()
    return basins[-1]*basins[-2]*basins[-3]
    

if __name__ == '__main__':
    data = aoc.load_data(NUMBER, verbose=True)
    print('Task1:', task1(data))
    print('Task2:', task2(data))

День 9. 1. Задана прямокутна мапа висот (від 1 до 9), знайти кількість точок, нижчих за усіх свої сусідів. 2. Знайти площі найбільших зв'язних по горизонталі і вертикалі фрагментів мапи без дев'яток, повернути добуток трьох найбільших фрагментів.

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

47 Востаннє редагувалося dot (09.12.2021 20:47:06)

Re: Advent of Code

2021#9
import math

heightmap = [[int(i) for i in line] for line in open("input.txt").read().split()]
lenght = len(heightmap[0])
risk = int(0)
basins = list()


def hm(c: list) -> int:
    r, s = c
    return heightmap[r][s]


def sides(r: int, c: int) -> set:
    points = set()
    if r > 0:
        points.add((r - 1, c))
    if r < len(heightmap) - 1:
        points.add((r + 1, c))
    if c > 0:
        points.add((r, c - 1))
    if c < lenght - 1:
        points.add((r, c + 1))
    return points


def low(r: int, c: int) -> bool:
    return True if hm((r, c)) < min(map(hm, sides(r, c))) else False


def basin(r: int, c: int) -> list:
    ins = {(r, c)}
    basin = {(r, c)}

    while ins:
        news = set()
        for i in ins:
            n = set()
            for s in sides(*i) - basin:
                if hm(s) > hm(i) and hm(s) != 9:
                    n.add(s)
                    basin.add(s)
            news.update(n)
        ins = news
    return basin


for r, row in enumerate(heightmap):
    for c, col in enumerate(row):
        if low(r, c):
            risk += col + 1
            basins.append(basin(r, c))

coords = list()
for b in basins:
    coords += b

class bcolors:
    OKBLUE = '\033[94m'
    ENDC = '\033[0m'

for r, row in enumerate(heightmap):
    line = str()
    for c, col in enumerate(row):
        if (r, c) in coords:
            line += bcolors.OKBLUE + str(hm((r, c))) + bcolors.ENDC
        else:
            line += str(hm((r, c)))
    print(line)

print("Part One:", risk)
print("Part Two:", math.prod(sorted(map(len, basins), reverse=True)[:3]))

Dekiljka proxolodnyx opovidok.

Perca. Pislja toho, jak ja nadislav rozvjatok poperednjoji zadatcy sjudy i v dekotryx mistsjax — vyrubyv sja internet, bo jakyjsj rozumnyk desj obrizav provid. Vtcasno ja vtcora rozvjazav zadatcku, ehe, pravda, ne vstyh zakomytyty, tomu na Qythabi vge odna typu prohalyna. Internetu ne bulo ag-no do 16 hodyny sjoʼdnja.

Druha, pro tsju zadatcu. Ja tcomusj dumav, ge susjidnja kljityna muse buty tupo na odynytsju biljce. Tomu perca vidpovidj za testom zjbihala sja, ale na tsjomu vse. Ja dectco ljinyvyj, tomu xotjiv pohljanuty na basejny vizujaljno — ctco i zrobyv. Tak i dijclo, ge riznytsja v odynytsju neobovjazkova. No i pomitno, ge rozvjatok neoptimizovanyj, bo mogna bulo vykorystaty fuktsiju z poperednjoji zadatcy.

48

Re: Advent of Code

2021_9.rs
fn task1(map: &Vec<Vec<u8>>) -> usize {
    map.iter()
        .enumerate()
        .map(|(i, line)| {
            line.iter()
                .enumerate()
                .filter_map(|(j, &height)| {
                    if (i == 0 || height < map[i - 1][j])
                        && (j == 0 || height < map[i][j - 1])
                        && (i == map.len() - 1 || height < map[i + 1][j])
                        && (j == line.len() - 1 || height < map[i][j + 1])
                    {
                        Some(height as usize + 1)
                    } else {
                        None
                    }
                })
                .sum::<usize>()
        })
        .sum()
}

fn floodfill(map: &mut Vec<Vec<u8>>, i: i32, j: i32) -> usize {
    if i < 0
        || j < 0
        || i >= map.len() as i32
        || j >= map[i as usize].len() as i32
        || map[i as usize][j as usize] == 9
    {
        0
    } else {
        map[i as usize][j as usize] = 9;
        1 + floodfill(map, i - 1, j)
            + floodfill(map, i + 1, j)
            + floodfill(map, i, j - 1)
            + floodfill(map, i, j + 1)
    }
}

fn task2(map: &Vec<Vec<u8>>) -> usize {
    let mut map = map.clone();
    let mut result = Vec::new();
    for i in 0..map.len() {
        for j in 0..map[i].len() {
            result.push(floodfill(&mut map, i as i32, j as i32));
        }
    }
    result.sort();
    result.iter().rev().take(3).fold(1, |x, acc| acc * x)
}

fn main() {
    let input = aoc::get_input_from_ini_with_year("9", "2021").unwrap();
    let map: Vec<Vec<_>> = input
        .lines()
        .map(|line| {
            line.chars()
                .map(|digit| digit.to_digit(10).unwrap() as u8)
                .collect()
        })
        .collect();
    println!("Result1: {}", task1(&map));
    println!("Result2: {}", task2(&map));
}

49 Востаннє редагувалося dot (10.12.2021 08:50:55)

Re: Advent of Code

2021#10, Py3.9.5
pairs = {"(": ")", "[": "]", "{": "}", "<": ">"}
illegal = int()
incomplete = list()

for chunk in [line for line in open("input.txt").read().split()]:
    history = list()
    for ch in chunk:
        if ch not in pairs.keys():
            if ch == pairs[history[-1]]:
                history.pop(-1)
            else:
                illegal += {")": 3, "]": 57, "}": 1197, ">": 25137}[ch]
                history.clear()
                break
        else:
            history.append(ch)
    if history:
        points = int()
        for h in history[::-1]:
            points = points * 5 + {")": 1, "]": 2, "}": 3, ">": 4}[pairs[h]]
        incomplete.append(points)

print("Part One:", illegal)
print("Part Two:", sorted(incomplete)[len(incomplete) // 2])

50 Востаннє редагувалося dot (10.12.2021 09:10:08)

Re: Advent of Code

Troxy zapizno, ale vyrjicyv zrobyty osobystu docku percostjy, tam je otcky i zironjky vykonanyx vprav.

Jak potrapyty: [Leaderboard] → [Private Leaderboard] → You can join a private leaderboard by entering its join code here, de vvodyte 728806-963332f3.

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

51

Re: Advent of Code

2021/10.py
import aoc

import os
import unittest

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

def check_brackets(line):
    stack = []
    PAIRS = {'(':')','[':']','{':'}','<':'>'}
    for symb in line:
        if symb in PAIRS:
            stack.append(PAIRS[symb])
        else:
            if not stack or stack.pop()!=symb:
                return (symb, [])
    return ('', stack)

def task1(data: str) -> int:
    VALUES = {'':0,')':3,']':57,'}':1197,'>':25137}
    return sum(VALUES[check_brackets(line)[0]] for line in data.splitlines())
        
def task2(data: str) -> int:
    VALUES = {')':1,']':2,'}':3,'>':4}
    results = []
    for line in data.splitlines():
        result = 0
        symb, closing = check_brackets(line)
        if not symb:
            for brack in reversed(closing):
                result = 5*result + VALUES[brack]
            #print(''.join(closing), result)
            results.append(result)
    results.sort()
    return results[len(results)//2]

class Test(unittest.TestCase):
    def test_brack(self):
        symb, stack = check_brackets('[({(<(())[]>[[{[]{<()<>>')
        self.assertEqual(symb, '')
        self.assertEqual(''.join(reversed(stack)),'}}]])})]')
    def test_task2(self):
        inp = '''[({(<(())[]>[[{[]{<()<>>
[(()[<>])]({[<{<<[]>>(
{([(<{}[<>[]}>{[]{[(<()>
(((({<>}<{<{<>}{[]{[]{}
[[<[([]))<([[{}[[()]]]
[{[{({}]{}}([{[{{{}}([]
{<[[]]>}<{[{[{[]{()[[[]
[<(<(<(<{}))><([]([]()
<{([([[(<>()){}]>(<<{{
<{([{{}}[<[[[<>{}]]]>[]]'''
        self.assertEqual(task2(inp), 288957)

if __name__ == '__main__':
    #unittest.main()
    data = aoc.load_data(NUMBER, verbose=True)
    print('Task1:', task1(data))
    print('Task2:', task2(data))
Подякували: dot1

52 Востаннє редагувалося koala (10.12.2021 10:09:09)

Re: Advent of Code

2021_10.rs
enum Brackets {
    Correct(Vec<char>),
    Incorrect(char),
}

fn check_brackets(line: &str) -> Brackets {
    let mut stack = Vec::new();
    for symbol in line.chars() {
        match symbol {
            '(' => stack.push(')'),
            '[' => stack.push(']'),
            '{' => stack.push('}'),
            '<' => stack.push('>'),
            closing => {
                if symbol != stack.pop().unwrap() {
                    return Brackets::Incorrect(closing);
                }
            }
        }
    }
    Brackets::Correct(stack)
}

fn task1(data: &str) -> usize {
    data.lines()
        .map(|line| match check_brackets(line) {
            Brackets::Incorrect(')') => 3,
            Brackets::Incorrect(']') => 57,
            Brackets::Incorrect('}') => 1197,
            Brackets::Incorrect('>') => 25137,
            _ => 0,
        })
        .sum()
}

fn task2(data: &str) -> usize {
    let mut scores: Vec<_> = data
        .lines()
        .filter_map(|line| match check_brackets(line) {
            Brackets::Correct(rest) => Some(rest.iter().rev().fold(0, |acc, br| {
                5 * acc
                    + match br {
                        ')' => 1,
                        ']' => 2,
                        '}' => 3,
                        '>' => 4,
                        _ => panic!("wrong char in ending!"),
                    }
            })),
            _ => None,
        })
        .collect();
    scores.sort();
    scores[scores.len() / 2]
}

fn main() {
    let input = aoc::get_input_from_ini_with_year("10", "2021").unwrap();
    println!("Result1: {}", task1(&input));
    println!("Result2: {}", task2(&input));
}

День 10. 1. Є некоректні дужкові вирази. Для виразів з неправильними дужками знайти суму значень (табличних) першої некоректної дужки. 2. Для незакінчених виразів обчислити за формулою значення рядка, який би доповнював до коректного, і знайти медіану цих значень.

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

53

Re: Advent of Code

Переробив усю структуру своїх advent_of_code. Раніше у мене була окрема бібліотека для завантаження даних, я створював окремі проєкти для кожної задачі й будував з нуля з усіма залежностями. Тепер у мене майже фреймворк :), окремі задачі лежать по модулях, функції називаються типово, перша побудова нового модуля займає пару секунд проти ~20с раніше. Принагідно звільнилося (з попередніми роками) >5GB старих побудов.

День 11. 1. Є мапа 10х10 восьминогів, що вміють світитися, якщо їхній заряд 10 або більше. На кожному кроці восьминоги накопичують 1 заряд, і якщо спалахують, то додатково заряджають на 1 усіх сусідів, включно з діагональними. Після спалаху заряд стає 0 (зокрема, на цьому кроці не іде зарядка від сусідів). Скільки буде спалахів за 100 кроків? 2. Який перший крок, на якому спалахнуть усі восьминоги одночасно?

year2021/day11.rs
#[derive(Clone)]
pub struct Octopuses {
    map: Vec<Vec<u8>>,
    last_flashed: usize,
}

impl Octopuses {
    fn new(input: &str) -> Octopuses {
        Octopuses {
            map: input.lines()
                      .map(|line|line.chars()
                                     .map(|c|c.to_digit(10).unwrap() as u8)
                                     .collect()
                      ).collect(),
            last_flashed: 0,
        }
    }
    
    fn step(&mut self) -> usize {
        for y in 0..self.map.len() {
            for x in 0..self.map[y].len() {
                self.map[y][x] += 1;
            }
        }
        self.last_flashed = 0;
        loop {
            let mut flashed = 0;
            for y in 0..self.map.len() {
                for x in 0..self.map[y].len() {
                    if self.try_flash(y, x) {
                        flashed += 1;
                    }
                }
            }
            if flashed == 0 {
                break;
            } else {
                self.last_flashed += flashed;
            }
        }
        self.last_flashed
    }

    fn try_flash(&mut self, y: usize, x: usize) -> bool {
        use std::cmp::{min, max};
        if self.map[y][x]<=9 {
            false
        } else {
            self.map[y][x] = 0;
            for dy in max(0, y as i32-1) as usize..min(self.map.len(), y+2) {
                for dx in max(0, x as i32-1) as usize..min(self.map[y].len(), x+2) {
                    if self.map[dy][dx] != 0 {
                        self.map[dy][dx] += 1;
                    }
                }
            }
            true
        }
    }

    fn area(&self) -> usize {
        self.map.len() * self.map[0].len()
    }
}

pub fn parse_input(input: &str) -> Octopuses {
    Octopuses::new(input)
}

pub fn task1(octopuses: &Octopuses) -> usize {
    let mut octopuses = octopuses.clone();
    (0..100).map(|_|octopuses.step()).sum()
}

pub fn task2(octopuses: &Octopuses) -> usize {
    let mut octopuses = octopuses.clone();
    let area = octopuses.area();
    (0..).take_while(|_|octopuses.step() != area).count() + 1
}
Подякували: dot1

54 Востаннє редагувалося dot (11.12.2021 10:39:27)

Re: Advent of Code

2021#11, Py3.9.5
octopuses = [[int(x) for x in l] for l in open("input.txt").read().split()]
grid = {(x, y) for y, l in enumerate(octopuses) for x, _ in enumerate(l)}
count = int()
step = int()

while octopuses != [[0] * 10] * 10:
    octopuses = [[e + 1 for e in l] for l in octopuses]
    fresh = {(x, y) for y, l in enumerate(octopuses) for x, e in enumerate(l) if e > 9}
    exhausted = set()

    sides = lambda x, y: {
        (_x, _y) for _x in (x - 1, x, x + 1) for _y in (y - 1, y, y + 1) if (_x, _y) != (x, y) and (_x, _y) in grid
    }

    while fresh:
        exhausted |= fresh.copy()
        charge = set()
        for f in fresh:
            for x, y in sides(*f):
                octopuses[y][x] += 1
                if octopuses[y][x] > 9 and (x, y) not in exhausted:
                    charge.add((x, y))
        fresh = charge

    for y, l in enumerate(octopuses):
        for x, e in enumerate(l):
            if e > 9:
                octopuses[y][x] = 0
                count += 1

    if step == 99:
        print("Part One", count)

    step += 1

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

55

Re: Advent of Code

Гм. Полагодив решту старого коду, призвів до ладу старі тести і залежності і спробував почати з 2015 року. Перша ж задача (1 грудня 2015, задача 1) - у мене відповідь на 1 менша за правильну, і я не бачу, в чому проблема. Загальна кількість дужок у мене непарна, а відповідь на сайті (яку я колись увів) - парна. WTF?

year2015/day1.rs
pub fn task1(instructions: &str) -> i32 {
    instructions.chars()
                .map(|c|match c {
                    '(' =>  1,
                    ')' => -1,
                    _ => panic!("Wrong input"),
                })
                .sum()
}
Подякували: dot1

56

Re: Advent of Code

А ще для ідіотів є така фігня: https://crates.io/crates/advent-of-code
Ні, я розумію, чому таке написали; але кому може захотітися таким користуватися?

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

57

Re: Advent of Code

Ja davno robyv 2015, a rozbyraty sja ljinky.

2015#1, Py
f = open("input.txt", "r")
floor = pos = 0

for s in f.read():
    if s == '(':
        floor += 1
    else:
        floor -= 1
    pos += 1
    if floor == -1:
        print(f"Basement: {pos}") # Part Two

print(f"Floor: {floor}")  # Part One
f.close()

Pravda, ne zavercyv ctce, spynyv sja na 21, a 19 rozvjazav lyce percu tcastynu.

58

Re: Advent of Code

year2021/day12.rs
use unicode_categories::UnicodeCategories;

type CaveName = String;

#[derive(Clone)]
pub struct CaveNode {
    ways_out: Vec<CaveName>,
    visited: Option<bool>,
}

impl CaveNode {
    fn new(name: &str, neighbor: &str) -> CaveNode {
        CaveNode{
            ways_out: vec![neighbor.to_owned()],
            visited: if name.chars().all(|c|c.is_letter_uppercase())  {
                None
            } else {
                Some(false)
            },
        }
    }
}

type CaveMap = std::collections::HashMap<CaveName, CaveNode>;

pub fn parse_input(input: &str) -> CaveMap {
    let mut map = CaveMap::new();
    for line in input.lines() {
        if let Some(dash) = line.find('-') {
            let left = line[..dash].to_string();
            let right = line[dash+1..].to_string();
            map.entry(left.to_owned())
               .and_modify(|node|node.ways_out.push(right.to_owned()))
               .or_insert(CaveNode::new(&left, &right));
            map.entry(right.to_owned())
               .and_modify(|node|node.ways_out.push(left.to_owned()))
               .or_insert(CaveNode::new(&right, &left));
        }
    }
    map
}

fn count_ways(map: &mut CaveMap, start: &str, end: &str) -> usize {
    if start == end {
        return 1;
    }
    if map[start].visited == Some(true) {
            return 0;
    }
    let start = start.to_string();
    if map[&start].visited.is_some() {
        map.entry(start.to_owned()).and_modify(|node|node.visited = Some(true));
    }
    let result = (0..map[&start].ways_out.len()).map(|i|{
        let name = map[&start].ways_out[i].to_owned();
        count_ways(map, &name, end)
    }).sum();
    if map[&start].visited.is_some() {
        map.entry(start).and_modify(|node|node.visited = Some(false));
    }
    result
}

pub fn task1(map: &CaveMap) -> usize {
    let mut map = map.clone();
    count_ways(&mut map, "start", "end")
}

fn count_ways2(map: &mut CaveMap, start: &str, end: &str, start_visited: bool, small_visited_twice: bool) -> usize {
    if start == end {
        return 1;
    }
    if start=="start" && start_visited {
        return 0;
    }
    let in_visited_small = map[start].visited.unwrap_or(false);
    if small_visited_twice && in_visited_small {
        return 0;
    }
    let start = start.to_string();
    if map[&start].visited.is_some() {
        map.entry(start.to_owned()).and_modify(|node|node.visited = Some(true));
    }
    let result = (0..map[&start].ways_out.len()).map(|i|{
        let name = map[&start].ways_out[i].to_owned();
        count_ways2(map, &name, end, true, small_visited_twice || in_visited_small)
    }).sum();
    if !in_visited_small && map[&start].visited.is_some() {
        map.entry(start).and_modify(|node|node.visited = Some(false));
    }
    result
}

pub fn task2(map: &CaveMap) -> usize {
    let mut map = map.clone();
    count_ways2(&mut map, "start", "end", false, false)
}

День 12. 1. Є граф печери з початком і кінцем. Деякі вузли можна відвідувати кілька разів, решту - лише один. Скільки існує шляхів крізь печеру? 2. Можна один раз відвідати уже відвіданий вузол, який зазвичай не можна відвідувати повторно. Скільки тепер?

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

59

Re: Advent of Code

Боюся, що я на тиждень зникну з форуму. Можливо, завтра вранці ще допишу. А потім же доробляти... ех...

Подякували: 0xDADA11C71

60 Востаннє редагувалося dot (12.12.2021 21:59:31)

Re: Advent of Code

Ne duge ljubju taki rekursivni zadatcky.

2021#12, Py3.9.5
from collections import defaultdict

caves = defaultdict(set)
for one, two in [line.split("-") for line in open("input.txt").read().splitlines()]:
    caves[one].add(two)
    caves[two].add(one)

caves = {i: caves[i] - {"start"} for i in caves}


def visit(twice=True, c="start", visited=set()):
    count = int()
    for cave in caves[c]:
        if cave == "end":
            count += 1
        elif cave.isupper():
            count += visit(twice, cave, visited)
        elif cave not in visited:
            count += visit(twice, cave, visited | {cave})
        elif not twice:
            count += visit(True, cave, visited | {cave})
    return count


print("Part One:", visit())
print("Part Two:", visit(False))