13 votes

Day 13: Transparent Origami

Today's problem description: https://adventofcode.com/2021/day/13

Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python with any of the "short names" listed in this page of supported languages):

<details>
<summary>Part 1</summary>

```python
Your code here.
```

</details>

16 comments

  1. [2]
    Crespyl
    Link
    This one was fun, but pretty straightforward compared to this point in some of the previous years. Part 1 Ruby def compute_p1(input) dots, instructions = input.split("\n\n") grid = Hash.new(false)...

    This one was fun, but pretty straightforward compared to this point in some of the previous years.

    Part 1 Ruby
    def compute_p1(input)
      dots, instructions = input.split("\n\n")
    
      grid = Hash.new(false)
    
      dots
        .lines
        .map(&:chomp)
        .map { _1.split(',') }
        .map { _1.map(&:to_i) }
        .each do |x,y|
        grid[[x,y]] = true
      end
    
      apply_fold(grid, instructions.lines.first)
    
      grid.values.count(true)
    end
    
    def apply_fold(grid, instruction)
      axis, value = instruction.match(/(x|y)=(\d+)/)[1..]
      value = value.to_i
    
      grid.keys.each do |x,y|
        new_x, new_y = case axis
                       when 'x'
                         next unless x > value
                         [value - (x - value), y]
                       when 'y'
                         next unless y > value
                         [x, value - (y - value)]
                       end
    
        grid[[new_x, new_y]] = true
        grid[[x, y]] = false
      end
    
      case axis
      when 'y'
        grid.keys.each do |x,y|
          grid.delete([x,y]) if y > value
        end
      when 'x'
        grid.keys.each do |x,y|
          grid.delete([x,y]) if x > value
        end
      end
    end
    
    Part 2 Ruby
    def compute_p2(input)
      dots, instructions = input.split("\n\n")
    
      grid = Hash.new(false)
    
      dots
        .lines
        .map(&:chomp)
        .map { _1.split(',') }
        .map { _1.map(&:to_i) }
        .each do |x,y|
        grid[[x,y]] = true
      end
    
      instructions.lines.each do |line|
        apply_fold(grid, line)
      end
    
      print "\n"
      print_grid(grid)
      print "\n"
    end
    
    def print_grid(grid)
      width = grid.keys.map { _1[0] }.max
      height = grid.keys.map { _1[1] }.max
    
      (height+1).times do |y|
        (width+1).times do |x|
          if grid[[x,y]]
            print '#'
          else
            print ' '
          end
        end
        print "\n"
      end
    end
    
    5 votes
    1. bhrgunatha
      Link Parent
      This year has felt easier than previous years. There were so many complaints about 2019 and I think Eric takes the feedback very seriously. I'm expecting the 18th & 19th to be more challenging...

      This year has felt easier than previous years. There were so many complaints about 2019 and I think Eric takes the feedback very seriously. I'm expecting the 18th & 19th to be more challenging since they're the weekend and close to the end.

      3 votes
  2. [3]
    DataWraith
    (edited )
    Link
    Day 13 (Rust) Today was the first time I actually timed myself (by making a screen recording). Took me about 1 hour to solve both tasks, though, despite not intending to show the recording to...

    Day 13 (Rust)

    Today was the first time I actually timed myself (by making a screen recording). Took me about 1 hour to solve both tasks, though, despite not intending to show the recording to anyone, I was still somewhat nervous about going fast this time — which, ironically, made me end up being slower.

    • ~12m: Understanding Problem/Parsing
    • ~20m: Part 1 initial impl.
    • ~20m: Part 1 Debugging
    • ~5m: Part 2

    As an aside: I knew I was going to confuse the axes going in, and of course I did, after thinking I had gotten it right...

    Imports & Data structures
    use std::collections::HashSet;
    
    #[derive(Debug, Clone)]
    pub enum OrigamiFold {
        Horizontal(usize),
        Vertical(usize),
    }
    
    #[derive(Eq, Hash, PartialEq, Clone, Copy)]
    pub struct Pos2D {
        x: usize,
        y: usize,
    }
    
    Parsing
    mod parse {
        use nom::{
            bytes::complete::tag,
            character::complete::{alpha1, digit1, line_ending},
            combinator::{eof, opt},
            multi::many1,
            IResult,
        };
    
        use super::OrigamiFold;
        use super::Pos2D;
    
        pub fn coordinate(input: &str) -> IResult<&str, Pos2D> {
            let (input, x) = digit1(input)?;
            let (input, _) = tag(",")(input)?;
            let (input, y) = digit1(input)?;
            let (input, _) = line_ending(input)?;
    
            let x = x.parse::<usize>().unwrap();
            let y = y.parse::<usize>().unwrap();
    
            Ok((input, Pos2D { x, y }))
        }
    
        pub fn fold(input: &str) -> IResult<&str, OrigamiFold> {
            let (input, _) = tag("fold along ")(input)?;
            let (input, hv) = alpha1(input)?;
            let (input, _) = tag("=")(input)?;
            let (input, axis) = digit1(input)?;
            let (input, _) = opt(line_ending)(input)?;
    
            let axis = axis.parse::<usize>().unwrap();
    
            Ok((
                input,
                match hv {
                    "x" => OrigamiFold::Horizontal(axis),
                    "y" => OrigamiFold::Vertical(axis),
                    _ => unreachable!(),
                },
            ))
        }
    
        pub fn parse(input: &str) -> IResult<&str, (Vec<Pos2D>, Vec<OrigamiFold>)> {
            let (input, coordinates) = many1(coordinate)(input)?;
            let (input, _) = line_ending(input)?;
            let (input, folds) = many1(fold)(input)?;
            let (input, _) = eof(input)?;
    
            Ok((input, (coordinates, folds)))
        }
    }
    
    Helper methods
    fn fold_position(pos: Pos2D, fold: &OrigamiFold) -> Pos2D {
        match fold {
            OrigamiFold::Vertical(fy) => {
                if pos.y >= *fy {
                    Pos2D {
                        x: pos.x,
                        y: fy - (pos.y - fy),
                    }
                } else {
                    pos
                }
            }
    
            OrigamiFold::Horizontal(fx) => {
                if pos.x >= *fx {
                    Pos2D {
                        x: fx - (pos.x - fx),
                        y: pos.y,
                    }
                } else {
                    pos
                }
            }
        }
    }
    
    fn fold_paper(parsed: (Vec<Pos2D>, Vec<OrigamiFold>)) -> HashSet<Pos2D> {
        let mut paper = HashSet::new();
    
        for &pos in parsed.0.iter() {
            paper.insert(pos);
        }
    
        for fold in parsed.1.iter() {
            paper = paper
                .iter()
                .map(|&pos| fold_position(pos, fold))
                .collect::<HashSet<Pos2D>>()
        }
    
        paper
    }
    

    I love how this turned out. Folding the paper is just mapping fold_position() over the current coordinates, and Rust does the rest.

    fn print_paper(paper: HashSet<Pos2D>) {
        let min_x = paper.iter().map(|pos| pos.x).min().unwrap();
        let min_y = paper.iter().map(|pos| pos.y).min().unwrap();
        let max_x = paper.iter().map(|pos| pos.x).max().unwrap();
        let max_y = paper.iter().map(|pos| pos.y).max().unwrap();
    
        for y in min_y..=max_y {
            for x in min_x..=max_x {
                if paper.contains(&Pos2D { x, y }) {
                    print!("#");
                } else {
                    print!(".");
                }
            }
    
            println!("")
        }
    
        println!()
    }
    

    I don't like the manual step required here.

    Solving
    fn main() {
        let input = include_str!("../../input-13.txt");
        let parsed = parse::parse(input).unwrap().1;
        let mut part1 = parsed.clone();
    
        for _ in (1..part1.1.len()) {
            part1.1.swap_remove(1);
        }
    
        println!("Part I:  {}", fold_paper(part1).len());
        println!("Part II:");
        print_paper(fold_paper(parsed));
    }
    
    5 votes
    1. [2]
      bhrgunatha
      Link Parent
      My rule now is strictly (x, y[,z]) for co-ordinates. Eric usually displays everything with y starting at 0 and increasing down the screen, so the trick is to display increasing y in your outer...

      I knew I was going to confuse the axes going in

      My rule now is strictly (x, y[,z]) for co-ordinates.

      Eric usually displays everything with y starting at 0 and increasing down the screen, so the trick is to display increasing y in your outer loop, then iterate with x increasing left to right.

      It's become a habit now, I don't even have to think - except those few sneaky problems - like with negative numbers or a hex grid.

      2 votes
      1. DataWraith
        Link Parent
        I've standardized on (y, x) coordinates myself actually, because then they are arranged exactly like you would print them out (in row-then-column/scanline order), with grid[0][0] being the...

        I've standardized on (y, x) coordinates myself actually, because then they are arranged exactly like you would print them out (in row-then-column/scanline order), with grid[0][0] being the top-left. But since I was using a HashSet with named coordinates, that actually wasn't the problem.

        The part where I was confused was the mapping of "fold along x/y" -> "horizontal/vertical": folding along the x-axis actually makes a vertical (y-axis) crease, although the fold itself is horizontal... I couldn't keep that straight.

        2 votes
  3. Crestwave
    Link
    This was fun, simple and clean. Part 1 #!/usr/bin/awk -f /,/ { split($0, xy, ",") grid[xy[1], xy[2]] = 1 } /fold/ { split($3, fold, "=") for (i in grid) { if (grid[i]) { split(i, xy, SUBSEP) x =...

    This was fun, simple and clean.

    Part 1
    #!/usr/bin/awk -f
    /,/ {
    	split($0, xy, ",")
    	grid[xy[1], xy[2]] = 1
    }
    
    /fold/ {
    	split($3, fold, "=")
    
    	for (i in grid) {
    		if (grid[i]) {
    			split(i, xy, SUBSEP)
    			x = xy[1]
    			y = xy[2]
    	
    			if (fold[1] == "x") {
    				if (x > fold[2]) {
    					delete grid[x, y]
    					grid[(x - (fold[2] * 2)) * -1, y] = 1
    				}
    			} else if (fold[1] == "y") {
    				if (y > fold[2]) {
    					delete grid[x, y]
    					grid[x, (y - (fold[2] * 2)) * -1] = 1
    				}
    			}
    		}
    	}
    
    	for (i in grid)
    		if (grid[i])
    			total += 1
    
    	print total
    	exit
    }
    
    Part 2 Part 2 just consisted of running my debug visualizer. It would have taken 3 seconds but I wasn't sure if this was a V or U and got locked out.
    #..#
    #..#
    #..#
    #..#
    #..#
    .##.
    

    (It was the latter)

    #!/usr/bin/awk -f
    /,/ {
    	split($0, xy, ",")
    	grid[xy[1], xy[2]] = 1
    }
    
    /fold/ {
    	split($3, fold, "=")
    
    	for (i in grid) {
    		if (grid[i]) {
    			split(i, xy, SUBSEP)
    			x = xy[1]
    			y = xy[2]
    
    
    			if (fold[1] == "x") {
    				if (x > fold[2]) {
    					delete grid[x, y]
    					grid[(x - (fold[2] * 2)) * -1, y] = 1
    				}
    			} else if (fold[1] == "y") {
    				if (y > fold[2]) {
    					delete grid[x, y]
    					grid[x, (y - (fold[2] * 2)) * -1] = 1
    				}
    			}
    		}
    	}
    }
    
    END {
    	for (i in grid) {
    		split(i, xy, SUBSEP)
    
    		if (xy[1] > _x)
    			_x = xy[1]
    
    		if (xy[2] > _y)
    			_y = xy[2]
    	}
    
    	for (y = 0; y <= _y; ++y) {
    		for (x = 0; x <= _x; ++x) {
    			if (grid[x, y])
    				printf("#")
    			else
    				printf(".")
    		}
    
    		print ""
    	}
    }
    
    4 votes
  4. PapaNachos
    Link
    Day 13 Part A - Python I thought my code was somehow not working because I missed the part where we only needed to do the first flip for part one. I thought there was a bug in the problem...
    Day 13 Part A - Python

    I thought my code was somehow not working because I missed the part where we only needed to do the first flip for part one. I thought there was a bug in the problem description. I actually had to go back and make it only do the first fold, since I already made it do the whole list

    import re
    from collections import defaultdict
    
    #data = test_data_grid
    data = real_data_grid
    data = data.split('\n')
    
    #folds = test_data_folds
    folds = real_data_folds
    
    folds = folds.split('\n')
    
    folds = folds[0:1]
    
    grid = defaultdict(lambda: defaultdict(None))
    
    for row in data:
        x,y, = row.split(',')
        x = int(x)
        y = int(y)
        grid[x][y] = '#'
        
    parser = re.compile(r'fold along (\w)=(\d+)')
        
    for row in folds:
        new_grid = defaultdict(lambda: defaultdict(None))
        fold_instructions = parser.search(row)
        axis = fold_instructions[1]
        val = int(fold_instructions[2])
        print(axis, val)
        if axis == 'y':
            for row in grid.keys():
                for col in grid[row].keys():
                    if col < val:
                        new_grid[row][col] = '#'
                    else:
                        diff = col - val
                        new_col = val - diff
                        new_grid[row][new_col] = '#'
        else:
            for row in grid.keys():
                for col in grid[row].keys():
                    if row < val:
                        new_grid[row][col] = '#'
                    else:
                        diff = row - val
                        new_row = val - diff
                        new_grid[new_row][col] = '#'
        grid = new_grid
        
    dot_count = 0
    for row in grid.keys():
        dot_count = dot_count + len(grid[row].keys())
        
    print(dot_count)
    def display_grid(grid):
        print("------------")
        for row in grid.keys():
            this_row = ""
            for col in grid[row].keys():
                this_row = this_row + str(grid[row][col])
            print(this_row)
            
    display_grid(grid)
    
    Day 13 Part B - Python

    I already had the folding algorithm working, so I only really had to figure out the visualization portion. More keeping x and y straight, my biggest weakness

    import re
    from collections import defaultdict
    
    #data = test_data_grid
    data = real_data_grid
    data = data.split('\n')
    
    #folds = test_data_folds
    folds = real_data_folds
    
    folds = folds.split('\n')
    
    grid = defaultdict(lambda: defaultdict(None))
    
    for row in data:
        x,y, = row.split(',')
        x = int(x)
        y = int(y)
        grid[x][y] = '#'
        
    parser = re.compile(r'fold along (\w)=(\d+)')
        
    for row in folds:
        new_grid = defaultdict(lambda: defaultdict(None))
        fold_instructions = parser.search(row)
        axis = fold_instructions[1]
        val = int(fold_instructions[2])
        #print(axis, val)
        if axis == 'y':
            for row in grid.keys():
                for col in grid[row].keys():
                    if col < val:
                        new_grid[row][col] = '#'
                    else:
                        diff = col - val
                        new_col = val - diff
                        new_grid[row][new_col] = '#'
        else:
            for row in grid.keys():
                for col in grid[row].keys():
                    if row < val:
                        new_grid[row][col] = '#'
                    else:
                        diff = row - val
                        new_row = val - diff
                        new_grid[new_row][col] = '#'
        grid = new_grid
        
    x_max = max(grid.keys())
    y_keys = []
    for row in grid.keys():
        y_keys = y_keys + list(grid[row].keys())
    y_max = max(y_keys)
    
    #print(x_max)
    #print(y_max)
    
    #print(grid)
    
    for row in range(y_max+1):
        this_row = ""
        for col in range(x_max+1):
            if row in grid.keys() and row in grid[col].keys():
                this_row = this_row + '#'
            else:
                this_row = this_row + ' '
        print(this_row)```
    
    </details>
    
    <details>
    <summary>Tips</summary>
    
    * Once again I am asking you to keep your x and y coordinates straight
    
    * Visualizing the problem is no longer optional for this one, so you might want to think about doing it in a way you can use
    
    * I found it easier to just make a new grid each time I folded, since doing it in place seemed really difficult
    
    </details>
    
    4 votes
  5. petrichor
    (edited )
    Link
    Python def fold(paper: set, axis: str, crease: int) -> set: result = set() for dot in paper: if axis == "x" and dot[0] >= crease: result.add((crease - (dot[0] - crease), dot[1])) elif axis == "y"...
    Python
    def fold(paper: set, axis: str, crease: int) -> set:
        result = set()
        for dot in paper:
            if axis == "x" and dot[0] >= crease:
                result.add((crease - (dot[0] - crease), dot[1]))
            elif axis == "y" and dot[1] >= crease:
                result.add((dot[0], crease - (dot[1] - crease)))
            else:
                result.add(dot)
        return result
    
    paper = set()
    for i, line in enumerate(input):
        if i < 1125: # Hardcoded for simplicity
            x, y = map(int, line.strip().split(","))
            dot = (x, y)
            paper.add(dot)
        elif i > 1125:
            ins = line.split()[2].split("=")
            paper = fold(paper, ins[0], int(ins[1]))
            if i == 1126: print(len(paper))
    
    for i in range(6):
        for j in range(39):
            if (j, i) in paper:
                print("#", end="")
            else:
                print(" ", end="")
        print()
    
    4 votes
  6. bhrgunatha
    Link
    Data Structure A hash table from point -> "#". My submissions used a set but I changed to a hash because: hash-count is constant time (which means it is stored internally - so I don't have to!). I...
    Data Structure

    A hash table from point -> "#".
    My submissions used a set but I changed to a hash because:

    • hash-count is constant time (which means it is stored internally - so I don't have to!).
      I don't think that set-count has the same guarantee.
    • printing is a little nicer.
    (define (parse-paper input)
      (values (for/hash ([l (in-list (lines (first input)))])
                (match-define (list x y) (numbers l))
                (values (list x y) "#"))
              (for/list ([l (lines (second input))])
                (match-define (list _ axis (app string->number v))
                  (regexp-match #rx"([xy])=([0-9]+)$" l))
                (list axis v))))
    

    My input is the whole file contents split by '\n\n'
    My utility lines splits on a single '\n'

    Part 1

    fold is purely functional - returning a new hash.
    It matches the instruction with constants "x" (vertical) & "y" (horizontal).
    fold/> only changes co-ordinates greater than the fold instruction.
    It amuses me greatly to name it fold.

    (define (part-01 input)
      (define-values (paper instructions) (parse-paper input))
      (hash-count (fold paper (first instructions))))
    
    (define (fold paper instruction)
      (define (fold/> v n)
        (if (> v n) n (- v (- n v))))
      (match instruction
        [(list "x" v) (for/hash ([p (in-hash-keys paper)])
                        (match-define (list x y) p)
                        (values (list (fold/> v x) y) "#"))]
        [(list "y" v) (for/hash ([p (in-hash-keys paper)])
                        (match-define (list x y) p)
                        (values (list x (fold/> v y)) "#"))]))
    
    Part 2

    Basically the same except for all instructions and displays the
    paper at the end. I use " " for because it's easier to read.

    (define (part-02 input)
      (define-values (paper instructions) (parse-paper input))
      (for/fold ([P paper] #:result (show-paper P))
                ([i (in-list instructions)])
        (fold P i)))
    
    (define (show-paper paper)
      (define ps (hash-keys paper))
      (define xmax (apply max (map first ps)))
      (define ymax (apply max (map second ps)))
      (for ([y (in-inclusive-range 0 ymax)])
        (for ([x (in-inclusive-range 0 xmax)])
          (printf "~a" (hash-ref paper (list x y) " ")))
        (printf "\n")))
    
    4 votes
  7. Liru
    Link
    Part 1 + 2, Rust use std::collections::{HashSet, VecDeque}; type Num = i16; #[derive(Debug)] pub enum Fold { X(Num), Y(Num), } #[derive(Debug, Default)] pub struct Instructions { points:...
    Part 1 + 2, Rust
    use std::collections::{HashSet, VecDeque};
    
    type Num = i16;
    
    #[derive(Debug)]
    pub enum Fold {
        X(Num),
        Y(Num),
    }
    
    #[derive(Debug, Default)]
    pub struct Instructions {
        points: HashSet<(Num, Num)>,
        folds: VecDeque<Fold>,
    }
    
    impl std::fmt::Display for Instructions {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            // Finding the max coords gives a slight slowdown for the final output, since we know it's 40x6,
            // but it works for folds in any state.
            let max_x = self
                .points
                .iter()
                .map(|(x, _y)| *x)
                .max()
                .unwrap_or_default();
            let max_y = self
                .points
                .iter()
                .map(|(_x, y)| *y)
                .max()
                .unwrap_or_default();
    
            for y in 0..=max_y {
                let xs: String = (0..=max_x)
                    .map(|x| {
                        if self.points.contains(&(x, y)) {
                            '█'
                        } else {
                            ' '
                        }
                    })
                    .collect();
                writeln!(f, "{}", xs)?;
            }
            Ok(())
        }
    }
    
    impl Instructions {
        /// Returns "visible" dots after the fold.
        pub fn fold(&mut self) -> Option<usize> {
            fn folder(fold_coord: Num, coord: Num) -> Num {
                coord - (fold_coord - coord).abs()
            }
            let next = self.folds.pop_front()?;
    
            match next {
                Fold::X(x_coord) => {
                    self.points = self
                        .points
                        .iter()
                        .map(|coord| (folder(coord.0, x_coord), coord.1))
                        .collect();
                }
                Fold::Y(y_coord) => {
                    self.points = self
                        .points
                        .iter()
                        .map(|coord| (coord.0, folder(coord.1, y_coord)))
                        .collect();
                }
            }
    
            Some(self.count_points())
        }
    
        pub fn count_points(&self) -> usize {
            self.points.len()
        }
    
        pub fn run(&mut self) {
            self.fold();
            println!("{}", self.count_points());
        }
    
        pub fn run2(&mut self) {
            while self.fold().is_some() {}
            println!("{}", self);
        }
    }
    
    pub fn parse(input: &str) -> Instructions {
        let s: Vec<&str> = input.trim().split("\n\n").collect();
    
        // let str1 = s[0];
        let coords = s[0]
            .lines()
            .map(|i| {
                let mut v = i.split(',').flat_map(str::parse);
                (v.next().unwrap(), v.next().unwrap())
            })
            .collect();
    
        let folds = s[1]
            .lines()
            .map(|i| {
                let v = &i[11..12];
    
                let coord = i[13..].parse().unwrap();
    
                match v {
                    "x" => Fold::X(coord),
                    "y" => Fold::Y(coord),
                    _ => unreachable!(),
                }
            })
            .collect();
    
        Instructions {
            points: coords,
            folds,
        }
    }
    

    Nothing too crazy here. I feel I could probably cut the time down a bit by using a 2D array instead of a hashset, but it already runs fast enough at around 400 microseconds.

    4 votes
  8. wycy
    (edited )
    Link
    Rust Edit: Replaced HashMap with HashSet since the value of the map was irrelevant Rust use std::env; use std::io::{self}; use std::collections::HashSet; extern crate regex; use regex::Regex; use...

    Rust

    Edit: Replaced HashMap with HashSet since the value of the map was irrelevant

    Rust
    use std::env;
    use std::io::{self};
    use std::collections::HashSet;
    
    extern crate regex;
    use regex::Regex;
    
    use point2d::point2d::Point2D;
    
    enum Fold {
        AlongX(i64),
        AlongY(i64)
    }
    impl From<&str> for Fold {
        fn from(s: &str) -> Self {
            let re = Regex::new(r"fold along ([xy])=(\d+)").unwrap();
            let matches = re.captures(s).unwrap();
            match &matches[1] {
                "x" => Self::AlongX(matches[2].parse().unwrap()),
                "y" => Self::AlongY(matches[2].parse().unwrap()),
                other => panic!("Unknown fold type: {}", other),
            }
        }
    }
    
    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();
    
        let initial_points: Vec<_> = input[0].split("\n").collect();
        let folds: Vec<_> = input[1].split("\n").map(Fold::from).collect();
    
        // Build initial image
        let mut image: HashSet<Point2D> = HashSet::new();
        let re = Regex::new(r"(\d+),(\d+)").unwrap();
        for point in initial_points {
            let matches = re.captures(&point).unwrap();
            let (x,y) = (matches[1].parse().unwrap(),matches[2].parse().unwrap());
            image.insert(Point2D{x: x, y: y});
        }
    
        // Process folds
        let mut xmax = image.iter().map(|&pt| pt.x).max().unwrap();
        let mut ymax = image.iter().map(|&pt| pt.y).max().unwrap();
        for (i,fold) in folds.iter().enumerate() {
    
            // Part 1
            if i == 1 {
                let part1 = image.iter().filter(|&pt| pt.x <= xmax && pt.y <= ymax).count();
                println!("Part 1: {}", part1); // 743
            }
    
            match &fold {
                Fold::AlongX(cut) => {
                    for y in 0..=ymax {
                        for x in *cut..=xmax {
                            let pt = Point2D { x: x, y: y };
                            let newpt = Point2D { x: 2*cut-x, y };
                            if image.contains(&pt) { image.insert(newpt); }
                        }
                    }
                    xmax = *cut;
                },
                Fold::AlongY(cut) => {
                    for y in *cut..=ymax {
                        for x in 0..=xmax {
                            let pt = Point2D { x: x, y: y };
                            let newpt = Point2D { x: x, y: 2*cut-y };
                            if image.contains(&pt) { image.insert(newpt); }
                        }
                    }
                    ymax = *cut;
                },
            }
        }
    
        println!("Part 2:"); // RCPLAKHL
        for y in 0..=ymax {
            for x in 0..=xmax {
                match image.contains(&Point2D { x: x, y: y }) {
                    true => { print!("█"); },
                    _    => { print!(" "); },
                }
            }
            println!();
        }
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    4 votes
  9. asterisk
    Link
    Python from re import findall def folding(axis: str, number: int) -> list: folded = list() for n in range(1, number + 1): for x, y in set(dots): if axis == "x" and x in (number - n, number + n):...
    Python
    from re import findall
    
    
    def folding(axis: str, number: int) -> list:
        folded = list()
        for n in range(1, number + 1):
            for x, y in set(dots):
                if axis == "x" and x in (number - n, number + n):
                    folded.append((number - n, y))
                elif axis == "y" and y in (number - n, number + n):
                    folded.append((x, number - n))
        return folded
    
    
    def visualing() -> None:
        max_x, max_y = [max(el) + 1 for el in [*zip(*dots)]]
        for y in range(max_y):
            for x in range(max_x):
                print("#" if (x, y) in dots else " ", end="")
            print("\n", end="")
        print()
    
    
    dots = list()
    actions = list()
    instructions = False
    
    for line in open("input.txt").read().splitlines():
        if not line:
            instructions = True
            continue
        if instructions:
            axis, number = findall(r"([xy])=(\d+)", line)[0]
            actions.append((axis, int(number)))
        else:
            dots.append(tuple(map(int, findall(r"\d+", line))))
    
    for number, action in enumerate(actions):
        dots = folding(*action)
        if number == 1:
            print("Part One:", len(dots))
    
    print("Part Two:")
    visualing()
    
    4 votes
  10. jzimbel
    Link
    Elixir This one was pretty straightforward, and luckily I had defined a CharSpace module while doing last year's puzzles which made it pretty simple to print out the final result. I'm not going to...

    Elixir

    This one was pretty straightforward, and luckily I had defined a CharSpace module while doing last year's puzzles which made it pretty simple to print out the final result. I'm not going to include that code because it's really over-engineered for the relatively basic needs of this specific puzzle (needed to make it generalized for up to 4 dimensions for last year's puzzle), so just know that it's a thing that can take a list of coordinate pairs and produce a string that "draws" them on an appropriately-sized canvas.

    I've found it really helpful/intuitive to use streams whenever a puzzle involves iteration over the initial input. At the top level, this approach transforms the problem into simply reading values from an enumerable. For this problem, the appropriate stream function to do the fold iterations was Stream.unfold/2, which gave me a chuckle.

    Both parts
    defmodule AdventOfCode.Solution.Year2021.Day13 do
      alias AdventOfCode.CharSpace.TwoDim
    
      def part1(input) do
        input
        |> parse_input()
        |> stream_folds()
        |> Enum.at(1)
        |> MapSet.size()
      end
    
      def part2(input) do
        input
        |> parse_input()
        |> stream_folds()
        |> Enum.at(-1)
        |> TwoDim.from_coords_list()
        |> to_string()
      end
    
      defp stream_folds(initial_data) do
        Stream.unfold(initial_data, fn
          nil ->
            nil
    
          {coords_set, []} ->
            {coords_set, nil}
    
          {coords_set, [instruction | instructions]} ->
            {coords_set, {fold(coords_set, instruction), instructions}}
        end)
      end
    
      defp fold(coords_set, {:left, fold_x}) do
        {to_fold, to_remain} =
          coords_set
          |> Enum.reject(fn {x, _} -> x == fold_x end)
          |> Enum.split_with(fn {x, _} -> x > fold_x end)
    
        to_fold
        |> MapSet.new(fn {x, y} -> {2 * fold_x - x, y} end)
        |> MapSet.union(MapSet.new(to_remain))
      end
    
      defp fold(coords_set, {:up, fold_y}) do
        {to_fold, to_remain} =
          coords_set
          |> Enum.reject(fn {_, y} -> y == fold_y end)
          |> Enum.split_with(fn {_, y} -> y > fold_y end)
    
        to_fold
        |> MapSet.new(fn {x, y} -> {x, 2 * fold_y - y} end)
        |> MapSet.union(MapSet.new(to_remain))
      end
    
      defp parse_input(input) do
        [coords, instructions] = String.split(input, "\n\n", trim: true)
    
        {parse_coords_set(coords), parse_instructions(instructions)}
      end
    
      defp parse_coords_set(coords) do
        coords
        |> String.split("\n", trim: true)
        |> MapSet.new(fn line ->
          ~r/(\d+),(\d+)/
          |> Regex.run(line, capture: :all_but_first)
          |> Enum.map(&String.to_integer/1)
          |> List.to_tuple()
        end)
      end
    
      defp parse_instructions(instructions) do
        instructions
        |> String.split("\n", trim: true)
        |> Enum.map(fn line ->
          ~r/(x|y)=(\d+)/
          |> Regex.run(line, capture: :all_but_first)
          |> then(fn
            ["x", n] -> {:left, String.to_integer(n)}
            ["y", n] -> {:up, String.to_integer(n)}
          end)
        end)
      end
    end
    
    4 votes
  11. kari
    Link
    Rust Today wasn't too bad but I spent an embarrassingly long amount of time trying to figure out the relation for which spots on the right/bottom map to which spots on the left/top after a fold....

    Rust

    Today wasn't too bad but I spent an embarrassingly long amount of time trying to figure out the relation for which spots on the right/bottom map to which spots on the left/top after a fold.

    Rust (both days)

    I definitely could clean my code up quite a bit but it works so, eh, maybe later :).

    use crate::lib::aoc;
    use std::{
        cmp::Ordering,
        collections::VecDeque,
        fmt::{Display, Formatter, Result},
        ops::{BitOr, BitOrAssign},
    };
    
    type Paper = Vec<Vec<PaperSpot>>;
    
    #[derive(Debug, Copy, Clone)]
    enum Fold {
        Up(usize),
        Left(usize),
        Invalid,
    }
    
    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
    enum PaperSpot {
        Dot,
        Empty,
    }
    
    impl Display for PaperSpot {
        fn fmt(&self, f: &mut Formatter) -> Result {
            write!(
                f,
                "{}",
                match self {
                    PaperSpot::Dot => '#',
                    PaperSpot::Empty => '.',
                }
            )
        }
    }
    
    impl BitOr for PaperSpot {
        type Output = Self;
    
        fn bitor(self, rhs: Self) -> Self::Output {
            match self == PaperSpot::Dot || rhs == PaperSpot::Dot {
                true => PaperSpot::Dot,
                false => PaperSpot::Empty,
            }
        }
    }
    
    impl BitOrAssign for PaperSpot {
        fn bitor_assign(&mut self, rhs: Self) {
            match *self == PaperSpot::Dot || rhs == PaperSpot::Dot {
                true => *self = PaperSpot::Dot,
                false => *self = PaperSpot::Empty,
            }
        }
    }
    
    fn new_paper(num_rows: usize, num_cols: usize) -> Paper {
        vec![vec![PaperSpot::Empty; num_cols]; num_rows]
    }
    
    fn fold_paper(paper: Paper, fold: Fold) -> Paper {
        match fold {
            Fold::Up(fold_row) => {
                let mut folded_paper = new_paper(paper.len() / 2, paper[0].len());
                for row in 0..paper.len() {
                    for col in 0..paper[row].len() {
                        match row.cmp(&fold_row) {
                            Ordering::Greater => {
                                folded_paper[paper.len() - 1 - row][col] |= paper[row][col]
                            }
                            Ordering::Less => folded_paper[row][col] |= paper[row][col],
                            Ordering::Equal => { /* If it's equal, this row is *on* the fold line so we can skip it*/
                            }
                        }
                    }
                }
                folded_paper
            }
            Fold::Left(fold_col) => {
                let mut folded_paper = new_paper(paper.len(), paper[0].len() / 2);
                for row in 0..paper.len() {
                    for col in 0..paper[row].len() {
                        match col.cmp(&fold_col) {
                            Ordering::Greater => {
                                folded_paper[row][paper[0].len() - 1 - col] |= paper[row][col]
                            }
                            Ordering::Less => folded_paper[row][col] |= paper[row][col],
                            Ordering::Equal => { /* If it's equal, this col is *on* the fold line so we can skip it*/
                            }
                        }
                    }
                }
                folded_paper
            }
            Fold::Invalid => {
                eprintln!("Tried to fold an invalid fold! Ignoring...");
                paper.to_vec()
            }
        }
    }
    
    fn display_paper(paper: Paper) {
        for row in paper {
            for paper_spot in row {
                print!("{}", paper_spot)
            }
            println!()
        }
        println!()
    }
    
    fn count_dots(paper: Paper) -> u32 {
        let mut num_dots = 0;
    
        for row in paper {
            for paper_spot in row {
                if let PaperSpot::Dot = paper_spot {
                    num_dots += 1
                };
            }
        }
    
        num_dots
    }
    
    pub fn run() {
        let lines = aoc::get_lines("./inputs/day13.in");
    
        let (num_rows, num_cols) =
            lines
                .iter()
                .take_while(|line| line.contains(','))
                .fold((0, 0), |(max_y, max_x), line| {
                    let (x, y): (usize, usize) = match line.split_once(',') {
                        Some((x, y)) => (
                            x.parse().expect("Some x val is wrong!"),
                            y.parse().expect("Some y val is wrong!"),
                        ),
                        None => panic!("One of your dot lines is entered incorrectly!"),
                    };
    
                    // Add 1 to x and y since x and y are indices but max_x and max_y are number of
                    // cols/rows
                    (usize::max(y + 1, max_y), usize::max(x + 1, max_x))
                });
    
        let (paper, mut folds): (Paper, VecDeque<Fold>) = lines.iter().fold(
            (new_paper(num_rows, num_cols), VecDeque::new()),
            |(mut paper, mut folds), line| {
                if let Some((x, y)) = line.split_once(',') {
                    // We already did the expects up there ^^
                    let col: usize = x.parse().unwrap();
                    let row: usize = y.parse().unwrap();
                    paper[row][col] = PaperSpot::Dot;
                } else if line.starts_with("fold along") {
                    if let Some((axis, index)) = line
                        .split(' ')
                        .nth(2)
                        .expect("One of your fold lines is entered incorrectly!")
                        .split_once('=')
                    {
                        let index: usize = index.parse().unwrap();
                        let fold = match axis {
                            "y" => Fold::Up(index),
                            "x" => Fold::Left(index),
                            _ => {
                                eprintln!("Your input file has an invalid fold! Ignoring...");
                                Fold::Invalid
                            }
                        };
    
                        folds.push_back(fold);
                    }
                }
    
                (paper, folds)
            },
        );
    
        // Do the first fold for part 1
        let paper_p1 = fold_paper(paper, folds.pop_front().expect("No folds in your input!"));
    
        // Finish folding then display the paper for part 2
        let mut paper = paper_p1.clone();
        while let Some(fold) = folds.pop_front() {
            paper = fold_paper(paper, fold);
        }
    
        aoc::output(13, "num dots", count_dots(paper_p1), 0);
        display_paper(paper);
        println!();
    
    }
    
    3 votes
  12. 3d12
    Link
    Friggin' yeesh. With everyone saying how easy this year is compared to previous, it's no wonder I couldn't hang in this far in previous years. This did end up being a very fun (and visually...

    Friggin' yeesh. With everyone saying how easy this year is compared to previous, it's no wonder I couldn't hang in this far in previous years.

    This did end up being a very fun (and visually pleasing!) problem once I worked out the kinks. I knew I'd get in trouble for "standardizing" my test folds to a centralized axis, so that did come back to bite me. But I went ahead and coded for all the folds since the problem implied that was the next step, and ended up getting some fun map/filter practice to backwardly-derive my answer to part 1.

    Part 1
    const fs = require('fs');
    const readline = require('readline');
    
    let inputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			inputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    function parseGrid(input) {
    	let gridPoints = [];
    	for (const line of input) {
    		let regex = /(\d+),(\d+)/;
    		let found = line.match(regex);
    		if (found) {
    			let pointX = parseInt(found[1]);
    			let pointY = parseInt(found[2]);
    			gridPoints.push({ x: pointX, y: pointY });
    		}
    	}
    	let foldInstructions = [];
    	for (const line of input) {
    		let regex = /fold along ([x|y])=(\d+)/;
    		let found = line.match(regex);
    		if (found) {
    			let foldDirection = found[1];
    			let foldDistance = parseInt(found[2]);
    			foldInstructions.push({ direction: foldDirection, distance: foldDistance });
    		}
    	}
    	return { gridPoints: gridPoints, foldInstructions: foldInstructions };
    }
    
    function mapGrid(input) {
    	let output = [];
    	console.log("DEBUG: input.map(e => e.x) = " + input.map(e => e.x).sort((a,b) => b-a)[0]);
    	let length = input.map(e => e.x).sort((a,b) => b-a)[0];
    	let depth = input.map(e => e.y).sort((a,b) => b-a)[0];
    	console.log("DEBUG: length = " + length + ", depth = " + depth);
    	for (let y = 0; y <= depth; y++) {
    		let currentLine = [];
    		for (let x = 0; x <= length; x++) {
    			if (input.filter(e => e.x === x).filter(e => e.y === y).length > 0) {
    				currentLine.push('#');
    			} else {
    				currentLine.push('.');
    			}
    		}
    		output.push(currentLine);
    	}
    	return output;
    }
    
    function foldGrid(input,direction,distance) {
    	let output = [];
    	if (direction === 'x') {
    		let inputCopy = input.map(e => e);
    		for (let row of inputCopy) {
    			row[distance] = '|';
    		}
    		console.log(inputCopy.map(e => e.join('')).join('\n'));
    		for (let row of inputCopy) {
    			let newRow = [];
    			for (let i = 0; i < distance; i++) {
    				let char1 = row[i];
    				let char2 = row[i+((distance-i)*2)];
    				if (char1 === '#' || char2 === '#') {
    					newRow.push('#');
    				} else {
    					newRow.push('.');
    				}
    			}
    			output.push(newRow);
    		}
    	} else if (direction === 'y') {
    		let foldRow = [];
    		for (let i = 0; i<input[distance].length; i++) {
    			foldRow.push('-');
    		}
    		let inputCopy = input.map(e => e);
    		inputCopy[distance] = foldRow;
    		console.log(inputCopy.map(e => e.join('')).join('\n'));
    		for (let i = 0; i < distance; i++) {
    			let currentRow = inputCopy[i];
    			let compareRow = [];
    			let distanceOffset = i+((distance-i)*2);
    			if (distanceOffset < inputCopy.length) {
    				compareRow = inputCopy[i+((distance-i)*2)];
    			} else {
    				compareRow = currentRow;
    			}
    			let newRow = [];
    			for (let charIndex = 0; charIndex < currentRow.length; charIndex++) {
    				let currentChar = currentRow[charIndex];
    				let compareChar = compareRow[charIndex];
    				if (currentChar === '#' || compareChar === '#') {
    					newRow.push('#');
    				} else {
    					newRow.push('.');
    				}
    			}
    			output.push(newRow);
    		}
    	} else {
    		return new Exception("Invalid direction passed: " + direction);
    	}
    	return output;
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let parsedGrid = parseGrid(inputArr);
    	console.log(parsedGrid);
    	let fold = 0;
    	let foldedMap = mapGrid(parsedGrid.gridPoints);
    	console.log("DEBUG: fold = " + fold + ", direction = " + parsedGrid.foldInstructions[0].direction + ", distance = " + parsedGrid.foldInstructions[0].distance);
    	console.log(foldedMap.map(e => e.join('')).join('\n'));
    	console.log('');
    	foldedMap = foldGrid(
    	 			foldedMap,
    	 			parsedGrid.foldInstructions[0].direction,
    	 			parsedGrid.foldInstructions[0].distance
    	 		);
    	// for (const instruction of parsedGrid.foldInstructions) {
    	// 	fold++;
    	// 	console.log("DEBUG: fold = " + fold + ", direction = " + instruction.direction + ", distance = " + instruction.distance);
    	// 	foldedMap = foldGrid(
    	// 			foldedMap,
    	// 			instruction.direction,
    	// 			instruction.distance
    	// 		);
    	// 	console.log('');
    	// 	console.log(foldedMap.map(e => e.join('')).join('\n'));
    	// 	console.log('');
    	// }
    	let totalDots = foldedMap
    		.filter(e => e.includes('#'))
    		.map(e => e.filter(f => f === '#').length)
    		.reduce((a,b) => a + b);
    	console.log("Answer found! " + totalDots);
    })();
    
    Part 2
    const fs = require('fs');
    const readline = require('readline');
    
    let inputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			inputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    function parseGrid(input) {
    	let gridPoints = [];
    	for (const line of input) {
    		let regex = /(\d+),(\d+)/;
    		let found = line.match(regex);
    		if (found) {
    			let pointX = parseInt(found[1]);
    			let pointY = parseInt(found[2]);
    			gridPoints.push({ x: pointX, y: pointY });
    		}
    	}
    	let foldInstructions = [];
    	for (const line of input) {
    		let regex = /fold along ([x|y])=(\d+)/;
    		let found = line.match(regex);
    		if (found) {
    			let foldDirection = found[1];
    			let foldDistance = parseInt(found[2]);
    			foldInstructions.push({ direction: foldDirection, distance: foldDistance });
    		}
    	}
    	return { gridPoints: gridPoints, foldInstructions: foldInstructions };
    }
    
    function mapGrid(input) {
    	let output = [];
    	console.log("DEBUG: input.map(e => e.x) = " + input.map(e => e.x).sort((a,b) => b-a)[0]);
    	let length = input.map(e => e.x).sort((a,b) => b-a)[0];
    	let depth = input.map(e => e.y).sort((a,b) => b-a)[0];
    	console.log("DEBUG: length = " + length + ", depth = " + depth);
    	for (let y = 0; y <= depth; y++) {
    		let currentLine = [];
    		for (let x = 0; x <= length; x++) {
    			if (input.filter(e => e.x === x).filter(e => e.y === y).length > 0) {
    				currentLine.push('#');
    			} else {
    				currentLine.push('.');
    			}
    		}
    		output.push(currentLine);
    	}
    	return output;
    }
    
    function foldGrid(input,direction,distance) {
    	let output = [];
    	if (direction === 'x') {
    		let inputCopy = input.map(e => e);
    		for (let row of inputCopy) {
    			row[distance] = '|';
    		}
    		console.log(inputCopy.map(e => e.join('')).join('\n'));
    		for (let row of inputCopy) {
    			let newRow = [];
    			for (let i = 0; i < distance; i++) {
    				let char1 = row[i];
    				let char2 = row[i+((distance-i)*2)];
    				if (char1 === '#' || char2 === '#') {
    					newRow.push('#');
    				} else {
    					newRow.push('.');
    				}
    			}
    			output.push(newRow);
    		}
    	} else if (direction === 'y') {
    		let foldRow = [];
    		for (let i = 0; i<input[distance].length; i++) {
    			foldRow.push('-');
    		}
    		let inputCopy = input.map(e => e);
    		inputCopy[distance] = foldRow;
    		console.log(inputCopy.map(e => e.join('')).join('\n'));
    		for (let i = 0; i < distance; i++) {
    			let currentRow = inputCopy[i];
    			let compareRow = [];
    			let distanceOffset = i+((distance-i)*2);
    			if (distanceOffset < inputCopy.length) {
    				compareRow = inputCopy[i+((distance-i)*2)];
    			} else {
    				compareRow = currentRow;
    			}
    			let newRow = [];
    			for (let charIndex = 0; charIndex < currentRow.length; charIndex++) {
    				let currentChar = currentRow[charIndex];
    				let compareChar = compareRow[charIndex];
    				if (currentChar === '#' || compareChar === '#') {
    					newRow.push('#');
    				} else {
    					newRow.push('.');
    				}
    			}
    			output.push(newRow);
    		}
    	} else {
    		return new Exception("Invalid direction passed: " + direction);
    	}
    	return output;
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let parsedGrid = parseGrid(inputArr);
    	console.log(parsedGrid);
    	let fold = 0;
    	let foldedMap = mapGrid(parsedGrid.gridPoints);
    	console.log("DEBUG: fold = " + fold + ", direction = " + parsedGrid.foldInstructions[0].direction + ", distance = " + parsedGrid.foldInstructions[0].distance);
    	console.log(foldedMap.map(e => e.join('')).join('\n'));
    	for (const instruction of parsedGrid.foldInstructions) {
    		fold++;
    		console.log("DEBUG: fold = " + fold + ", direction = " + instruction.direction + ", distance = " + instruction.distance);
    		foldedMap = foldGrid(
    				foldedMap,
    				instruction.direction,
    				instruction.distance
    			);
    		console.log('');
    		console.log(foldedMap.map(e => e.join('')).join('\n'));
    		console.log('');
    	}
    })();
    
    3 votes
  13. Gyrfalcon
    Link
    In retrospect a set of some kind would have saved me a lot of checking and probably have been faster, but that's not how it panned out. Just glad to make a bit of progress in catching up! Parts 1...

    In retrospect a set of some kind would have saved me a lot of checking and probably have been faster, but that's not how it panned out. Just glad to make a bit of progress in catching up!

    Parts 1 and 2
    import std/[sequtils, strutils, sugar, os]
    
    proc parseInput(inputFile: string): (seq[(int,int)], seq[(bool, int)]) =
      let input = collect(newSeq):
        for line in inputFile.lines: line
    
      var instructions = false
    
      for line in input:
        if not instructions:
          if line.len == 0:
            instructions = true
          else:
            let values = line.split(",")
            result[0].add((values[0].parseInt(), values[1].parseInt()))
        else:
          result[1].add(('x' in line, line.split("=")[^1].parseInt()))
    
    func doFold(paper: seq[(int,int)], steps: seq[(bool, int)]): (seq[(int,int)], seq[(bool, int)]) =
      let currentStep = steps[0]
      result[1] = steps[1 .. ^1]
    
      if currentStep[0]:
        for point in paper:
          if point[0] < currentStep[1]:
            if not (point in result[0]):
              result[0].add(point)
          else:
            let newPoint = (currentStep[1] - (point[0] - currentStep[1]), point[1])
            if not (newPoint in result[0]):
              result[0].add(newPoint)
      else:
        for point in paper:
          if point[1] < currentStep[1]:
            if not (point in result[0]):
              result[0].add(point)
          else:
            let newPoint = (point[0], currentStep[1] - (point[1] - currentStep[1]))
            if not (newPoint in result[0]):
              result[0].add(newPoint)
    
    func completeFold(paper: (seq[(int,int)], seq[(bool, int)])): seq[(int,int)] =
      var mPaper = paper
      while mPaper[1].len > 0:
        mPaper = doFold(mPaper[0], mPaper[1])
    
      result = mPaper[0]
    
    # This is absolutely the worst way to do this
    proc printPaper(paper: seq[(int, int)]) =
      let xMax = max(mapIt(paper, it[0]))
      let yMax = max(mapIt(paper, it[1]))
    
      for ydx in 0 .. yMax:
        var line = ""
        for xdx in 0 .. xMax:
          if (xdx, ydx) in paper:
            line = line & "#"
          else:
            line = line & " "
        echo line
    
    proc main(inputFile: string) =
      var paperState = parseInput(inputFile)
      paperState = doFold(paperState[0], paperState[1])
      echo paperState[0].len
    
      let finalState = completeFold(paperState)
      printPaper(finalState)
    
    when is_main_module:
      main(paramStr(1))
    
    2 votes