wycy's recent activity

  1. Comment on Does your flag fail? CGP Grey grades the state flags of the USA! in ~design

    wycy
    Link
    Rhode Island’s has a word on it but I think it’s underrated. Should’ve been B-tier at least. It’s a great flag. DC’s should’ve been officially on the roster, and should be S-tier not A-tier. I...

    Rhode Island’s has a word on it but I think it’s underrated. Should’ve been B-tier at least. It’s a great flag.

    DC’s should’ve been officially on the roster, and should be S-tier not A-tier.

    I might’ve also thrown in Chicago’s flag as an honorable mention A or S tier flag.

    1 vote
  2. Comment on The Verge complains about ubiquitous login prompts in ~tech

    wycy
    Link
    On a somewhat related note, because I was involved in the LastPass breach, I've been changing my passwords for everything over the past couple days. In so doing, I've become absolutely astonished...

    On a somewhat related note, because I was involved in the LastPass breach, I've been changing my passwords for everything over the past couple days. In so doing, I've become absolutely astonished at how bad the authentication systems are practically everywhere. I've been using this as an opportunity to select strong, unique passwords everywhere, but SO MANY websites have a maximum character limit of 18 or 20 characters, including many banks. I can't fathom why so many sites would implement a maximum character limit, especially one so low.

    It's absolutely insane to me that my Tildes account has a much stronger password than my Bank of America account.

    5 votes
  3. Comment on Which web browser do you use? in ~tech

    wycy
    Link
    Firefox at home and Chrome at work. I’d use Firefox at work but something about the work network or PC configuration makes it a little screwy, unfortunately.

    Firefox at home and Chrome at work. I’d use Firefox at work but something about the work network or PC configuration makes it a little screwy, unfortunately.

    1 vote
  4. Comment on Day 24: Blizzard Basin in ~comp.advent_of_code

    wycy
    Link
    Rust Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; use std::collections::HashSet; use std::collections::VecDeque; use std::collections::BinaryHeap; use...

    Rust

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    use std::collections::HashSet;
    use std::collections::VecDeque;
    use std::collections::BinaryHeap;
    use std::cmp::Ordering;
    
    use point2d::point2d::Point2D;
    
    #[derive(Debug,Copy,Clone,Hash,Eq,PartialEq)]
    struct Blizzard {
        loc: Point2D,
        dir: BlizzardDirection,
    }
    
    #[derive(Debug,Copy,Clone,Hash,Eq,PartialEq)]
    enum BlizzardDirection {
        Up,
        Down,
        Left,
        Right,
    }
    impl From<char> for BlizzardDirection {
        fn from(c: char) -> Self {
            match c {
                '^' => Self::Up,
                'v' => Self::Down,
                '<' => Self::Left,
                '>' => Self::Right,
                _ => panic!("Unexpected blizzard direction character: {c}"),
            }
        }
    }
    
    #[derive(PartialEq,Eq)]
    struct Path {
        pt: Point2D,
        steps: i64,
    }
    impl Ord for Path {
        fn cmp(&self, other: &Self) -> Ordering {
            other.steps.cmp(&self.steps)
        }
    }
    impl PartialOrd for Path {
        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
            Some(other.steps.cmp(&self.steps))
        }
    }
    
    fn neighbor_addresses(pt: &Point2D) -> Vec<Point2D> {
        vec![Point2D { x: pt.x  , y: pt.y-1 },  // up
             Point2D { x: pt.x+1, y: pt.y   },  // right
             Point2D { x: pt.x  , y: pt.y+1 },  // down
             Point2D { x: pt.x-1, y: pt.y   }]  // left
    }
    
    fn shortest_path(start: &Point2D, waypoints: &VecDeque<Point2D>, xmax: i64, ymax: i64, blizzards: &Vec<Blizzard>) -> i64 {
    
        // Prepare
        let mut ends = waypoints.clone();
        let mut end = ends.pop_front().unwrap();
    
        // Explore
        let mut heap: BinaryHeap<Path> = BinaryHeap::new();
        let mut seen: HashSet<(Point2D,i64)> = HashSet::new();
        heap.push(Path {pt: *start, steps: 0});
        'heap_lp: while let Some(path) = heap.pop() {
    
            // Current properties
            let (pt_now,time_now) = (path.pt,path.steps);
    
            // Determine future properties
            let new_time = time_now + 1;
            let new_blizzards = blizzards_at_time(&blizzards,new_time,xmax,ymax);
    
            // Consider waiting on this square IF blizzard will not be here next step
            if new_blizzards.iter().filter(|b| b.loc == pt_now).count() == 0 {
                if seen.insert((pt_now,new_time)) {
                    heap.push(Path { pt: pt_now, steps: new_time });
                }
            }
    
            'n_loop: for n in neighbor_addresses(&pt_now) {
    
                // Check point is a waypoint, and check for next waypoint
                if n == end {
                    if ends.len() > 0 {
                        end = ends.pop_front().unwrap();
                        heap.clear();
                        seen.clear();
                        heap.push(Path {pt: n, steps: new_time });
                        continue 'heap_lp;
                    } else {
                        return new_time;
                    }
               }
    
                // Check point is inside the walls
                if n.x <= 0 || n.x > xmax { continue 'n_loop; }
                if n.y <= 0 || n.y > ymax { continue 'n_loop; }
    
                // Check point will not be an active blizzard
                if new_blizzards.iter().filter(|b| b.loc == n).count() == 0 {
                    if seen.insert((n,new_time)) {
                        heap.push(Path { pt: n, steps: new_time });
                    }
                }
            }
    
        }
        i64::MAX
    }
    
    fn blizzards_at_time(blizzards: &Vec<Blizzard>, time: i64, xmax: i64, ymax: i64) -> Vec<Blizzard> {
        let mut adj_blizzards = blizzards.clone();
        for mut blizz in &mut adj_blizzards {
            match blizz.dir {
                BlizzardDirection::Up    => { blizz.loc.y = 1 + ((blizz.loc.y - 1 - time).rem_euclid(ymax)); },
                BlizzardDirection::Down  => { blizz.loc.y = 1 + ((blizz.loc.y - 1 + time).rem_euclid(ymax)); },
                BlizzardDirection::Left  => { blizz.loc.x = 1 + ((blizz.loc.x - 1 - time).rem_euclid(xmax)); },
                BlizzardDirection::Right => { blizz.loc.x = 1 + ((blizz.loc.x - 1 + time).rem_euclid(xmax)); },
            }
        }
        adj_blizzards
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Build blizzard map
        let mut blizzards: Vec<Blizzard> = Vec::new();
        let mut start = Point2D { x: 0, y: 0 };
        let mut end = start;
        for (y,line) in input.iter().enumerate() {
            for (x,ch) in line.chars().enumerate() {
                let pt = Point2D { x: x as i64, y: y as i64 };
                if ch == '.' && y == 0 { start = pt; }
                if ch == '.' && y == input.len()-1 { end = pt; }
                match ch {
                    '>' | '<' | '^' | 'v' => blizzards.push(Blizzard { loc: pt, dir: BlizzardDirection::from(ch) }),
                    '.' | '#' => {},
                    _ => panic!("Unexpected character: {ch}"),
                }
            }
        }
        let (xmax,ymax) = (end.x,end.y-1);
    
        // Part 1
        let mut waypoints: VecDeque<Point2D> = VecDeque::new();
        waypoints.push_front(end);
        let part1 = shortest_path(&start,&waypoints,xmax,ymax,&blizzards);
        println!("Part 1: {part1}"); // 334
    
        // Part 2
        let mut waypoints: VecDeque<Point2D> = VecDeque::new();
        waypoints.push_front(end);
        waypoints.push_front(start);
        waypoints.push_front(end);
        let part2 = shortest_path(&start,&waypoints,xmax,ymax,&blizzards);
        println!("Part 2: {part2}"); // 934
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    1 vote
  5. Comment on Day 20: Grove Positioning System in ~comp.advent_of_code

    wycy
    (edited )
    Link
    I have a solution for part 1 (sample + my input) but just the sample for part 2. Ugh. I have no idea what could be wrong given it gets the sample perfectly. Edit: Figured out the problem, woohoo....

    I have a solution for part 1 (sample + my input) but just the sample for part 2. Ugh. I have no idea what could be wrong given it gets the sample perfectly.

    Edit: Figured out the problem, woohoo. The problem was:

    SpoilerI had handling to skip doing any mixing when the actual value was 0, but didn't properly handle this when the *effective* number (when using mod) was 0.
    1 vote
  6. Comment on Day 21: Monkey Math in ~comp.advent_of_code

    wycy
    Link
    Python Python #!/usr/bin/env python3 import sys from typing import List from typing import Tuple import re import sympy as sy def get_data(filename: str) -> List[str]: with open(filename,'r') as...

    Python

    Python
    #!/usr/bin/env python3
    import sys
    from typing import List
    from typing import Tuple
    import re
    import sympy as sy
    
    def get_data(filename: str) -> List[str]:
        with open(filename,'r') as f:
            lines = f.read().splitlines()
        return lines
    
    equation_pattern = re.compile("([\w]{4}): ([\w]{4}) (.) ([\w]{4})")
    constant_pattern = re.compile("([\w]{4}): ([\d]+)")
    generic_pattern  = re.compile("([\w]{4}): (.+)")
    
    class Equation(object):
        def __init__(self,line):
            matches = re.search(equation_pattern, line)
            self.name  = matches[1]
            self.left  = matches[2]
            self.right = matches[4]
            self.op    = matches[3]
    
    def part1():
    
        # Input
        input = get_data(sys.argv[1])
        monkeys = {}
        equations = []
        for line in input:
            if matches := re.search(constant_pattern,line):
                monkeys[matches[1]] = int(matches[2])
            else:
                equations.append(Equation(line))
    
        # Solve
        while len(equations) > 0:
            for eqn in equations:
                if eqn.left in monkeys and eqn.right in monkeys:
                    monkeys[eqn.name] = int(eval(f"{monkeys[eqn.left]} {eqn.op} {monkeys[eqn.right]}"))
            equations = [eqn for eqn in equations if eqn.name not in monkeys]
    
        print(f"Part 1: {monkeys['root']}") # 152479825094094
    
    def part2():
    
        # Input
        input = get_data(sys.argv[1])
        monkeys = {}
        for line in input:
            matches = re.search(generic_pattern,line)
            monkeys[matches[1]] = matches[2]
    
        # Build equation
        eq_l = monkeys['root'].split()[0]
        eq_r = monkeys['root'].split()[2]
        monkeys.pop('root')
        monkeys.pop('humn')
        while len(monkeys) > 0:
            for word in eq_l.split():
                if word in monkeys:
                    eq_l = eq_l.replace(word,'( '+monkeys[word]+' )')
                    monkeys.pop(word)
            for word in eq_r.split():
                if word in monkeys:
                    eq_r = eq_r.replace(word,'( '+monkeys[word]+' )')
                    monkeys.pop(word)
    
        # Solve
        eq_l = sy.parse_expr(eq_l)
        eq_r = sy.parse_expr(eq_r)
        x = sy.symbols('humn')
        part2 = sy.solve(eq_l - eq_r)[0]
        print(f"Part 2: {part2}") # 3360561285172
    
    if __name__ == "__main__":
        part1()
        part2()
    
    1 vote
  7. Comment on Day 18: Boiling Boulder in ~comp.advent_of_code

    wycy
    Link
    Rust I'm way behind. I need to get back to day 16/17 now. Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; use std::collections::HashMap; use...

    Rust

    I'm way behind. I need to get back to day 16/17 now.

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    use std::collections::HashMap;
    
    use point3d::point3d::Point3D;
    
    #[derive(Eq,PartialEq)]
    enum Voxel {
        Lava,
        Air,
    }
    
    fn neighbors(pt: &Point3D) -> Vec<Point3D> {
        vec![
            Point3D { x: pt.x+1, y: pt.y  , z: pt.z   },
            Point3D { x: pt.x-1, y: pt.y  , z: pt.z   },
            Point3D { x: pt.x  , y: pt.y+1, z: pt.z   },
            Point3D { x: pt.x  , y: pt.y-1, z: pt.z   },
            Point3D { x: pt.x  , y: pt.y  , z: pt.z+1 },
            Point3D { x: pt.x  , y: pt.y  , z: pt.z-1 },
        ]
    }
    
    fn coords_voxel(s: &str) -> (Point3D,Voxel) {
        let coords: Vec<_> = s.split(",").collect();
        let (x,y,z) = (coords[0].parse().unwrap(), coords[1].parse().unwrap(), coords[2].parse().unwrap());
        (Point3D { x: x, y: y, z: z }, Voxel::Lava)
    }
    
    fn flood_fill(pt: &Point3D, map: &mut HashMap<Point3D,Voxel>, xrange: (i64,i64), yrange: (i64,i64), zrange: (i64,i64)) {
        let (xmin,xmax) = xrange;
        let (ymin,ymax) = yrange;
        let (zmin,zmax) = zrange;
        if pt.x == xmin || pt.x == xmax || pt.y == ymin || pt.y == ymax || pt.z == zmin || pt.z == zmax { return }
        match &map.get(&pt) {
            Some(Voxel::Lava) | Some(Voxel::Air) => return,
            _ => &map.insert(*pt,Voxel::Air),
        };
        flood_fill(&Point3D { x: pt.x + 1, y: pt.y    , z: pt.z    }, map, xrange, yrange, zrange);
        flood_fill(&Point3D { x: pt.x - 1, y: pt.y    , z: pt.z    }, map, xrange, yrange, zrange);
        flood_fill(&Point3D { x: pt.x    , y: pt.y + 1, z: pt.z    }, map, xrange, yrange, zrange);
        flood_fill(&Point3D { x: pt.x    , y: pt.y - 1, z: pt.z    }, map, xrange, yrange, zrange);
        flood_fill(&Point3D { x: pt.x    , y: pt.y    , z: pt.z + 1}, map, xrange, yrange, zrange);
        flood_fill(&Point3D { x: pt.x    , y: pt.y    , z: pt.z - 1}, map, xrange, yrange, zrange);
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Build map
        let mut voxels: HashMap<Point3D,Voxel> = input.iter().map(|s| coords_voxel(s)).collect();
    
        // Part 1
        let part1 = &voxels.len()*6 
                  - &voxels.iter().map(|(pt,_)| {
                        neighbors(&pt).iter().map(|n| match &voxels.get(&n) {
                            Some(_) => 1, None => 0,
                        }).sum::<usize>()
                  }).sum::<usize>();
        println!("Part 1: {part1}"); // 3610
    
        // Determine maximum extents
        let (xmin,xmax) = (&voxels.keys().map(|pt| pt.x).min().unwrap() - 2, &voxels.keys().map(|pt| pt.x).max().unwrap() + 2);
        let (ymin,ymax) = (&voxels.keys().map(|pt| pt.y).min().unwrap() - 2, &voxels.keys().map(|pt| pt.y).max().unwrap() + 2);
        let (zmin,zmax) = (&voxels.keys().map(|pt| pt.z).min().unwrap() - 2, &voxels.keys().map(|pt| pt.z).max().unwrap() + 2);
    
        // Flood the outer area with air
        flood_fill(&Point3D { x: xmin+1, y: ymin+1, z: zmin+1 },&mut voxels, (xmin,xmax), (ymin,ymax), (zmin,zmax));
    
        // Calculate surface area
        let part2 = &voxels
            .iter()
            .filter(|(_,v)| **v == Voxel::Lava)
            .map(|(pt,_)| {
                neighbors(&pt).iter().map(|n| match &voxels.get(&n) {
                    Some(Voxel::Air) => 1, _ => 0,
                }).sum::<usize>()
            }).sum::<usize>();
        println!("Part 2: {part2}"); // 2082
    
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    1 vote
  8. Comment on Day 14: Regolith Reservoir in ~comp.advent_of_code

    wycy
    (edited )
    Link
    Rust I enjoyed this one. I was a bit afraid due to the problems reference to 2018 day 17, which I remember finding incredibly hard at the time, but this one wasn’t as bad. Rust use std::env; use...

    Rust

    I enjoyed this one. I was a bit afraid due to the problems reference to 2018 day 17, which I remember finding incredibly hard at the time, but this one wasn’t as bad.

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    use std::collections::HashMap;
    
    use point2d::point2d::Point2D;
    
    const ORIGIN_X: i64 = 500;
    const ORIGIN_Y: i64 = 0;
    const ORIGIN: Point2D = Point2D { x: ORIGIN_X, y: ORIGIN_Y };
    
    enum MapType {
        Origin,
        Rock,
        Sand,
    }
    
    fn can_move(sand_map: &HashMap<Point2D,MapType>, pt: &Point2D) -> bool {
        match sand_map.get(&pt) {
            None => true,
            Some(_) => false,
        }
    }
    
    fn drop_sand(sand_map: &mut HashMap<Point2D,MapType>, abyss: i64, part1: bool) -> bool {
    
        let mut new_pt = Point2D { x: ORIGIN_X, y: ORIGIN_Y };
        loop {
    
            // Assess movements
            let try_down  = Point2D { x: new_pt.x,   y: new_pt.y+1 };
            let try_left  = Point2D { x: new_pt.x-1, y: new_pt.y+1 };
            let try_right = Point2D { x: new_pt.x+1, y: new_pt.y+1 };
    
            let can_down  = can_move(&sand_map, &try_down);
            let can_left  = can_move(&sand_map, &try_left);
            let can_right = can_move(&sand_map, &try_right);
    
            // Assign movements
            if can_down {
                new_pt = try_down;
                if part1 && new_pt.y + 1 == abyss { return false; } // Fell to the abyss
            } else if can_left {
                new_pt = try_left;
            } else if can_right {
                new_pt = try_right;
            } else {
                if !part1 && new_pt == ORIGIN { return false; } // Blocking origin
                sand_map.insert(new_pt,MapType::Sand);
                return true; // At rest
            }
        }
    }
    
    fn build_map(input: &Vec<String>) -> HashMap<Point2D,MapType> {
        let mut sand_map: HashMap<Point2D,MapType> = HashMap::new();
        let origin = Point2D { x: ORIGIN_X, y: ORIGIN_Y };
        sand_map.insert(origin, MapType::Origin);
        for line in input {
            let segments: Vec<_> = line.split("->").collect();
            for segment in segments.windows(2) {
                let p1: Vec<_> = segment[0].split(",").map(|v| v.trim().parse::<i64>().unwrap()).collect();
                let p2: Vec<_> = segment[1].split(",").map(|v| v.trim().parse::<i64>().unwrap()).collect();
                let (x1,y1) = (p1[0], p1[1]);
                let (x2,y2) = (p2[0], p2[1]);
                let xrange = if x2 > x1 { x1..=x2 } else { x2..=x1 };
                let yrange = if y2 > y1 { y1..=y2 } else { y2..=y1 };
                for x in xrange.clone() {
                    for y in yrange.clone() {
                        sand_map.insert(Point2D { x: x, y: y }, MapType::Rock);
                    }
                }
            }
        }
        sand_map
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Part 1
        let mut sand_map = build_map(&input);
    
        // Identify the extent of the abyss
        let abyss = &sand_map.keys().map(|&pt| pt.y).max().unwrap() + 2;
    
        // Simulate sand
        'sand_lp: for sand in 0.. {
            let result = drop_sand(&mut sand_map,abyss,true);
            if !result { println!("Part 1: {sand}"); break 'sand_lp; } // 1061
        }
    
        // Part 2 (reset map)
        let mut sand_map = build_map(&input);
    
        // Build floor
        for x in 0..=1000 {
            sand_map.insert(Point2D { x: x, y: abyss },MapType::Rock);
        }
    
        'sand_lp2: for sand in 1.. {
            let result = drop_sand(&mut sand_map,abyss,false);
            if !result { println!("Part 2: {sand}"); break 'sand_lp2; } //  25055
        }
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    1 vote
  9. Comment on Day 13: Distress Signal in ~comp.advent_of_code

    wycy
    Link
    Python Python #!/usr/bin/env python3 import sys from typing import List from typing import Tuple from itertools import zip_longest from functools import cmp_to_key RAN_OUT = -9999999999 def...

    Python

    Python
    #!/usr/bin/env python3
    import sys
    from typing import List
    from typing import Tuple
    from itertools import zip_longest
    from functools import cmp_to_key
    
    RAN_OUT = -9999999999
    
    def get_data1(filename: str) -> List[str]:
        with open(filename,'r') as f:
            lines = f.read()
        lines = lines.split("\n\n")
        return lines
    
    def get_data2(filename: str) -> List[str]:
        with open(filename,'r') as f:
            lines = f.read().splitlines()
        return [l for l in lines if l != '']
    
    def compare_packets(p1,p2) -> int:
        for v1,v2 in zip_longest(p1,p2,fillvalue=RAN_OUT):
            if v1 == RAN_OUT: return -1
            if v2 == RAN_OUT: return +1
            if type(v1) == int and type(v2) == int:
                if v1 < v2: return -1
                if v1 > v2: return +1
            elif type(v1) == list and type(v2) == list:
                outcome = compare_packets(v1,v2)
                if outcome is not None: return outcome
            elif type(v1) == list and type(v2) == int:
                outcome = compare_packets(v1,[v2])
                if outcome is not None: return outcome
            elif type(v1) == int and type(v2) == list:
                outcome = compare_packets([v1],v2)
                if outcome is not None: return outcome
            else:
                quit(f"Error: Unknown types: v1={type(v1)}, v2={type(v2)}")
    
    def main():
    
        # Part 1
        input = get_data1(sys.argv[1])
        part1 = 0
        for ix,pair in enumerate(input):
            parts = pair.split("\n")
            first,second = parts[0], parts[1]
    
            first  = eval(first)
            second = eval(second)
    
            if compare_packets(first,second) == -1:
                part1 += ix + 1
     
        print(f"Part 1: {part1}") # 5760
    
        # Part 2
        input = get_data2(sys.argv[1])
        input.append("[[2]]")
        input.append("[[6]]")
        packets = [eval(p) for p in input]
        packets = sorted(packets, key=cmp_to_key(compare_packets))
        
        part2 = 1
        for ix,packet in enumerate(packets):
            if packet == [[2]]: part2 *= (ix+1)
            if packet == [[6]]: part2 *= (ix+1)
        print(f"Part 2: {part2}") # 26670
    
    
    if __name__=="__main__":
        main()
    
    1 vote
  10. Comment on Day 12: Hill Climbing Algorithm in ~comp.advent_of_code

    wycy
    Link
    Rust Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; use std::collections::{HashMap,HashSet}; use std::collections::BinaryHeap; use std::cmp::Ordering; use...

    Rust

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    use std::collections::{HashMap,HashSet};
    
    use std::collections::BinaryHeap;
    use std::cmp::Ordering;
    
    use point2d::point2d::Point2D;
    
    fn elevation(ch: char) -> i64 {
        match ch {
            'a'..='z' => { ch as i64 - 96 },
            'S'=> { 1  },
            'E'=> { 26 },
            _ => panic!("Unknown character: {}", ch),
        }
    }
    
    #[derive(PartialEq,Eq)]
    struct Path {
        pt: Point2D,
        steps: i64,
    }
    impl Ord for Path {
        fn cmp(&self, other: &Self) -> Ordering {
            other.steps.cmp(&self.steps)
        }
    }
    impl PartialOrd for Path {
        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
            Some(other.steps.cmp(&self.steps))
        }
    }
    
    fn neighbor_addresses(pt: &Point2D) -> Vec<Point2D> {
        vec![Point2D { x: pt.x  , y: pt.y-1 },  // up
             Point2D { x: pt.x+1, y: pt.y   },  // right
             Point2D { x: pt.x  , y: pt.y+1 },  // down
             Point2D { x: pt.x-1, y: pt.y   }]  // left
    }
    
    fn shortest_path(map: &HashMap<Point2D,i64>, start: Point2D, end: Point2D) -> i64 {
    
        // Explore
        let mut visited: HashSet<Point2D> = HashSet::new();
        let mut heap: BinaryHeap<Path> = BinaryHeap::new();
        heap.push(Path{pt: start, steps: 0});
        while heap.len() > 0 {
    
            let path = heap.pop().unwrap();
            let (now,current_steps) = (path.pt, path.steps);
            let current_elevation = map.get(&now).unwrap();
    
            'neighbor_loop: for neighbor in neighbor_addresses(&now) {
    
                // Determine if visitable
                let new_elev = match map.get(&neighbor) {
                    Some(e) => e,
                    None => { continue 'neighbor_loop; }, // off the map
                };
                if new_elev > &(current_elevation + 1) { continue 'neighbor_loop; }
    
                // Check already visited
                if visited.get(&neighbor).is_some() { continue 'neighbor_loop; }
                visited.insert(neighbor);
    
                // New steps
                let new_steps = current_steps + 1;
    
                // Found the end
                if neighbor == end { return new_steps; }
                heap.push(Path{pt: neighbor, steps: new_steps});
            }
        }
        i64::MAX
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Build map
        let mut elev_map: HashMap<Point2D,i64> = HashMap::new();
        let mut start = Point2D {x: 0, y: 0 };
        let mut end = Point2D {x: 0, y: 0 };
        let mut possible_starts: Vec<Point2D> = Vec::new();
        for (y,line) in input.iter().enumerate() {
            for (x,elev_ch) in line.chars().enumerate() {
                let pt = Point2D { x: x as i64, y: y as i64 };
                elev_map.insert(pt, elevation(elev_ch));
                // Identify start and end points
                match elev_ch {
                    'a' => possible_starts.push(pt),
                    'S' => start = pt,
                    'E' => end   = pt,
                    _ => {},
                }
            }
        }
    
        // Part 1
        let part1 = shortest_path(&elev_map.clone(), start, end);
        println!("Part 1: {part1}"); // 380
    
        // Part 2
        let best = possible_starts.iter().map(|s| shortest_path(&elev_map,*s,end)).min().unwrap();
        println!("Part 2: {best}"); // 375
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    Point2D
    //#[macro_use]
    //extern crate text_io;
    
    pub mod point2d {
        use std::ops::{Add,AddAssign};
        use std::iter::Sum;
        extern crate num_complex;
        use num_complex::Complex;
    
        #[derive(Debug, Copy, Clone, Hash)]
        pub struct Point2D {
            pub x: i64,
            pub y: i64,
        }
    
        impl Point2D {
            pub fn new(x: i64, y: i64) -> Self {
                Self {
                    x: x, y: y,
                }
            }
            pub fn zeros() -> Self {
                Self {
                    x: 0, y: 0,
                }
            }
            pub fn from_complex(cmplx: Complex<i64>) -> Self {
                Self {
                    x: cmplx.re, y: cmplx.im,
                }
            }
            pub fn as_complex(self) -> Complex<i64> {
                Complex::new(self.x, self.y)
            }
            pub fn as_tuple(self) -> (i64,i64) {
                (self.x, self.y)
            }
        }
        impl Add for Point2D {
            type Output = Self;
            fn add(self, other: Self) -> Self {
                Self {
                    x: self.x + other.x,
                    y: self.y + other.y,
                }
            }
        }
        impl AddAssign for Point2D {
            fn add_assign(&mut self, other: Self) {
                *self = Self {
                    x: self.x + other.x,
                    y: self.y + other.y,
                };
            }
        }
        impl PartialEq for Point2D {
            fn eq(&self, other: &Self) -> bool {
                self.x == other.x && self.y == other.y
            }
        }
        impl Eq for Point2D {}
    
        /*impl<'a> Sum<&'a Self> for Point2D {
            fn sum<I>(iter: I) -> Self
            where
                I: Iterator<Item = &'a Self>,
            {
                iter.fold(Self { x: 0, y: 0 }, |a, b| Self {
                    x: a.x + b.x,
                    y: a.y + b.y,
                })
            }
        }*/
    
        impl Sum for Point2D {
            fn sum<I>(iter: I) -> Self
            where
                I: Iterator<Item = Self>,
            {
                iter.fold(Self { x: 0, y: 0 }, |a, b| Self {
                    x: a.x + b.x,
                    y: a.y + b.y,
                })
            }
        }
    }
    
    2 votes
  11. Comment on Day 11: Monkey in the Middle in ~comp.advent_of_code

    wycy
    Link
    Rust Rust use std::env; use std::io::{self}; extern crate regex; use regex::Regex; #[derive(Debug)] enum Operation { Add(i64), Multiply(i64), Square, } #[derive(Debug)] struct Monkey { pub items:...

    Rust

    Rust
    use std::env;
    use std::io::{self};
    
    extern crate regex;
    use regex::Regex;
    
    #[derive(Debug)]
    enum Operation {
        Add(i64),
        Multiply(i64),
        Square,
    }
    #[derive(Debug)]
    struct Monkey {
        pub items: Vec<i64>,
        pub operation: Operation,
        pub test_value: i64,
        pub target_true: i64,
        pub target_false: i64,
        pub inspections: i64,
    }
    impl From<&str> for Monkey {
        fn from(s: &str) -> Self {
            let lines: Vec<_> = s.split("\n").collect();
    
            // Regexes
            let re_starting  = Regex::new(r"Starting items: ([0-9 ,]+)").unwrap();
            let re_operation = Regex::new(r"Operation: new = old (\+|\*) (old|[\d]+)").unwrap();
            let re_test      = Regex::new(r"Test: divisible by ([\d]+)").unwrap();
            let re_true      = Regex::new(r"If true: throw to monkey ([\d]+)").unwrap();
            let re_false     = Regex::new(r"If false: throw to monkey ([\d]+)").unwrap();
    
            // Captures
            let starting_captures = re_starting.captures(&lines[1]).unwrap();
            let operate_captures = re_operation.captures(&lines[2]).unwrap();
            let test_captures = re_test.captures(&lines[3]).unwrap();
            let true_captures = re_true.captures(&lines[4]).unwrap();
            let false_captures = re_false.captures(&lines[5]).unwrap();
    
            // Data extractions
            let starting_items: Vec<i64> = starting_captures[1].split(",").map(|x| x.trim().parse().unwrap()).collect();
            let operation = if &operate_captures[2] == "old" {
                Operation::Square
            } else {
                match &operate_captures[1] {
                    "+" => Operation::Add     (operate_captures[2].parse().unwrap()),
                    "*" => Operation::Multiply(operate_captures[2].parse().unwrap()),
                    other => panic!("Error: Unknown operation {other}"),
                }
            };
    
            let test_value: i64 = test_captures[1].parse().unwrap();
            let target_true  = true_captures[1].parse().unwrap();
            let target_false = false_captures[1].parse().unwrap();
    
            // Construct object
            Self {
                items: starting_items,
                operation: operation,
                test_value: test_value,
                target_true: target_true,
                target_false: target_false,
                inspections: 0,
            }
        }
    }
    
    fn solve(input: &str, part2: bool) -> io::Result<()> {
        let input_str = std::fs::read_to_string(input).unwrap();
        let input_str = input_str.trim();
        let input: Vec<_> = input_str.split("\n\n").collect();
    
        // Initialize
        let mut monkeys: Vec<_> = input.iter().map(|x| Monkey::from(*x)).collect();
        let num_monkeys = &monkeys.len();
        let lcm = &monkeys.iter().map(|x| x.test_value).product::<i64>();
    
        // Turns
        let lcm = &monkeys.iter().map(|x| x.test_value).product::<i64>();
        let rounds = if part2 { 10_000 } else { 20 };
        for round in 0..rounds {
            for m_id in 0..*num_monkeys {
                for item_id in 0..monkeys[m_id].items.len() {
                    monkeys[m_id].inspections += 1;
                    match monkeys[m_id].operation {
                        Operation::Add(v) => monkeys[m_id].items[item_id] += v,
                        Operation::Multiply(v) => monkeys[m_id].items[item_id] *= v,
                        Operation::Square => monkeys[m_id].items[item_id] *= monkeys[m_id].items[item_id],
                    }
                    if !part2 { monkeys[m_id].items[item_id] /= 3; }
                    let target_monkey = if monkeys[m_id].items[item_id] % monkeys[m_id].test_value == 0 {
                        monkeys[m_id].target_true
                    } else {
                        monkeys[m_id].target_false
                    };
                    if part2 { monkeys[m_id].items[item_id] %= lcm; }
                    let item_transferred = monkeys[m_id].items[item_id];
                    monkeys[target_monkey as usize].items.push(item_transferred);
                }
                monkeys[m_id].items.clear();
            }
        }
    
        // Monkey Business
        monkeys.sort_by(|a, b| b.inspections.cmp(&a.inspections));
        let top_two: i64 = monkeys.iter().take(2).map(|x| x.inspections).product();
        let part = if part2 { 2 } else { 1 };
        println!("Part {part}: {top_two}");
        // Part 1: 120384
        // Part 2: 32059801242
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename,false).unwrap();
        solve(&filename,true).unwrap();
    }
    
    1 vote
  12. Comment on Day 10: Cathode-Ray Tube in ~comp.advent_of_code

    wycy
    Link
    Rust Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; enum Status { Waiting, Processing, Done, } enum InstKind { Noop, Addx, } struct Instruction { kind: InstKind,...

    Rust

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    
    enum Status {
        Waiting,
        Processing,
        Done,
    }
    enum InstKind {
        Noop,
        Addx,
    }
    struct Instruction {
        kind: InstKind,
        value: Option<i64>,
        status: Status,
    }
    impl Instruction {
        fn from(s: &String) -> Self {
            let parts: Vec<_> = s.split(" ").collect();
            match parts[0] {
                "noop" => Self { kind: InstKind::Noop, value: None, status: Status::Waiting },
                "addx" => Self { kind: InstKind::Addx, value: Some(parts[1].parse().unwrap()), status: Status::Waiting },
                other => panic!("Unknown instruction: {other}"),
            }
        }
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Initializations
        let mut instructions: Vec<_> = input.iter().map(Instruction::from).collect();
        let mut reg: i64 = 1;
        let mut instr: usize = 0;
        let mut part1: i64 = 0;
        const PIX_PER_ROW: usize = 40;
        const NUM_ROWS: usize = 6;
        let mut screen = [[false; PIX_PER_ROW]; NUM_ROWS];
    
        // Processing
        'main_lp: for cyc in 1.. {
            if instr >= instructions.len() { break 'main_lp; }
            let ins = &instructions[instr];
    
            // Gather signal strengths
            match cyc {
                20 | 60 | 100 | 140 | 180 | 220 => {
                    println!("Cycle {cyc}, reg: {reg}, signal={}",cyc*reg);
                    part1 += cyc * reg;
                },
                _ => {},
            }
    
            // Handle instruction
            match ins.kind {
                InstKind::Noop => { instr += 1 },
                InstKind::Addx => {
                    match ins.status {
                        Status::Waiting => {
                            instructions[instr].status = Status::Processing;
                            },
                        Status::Processing => {
                            reg += ins.value.unwrap();
                            instructions[instr].status = Status::Done;
                            instr += 1; 
                            },
                        _ => panic!("Shouldn't be able to get here"),
                    }
                },
            }
    
            // Render image
            let row: usize = cyc as usize / PIX_PER_ROW;
            let col: usize = cyc as usize % PIX_PER_ROW;
            let coli = col as i64;
            let (px0,px1,px2) = (reg - 1, reg, reg + 1);
            if coli == px0 || coli == px1 || coli == px2 { screen[row][col] = true; }
        }
        println!("Part 1: {part1}"); // 13480
    
        // Part 2 Output: EGJBGCFK
        for y in 0..NUM_ROWS {
            for x in 0..PIX_PER_ROW {
                if screen[y][x] { print!("█"); } else { print!(" "); }
            }
            println!();
        }
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    1 vote
  13. Comment on Day 8: Treetop Tree House in ~comp.advent_of_code

    wycy
    Link
    Rust I'm not very happy with my code today. Feels like I violated DRY a lot. Ah well. Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; const DIM: usize = 99; //...

    Rust

    I'm not very happy with my code today. Feels like I violated DRY a lot. Ah well.

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    
    const DIM: usize = 99; // Sample=5
    
    fn visible_trees(tree_map: &Vec<Vec<u32>>) -> usize {
        let mut num_visible = 0;
        for y in 0..DIM {
            'srch: for x in 0..DIM {
                // Handle edges
                if x == 0 || y == 0 || x == DIM-1 || y == DIM-1 { num_visible += 1; continue 'srch; }
                let v = tree_map[x][y];
                // Scan -y
                let mut visible = true;
                'ym: for y0 in 0..y {
                    if tree_map[x][y0] >= v { visible = false; break 'ym; }
                }
                if visible { num_visible += 1; continue 'srch; }
                // Scan +y
                let mut visible = true;
                'yp: for y0 in y+1..DIM {
                    if tree_map[x][y0] >= v { visible = false; break 'yp; }
                }
                if visible { num_visible += 1; continue 'srch; }
                // Scan -x
                let mut visible = true;
                'xm: for x0 in 0..x {
                    if tree_map[x0][y] >= v { visible = false; break 'xm; }
                }
                if visible { num_visible += 1; continue 'srch; }
                // Scan +x
                let mut visible = true;
                'xp: for x0 in x+1..DIM {
                    if tree_map[x0][y] >= v { visible = false; break 'xp; }
                }
                if visible { num_visible += 1; continue 'srch; }
            }
        }
        num_visible
    }
    
    fn scenic_score(tree_map: &Vec<Vec<u32>>) -> usize {
        let mut max_scenic = 0;
        for y in 0..DIM {
            'srch: for x in 0..DIM {
                // Handle edges
                if x == 0 || y == 0 || x == DIM-1 || y == DIM-1 { continue 'srch; } // Zero scenic
                let v = tree_map[x][y];
                // Scan -y
                let mut dist_ym = 0;
                'ym: for y0 in (0..y).rev() {
                    if tree_map[x][y0] <= v { dist_ym += 1; }
                    if tree_map[x][y0] >= v { break 'ym; }
                }
                // Scan +y
                let mut dist_yp = 0;
                'yp: for y0 in y+1..DIM {
                    if tree_map[x][y0] <= v { dist_yp += 1; }
                    if tree_map[x][y0] >= v { break 'yp; }
                }
                // Scan -x
                let mut dist_xm = 0;
                'xm: for x0 in (0..x).rev() {
                    if tree_map[x0][y] <= v { dist_xm += 1; }
                    if tree_map[x0][y] >= v { break 'xm; }
                }
                // Scan +x
                let mut dist_xp = 0;
                'xp: for x0 in x+1..DIM {
                    if tree_map[x0][y] <= v { dist_xp += 1; }
                    if tree_map[x0][y] >= v { break 'xp; }
                }
                max_scenic = std::cmp::max(max_scenic, dist_ym * dist_yp * dist_xp * dist_xm);
            }
        }
        max_scenic
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Build tree map
        let mut tree_map = vec![vec![0; DIM]; DIM];
        for (y,line) in input.iter().enumerate() {
            for (x,c) in line.chars().enumerate() {
                tree_map[x][y] = c.to_digit(10).unwrap();
            }
        }
    
        // Outputs
        println!("Part 1: {}",visible_trees(&tree_map)); // 1693
        println!("Part 2: {}",scenic_score(&tree_map)); // 422059
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    2 votes
  14. Comment on Day 6: Tuning Trouble in ~comp.advent_of_code

    wycy
    Link
    Rust use std::env; use std::io::{self}; use std::collections::HashSet; fn packet_start(s: &str) -> usize { const MARKER_SIZE: usize = 4; let mut start = MARKER_SIZE; for chs in...
    Rust
    use std::env;
    use std::io::{self};
    use std::collections::HashSet;
    
    fn packet_start(s: &str) -> usize {
        const MARKER_SIZE: usize = 4;
        let mut start = MARKER_SIZE;
        for chs in s.chars().collect::<Vec<_>>().windows(MARKER_SIZE) {
            let (a,b,c,d) = (chs[0],chs[1],chs[2],chs[3]);
            if a != b && a != c && a != d && b != c && b != d && c != d { break }
            else { start += 1; }
        }
        start
    }
    
    fn message_start(s: &str) -> usize {
        const MARKER_SIZE: usize = 14;
        let mut start = MARKER_SIZE;
        'outer: for chs in s.chars().collect::<Vec<_>>().windows(MARKER_SIZE) {
            start += 1;
            let mut char_set: HashSet<char> = HashSet::new();
            for ch in chs {
                if char_set.contains(ch) { continue 'outer; }
                else { char_set.insert(*ch); }
            }
            break;
        }
        start - 1
    }
    
    fn solve(input: &str) -> io::Result<()> {
        // Input
        let input_str = std::fs::read_to_string(input).unwrap();
        let input_str = input_str.trim();
    
        // Output
        println!("Part 1: {}",packet_start(input_str)); // 1578
        println!("Part 2: {}",message_start(input_str)); // 2178
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    2 votes
  15. Comment on Day 5: Supply Stacks in ~comp.advent_of_code

    wycy
    Link Parent
    There have been previous years where the answer was not only text, but text based on ASCII renders of text, e.g. ███ ██ ███ █ ██ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ██ ████ █ ███...

    There have been previous years where the answer was not only text, but text based on ASCII renders of text, e.g.

    ███   ██  ███  █     ██  █  █ █  █ █
    █  █ █  █ █  █ █    █  █ █ █  █  █ █
    █  █ █    █  █ █    █  █ ██   ████ █
    ███  █    ███  █    ████ █ █  █  █ █
    █ █  █  █ █    █    █  █ █ █  █  █ █
    █  █  ██  █    ████ █  █ █  █ █  █ ████
    
    3 votes
  16. Comment on Let's talk about ChatGPT in ~tech

    wycy
    Link Parent
    POSSIBLE AOC DAY 5 SPOILERS I was struggling with the Rust borrow checker today (having not written any Rust since last year's Advent of Code), so I turned to ChatGPT and asked it "Write me a Rust...

    POSSIBLE AOC DAY 5 SPOILERS

    I was struggling with the Rust borrow checker today (having not written any Rust since last year's Advent of Code), so I turned to ChatGPT and asked it "Write me a Rust function that removes the last character of a string and appends it to a different string" and it wrote out just the perfect function. I was in awe.

    8 votes
  17. Comment on Day 5: Supply Stacks in ~comp.advent_of_code

    wycy
    (edited )
    Link
    Rust Rust use std::env; use std::io::{self}; extern crate regex; use regex::Regex; #[macro_use] extern crate lazy_static; lazy_static! { static ref RE_MOVES: Regex = { Regex::new(r"move ([\d]+)...

    Rust

    Rust
    use std::env;
    use std::io::{self};
    
    extern crate regex;
    use regex::Regex;
    
    #[macro_use] extern crate lazy_static;
    lazy_static! {
        static ref RE_MOVES: Regex = {
            Regex::new(r"move ([\d]+) from ([\d]+) to ([\d]+)").unwrap()
        };
    }
    
    #[derive(Debug)]
    struct Move {
        qty: usize,
        from: usize,
        to: usize,
    }
    impl From<&str> for Move {
        fn from(s: &str) -> Self {
            let matches = RE_MOVES.captures(s).unwrap();
            Self {
                qty:  matches[1].parse().unwrap(),
                from: matches[2].parse().unwrap(),
                to:   matches[3].parse().unwrap(),
            }
        }
    }
    
    const MAX_NUM_STACKS: usize = 9;
    
    fn move_last_n_chars(s1: &str, s2: &str, n: usize) -> (String, String) {
        let mut s1_chars: Vec<char> = s1.chars().collect();
        let     s2_chars: Vec<char> = s2.chars().collect();
        
        let mut moved_chars: String = s1_chars.iter().rev().take(n).collect();
        moved_chars = moved_chars.chars().rev().collect();
    
        for _ in 0..n { s1_chars.pop().unwrap(); }
    
        let s1_new = s1_chars.into_iter().collect();
        let mut s2_new: String = s2_chars.into_iter().collect();
        s2_new.push_str(&moved_chars);
    
        (s1_new, s2_new)
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let input_str = std::fs::read_to_string(input).unwrap();
        let input_str = input_str.trim();
        let input: Vec<_> = input_str.split("\n\n").collect();
    
        // Initialize moves
        let moves: Vec<_> = input[1].split("\n").map(Move::from).collect();
    
        // Initialize stacks
        let mut stacks: [String; MAX_NUM_STACKS] = Default::default();
        for line in input[0].lines() {
            if line.chars().nth(1).unwrap() == '1' { continue } // Skip last line indexing the stacks
            for (ix,ch) in line.chars().enumerate() {
                match ix {
                    1  => if ch != ' ' { stacks[0].push_str(&ch.to_string()) },
                    5  => if ch != ' ' { stacks[1].push_str(&ch.to_string()) },
                    9  => if ch != ' ' { stacks[2].push_str(&ch.to_string()) },
                    13 => if ch != ' ' { stacks[3].push_str(&ch.to_string()) },
                    17 => if ch != ' ' { stacks[4].push_str(&ch.to_string()) },
                    21 => if ch != ' ' { stacks[5].push_str(&ch.to_string()) },
                    25 => if ch != ' ' { stacks[6].push_str(&ch.to_string()) },
                    29 => if ch != ' ' { stacks[7].push_str(&ch.to_string()) },
                    33 => if ch != ' ' { stacks[8].push_str(&ch.to_string()) },
                    _  => {},
                }
            }
        }
    
        // Reverse the stacks
        for ix in 0..MAX_NUM_STACKS {
            stacks[ix] = stacks[ix].chars().rev().collect::<String>();
        }
        
        // Copy original state for part 2
        let stacks_copy = stacks.clone();
    
        // Process part 1 moves
        for moved in &moves {
            let (qty,from,to) = (moved.qty,moved.from-1, moved.to-1);
            for _ in 0..qty {
                //(stacks[from],stacks[to]) = move_last_char(&stacks[from],&stacks[to]);
                (stacks[from],stacks[to]) = move_last_n_chars(&stacks[from],&stacks[to],1);
            }
        }
    
        // Part 1 Output
        print!("Part 1: "); // VGBBJCRMN
        for ix in 0..MAX_NUM_STACKS { print!("{}",stacks[ix].chars().last().unwrap()); }
        println!();
    
        // Process part 2 moves
        stacks = stacks_copy;
        for moved in &moves {
            let (qty,from,to) = (moved.qty,moved.from-1, moved.to-1);
            (stacks[from],stacks[to]) = move_last_n_chars(&stacks[from],&stacks[to],qty);
        }
    
        // Part 2 Output
        print!("Part 2: "); // LBBVJBRMH
        for ix in 0..MAX_NUM_STACKS { print!("{}",stacks[ix].chars().last().unwrap()); }
        println!();
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    2 votes
  18. Comment on Day 4: Camp Cleanup in ~comp.advent_of_code

    wycy
    Link
    Rust Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; use std::ops::RangeInclusive; extern crate regex; use regex::Regex; struct Team { elf1:...

    Rust

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    use std::ops::RangeInclusive;
    
    extern crate regex;
    use regex::Regex;
    
    struct Team {
        elf1: RangeInclusive<usize>,
        elf2: RangeInclusive<usize>,
    }
    impl Team {
        pub fn has_overlaps1(&self) -> bool {
            (self.elf1.contains(&self.elf2.start()) && self.elf1.contains(&self.elf2.end())) ||
            (self.elf2.contains(&self.elf1.start()) && self.elf2.contains(&self.elf1.end()))
        }
        pub fn has_overlaps2(&self) -> bool {
            self.elf1.contains(&self.elf2.start()) || self.elf1.contains(&self.elf2.end()) ||
            self.elf2.contains(&self.elf1.start()) || self.elf2.contains(&self.elf1.end())
        }
    }
    impl From<&String> for Team {
        fn from(s: &String) -> Self {
            let re = Regex::new(r"(\d+)\-(\d+),(\d+)\-(\d+)").unwrap();
            let matches = re.captures(s).unwrap();
            Self {
                elf1: RangeInclusive::new(matches[1].parse().unwrap(),matches[2].parse().unwrap()),
                elf2: RangeInclusive::new(matches[3].parse().unwrap(),matches[4].parse().unwrap()),
            }
        }
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Part 1
        let part1 = input
            .iter()
            .map(Team::from)
            .filter(|x| x.has_overlaps1())
            .count();
        println!("Part 1: {:?}", part1); // 450
    
        // Part 2
        let part2 = input
            .iter()
            .map(Team::from)
            .filter(|x| x.has_overlaps2())
            .count();
        println!("Part 2: {:?}", part2); // 837
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    2 votes
  19. Comment on Day 2: Rock Paper Scissors in ~comp.advent_of_code

    wycy
    (edited )
    Link
    Rust Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; #[derive(Debug,PartialEq,Eq)] enum GameResult { Win = 6, Draw = 3, Lose = 0, } impl From<&str> for GameResult...

    Rust

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    
    #[derive(Debug,PartialEq,Eq)]
    enum GameResult {
        Win = 6,
        Draw = 3,
        Lose = 0,
    }
    impl From<&str> for GameResult {
        fn from(c: &str) -> Self {
            match c {
                "X" => Self::Lose,
                "Y" => Self::Draw,
                "Z" => Self::Win,
                other => panic!("Unknown directive: {}",other),
            }
        }
    }
    
    #[derive(Copy,Clone,Debug,PartialEq,Eq)]
    enum Play {
        Rock = 1,
        Paper = 2,
        Scissors = 3,
    }
    impl From<&str> for Play {
        fn from(c: &str) -> Self {
            match c {
                "A" | "X" => Self::Rock,
                "B" | "Y" => Self::Paper,
                "C" | "Z" => Self::Scissors,
                other => panic!("Unknown play: {}",other),
            }
        }
    }
    
    // plays: (their play, my play)
    fn result(plays: &(Play,Play)) -> GameResult {
        use Play::*;
        use GameResult::*;
        match plays {
            (Rock,Rock) | (Paper,Paper) | (Scissors,Scissors) => { Draw }, 
            (Rock,Paper)     => { Win },
            (Rock,Scissors)  => { Lose },
            (Paper,Rock)     => { Lose },
            (Paper,Scissors) => { Win },
            (Scissors,Rock)  => { Win },
            (Scissors,Paper) => { Lose },
        }
    }
    
    fn play_from_result(their_play: Play, result: &GameResult) -> Play {
        use Play::*;
        use GameResult::*;
        match result {
            Draw => their_play,
            Win => {
                match their_play {
                    Rock     => Paper,
                    Paper    => Scissors,
                    Scissors => Rock,
                }},
            Lose => {
                match their_play {
                    Rock     => Scissors,
                    Paper    => Rock,
                    Scissors => Paper,
                }},
        }
    }
    
    fn plays(s: &String) -> (Play,Play) {
        let plays: Vec<_> = s.split_whitespace().collect();
        (Play::from(plays[0]), Play::from(plays[1]))
    }
    
    fn play_and_result(s: &String) -> (Play,GameResult) {
        let play_and_result: Vec<_> = s.split_whitespace().collect();
        (Play::from(play_and_result[0]), GameResult::from(play_and_result[1]))
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Part 1
        let part1 = input
            .iter()
            .map(|x| {
                let (their_play,my_play) = plays(x);
                let result = result(&(their_play,my_play));
                my_play as usize + result as usize
            })
            .sum::<usize>();
        println!("Part 1: {}", part1); // 11906
    
        // Part 2
        let part2 = input
            .iter()
            .map(|x| {
                let (play,result) = play_and_result(x);
                let my_play = play_from_result(play,&result);
                my_play as usize + result as usize
            })
            .sum::<usize>();
        println!("Part 2: {}", part2); //  11186
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    2 votes
  20. Comment on Day 1: Calorie Counting in ~comp.advent_of_code

    wycy
    Link
    Rust Rust use std::env; use std::io::{self}; fn solve(input: &str) -> io::Result<()> { // Input let input_str = std::fs::read_to_string(input).unwrap(); let input_str = input_str.trim(); let...

    Rust

    Rust
    use std::env;
    use std::io::{self};
    
    fn solve(input: &str) -> io::Result<()> {
        // Input
        let input_str = std::fs::read_to_string(input).unwrap();
        let input_str = input_str.trim();
        let input: Vec<_> = input_str.split("\n\n").collect();
    
        // Get each Elf's calories
        let mut elf_snacks: Vec<usize> = input
            .iter()
            .map(|x| x
                .split("\n")
                .map(|y| y.parse::<usize>().unwrap())
            .sum())
            .collect();
        elf_snacks.sort_by(|a, b| b.cmp(a));
    
        // Calculate answers
        let part1 = elf_snacks.iter().max().unwrap();
        let part2 = elf_snacks.iter().take(3).sum::<usize>();
    
        println!("Part 1: {}", part1); // 72602
        println!("Part 2: {}", part2); // 207410
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    1 vote