Skip to content

Commit

Permalink
feat: Day 10 2021 ⭐ 🌟 Rust and TypeScript solution
Browse files Browse the repository at this point in the history
  • Loading branch information
icyJoseph committed Dec 10, 2021
1 parent cc6bbc3 commit 282ea21
Show file tree
Hide file tree
Showing 3 changed files with 380 additions and 0 deletions.
9 changes: 9 additions & 0 deletions 2021/day-10/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
authors = ["Joseph Chamochumbi <[email protected]>"]
edition = "2018"
name = "day-10"
version = "0.1.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
aoc = {git = "https://github.com/icyJoseph/advent-of-code.git"}
203 changes: 203 additions & 0 deletions 2021/day-10/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
use aoc;

use std::iter::{Iterator, Peekable};

#[derive(Debug)]
struct Node {
left: Option<char>,
right: Option<char>,
children: Vec<Node>,
}

impl Node {
fn new() -> Self {
Node {
left: None,
right: None,
children: vec![],
}
}

fn verify(&self) -> Option<&Node> {
let mut children_error = None;

for child in self.children.iter() {
let result = child.verify();
if result.is_some() {
children_error = result;
break;
}
}

match children_error {
Some(err) => Some(err),
None => match (self.left, self.right) {
(Some('('), Some(')')) => return None,
(Some('['), Some(']')) => return None,
(Some('{'), Some('}')) => return None,
(Some('<'), Some('>')) => return None,
(Some(_), Some(_)) => return Some(self),
(Some(_), None) => return None,
_ => Some(self),
},
}
}

fn complete(&self) -> Vec<char> {
let mut acc = vec![];

for child in self.children.iter() {
let mut complete = child.complete();
acc.append(&mut complete);
}

match (self.left, self.right) {
(Some('{'), None) => {
acc.push('}');
}
(Some('['), None) => {
acc.push(']');
}
(Some('('), None) => {
acc.push(')');
}
(Some('<'), None) => {
acc.push('>');
}
_ => { /* Do nothing */ }
}

acc
}
}

fn parse<T: Iterator<Item = char>>(peekable: &mut Peekable<T>) -> Node {
let mut node = Node::new();

let next = peekable.next();

match next {
Some('[' | '(' | '{' | '<') => {
node.left = next;

let mut children: Vec<Node> = vec![];

loop {
match peekable.peek() {
Some('[' | '(' | '{' | '<') => {
let child = parse(peekable);
children.push(child);
}
_ => break,
}
}

node.children = children;
node.right = peekable.next();

return node;
}
Some(c) => {
node.right = Some(c);
return node;
}
None => return node,
}
}

fn solve(raw: String) -> () {
let rows = raw.trim().split("\n").collect::<Vec<&str>>();

let mut incomplete: Vec<Node> = vec![];

let mut score = 0;

for row in rows.iter() {
let mut it = row.chars().peekable();

loop {
match it.peek() {
None => break,
Some(_) => {
let result = parse(&mut it);

match result.verify() {
None => incomplete.push(result),
Some(err) => match err.right {
Some(')') => score += 3,
Some(']') => score += 57,
Some('}') => score += 1197,
Some('>') => score += 25137,
_ => { /*do nothing*/ }
},
}
}
}
}
}

println!("Part One: {}", score);

let mut complete_scores: Vec<u64> = vec![];

for entry in incomplete {
let complete = entry.complete();

if complete.len() == 0 {
continue;
}

complete_scores.push(complete.iter().fold(0, |prev, curr| match curr {
')' => prev * 5 + 1,
']' => prev * 5 + 2,
'}' => prev * 5 + 3,
'>' => prev * 5 + 4,
_ => prev,
}))
}

complete_scores.sort_by(|a, b| a.cmp(&b).reverse());

println!("Part Two: {}", complete_scores[complete_scores.len() / 2]);
}

fn main() {
// let input = std::fs::read_to_string("./input/example.in").expect("Error reading input");
let input = aoc::get_input(2021, 10);

solve(input);
}

// Utilities
#[allow(dead_code)]
fn normal(x: usize, y: usize, width: usize) -> usize {
x + y * width
}

#[allow(dead_code)]
fn rev_normal(norm: usize, width: usize) -> (usize, usize) {
(norm % width, norm / width)
}

#[allow(dead_code)]
fn parse_num<T: std::str::FromStr>(str: &str) -> T {
match str.trim().parse::<T>() {
Ok(n) => n,
_ => panic!("Error parsing"),
}
}
#[allow(dead_code)]
fn to_int(bin: &str) -> u32 {
match u32::from_str_radix(bin, 2) {
Ok(n) => n,
_ => panic!("Error parsing binary to integer"),
}
}

#[allow(dead_code)]
fn string_vec<T: std::string::ToString>(vec: &Vec<T>, separator: &str) -> String {
vec.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(separator)
}
168 changes: 168 additions & 0 deletions 2021/deno/day-10.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
const input = await Deno.readTextFile("./input/day-10.in");

const syntaxScores: Record<string, number> = {
")": 3,
"]": 57,
"}": 1197,
">": 25137
};

const autoCompleteScores: Record<string, number> = {
")": 1,
"]": 2,
"}": 3,
">": 4
};

const rows = input.split("\n");

const peekable = (chars: string[]) => {
let i = 0;
return {
next() {
let value = chars[i] ?? null;
i = i + 1;
return value;
},
peek() {
return chars[i] ?? null;
}
};
};

type Node = {
left: string | null;
right: string | null;
children: Node[];
};

const createNode = (): Node => {
return { left: null, right: null, children: [] };
};

const antiNodes: Record<string, string> = {
"(": ")",
"[": "]",
"{": "}",
"<": ">"
};

const parse = (
iterable: ReturnType<typeof peekable>,
node: Node = createNode()
): Node => {
const next = iterable.next();

if (next === null) return node;

switch (next) {
case "[":
case "{":
case "<":
case "(": {
node.left = next;

let children: Node[] = [];

while (["(", "[", "{", "<"].includes(iterable.peek())) {
let child = parse(iterable);
children.push(child);
}

node.children = children;
node.right = iterable.next();

return node;
}

case ")":
case "]":
case "}":
case ">":
node.right = next;
return node;

default:
throw new Error(`Unexpected: ${next}`);
}
};

const verify = (node: Node) => {
const { left, right } = node;

if (left === null) return;
if (right === null) return;

if (antiNodes[left] !== right) {
throw right; // illegal anti node
}
};

const verifyAll = (node: Node) => {
node.children.forEach((child) => verifyAll(child));

verify(node);
};

const { score, incomplete } = rows.reduce<{
score: number;
incomplete: Node[];
}>(
(prev, row) => {
let it = peekable(row.split(""));

while (it.peek() !== null) {
// there's more!
const parsed = parse(it);
try {
verifyAll(parsed);
prev.incomplete.push(parsed);
} catch (err) {
if (typeof err === "string") {
prev.score += syntaxScores[err];
} else {
throw new Error(err);
}
}
}

return prev;
},
{ score: 0, incomplete: [] }
);

const complete = (node: Node, acc: string[]) => {
if (node.left === null) {
throw new Error("Node is opened with null");
}

if (node.right === null) {
acc.push(antiNodes[node.left]);
}
};

const completeNode = (node: Node, acc: string[] = []) => {
node.children.forEach((child) => completeNode(child, acc));

complete(node, acc);

return acc;
};

const autoScoreResults = incomplete.map((entry) => {
return completeNode(entry).reduce((prev, curr) => {
return prev * 5 + autoCompleteScores[curr];
}, 0);
});

autoScoreResults.sort((a, b) => b - a);

/**
* Part One
*/
console.log("Part One:", score);

/**
* Part Two
*/
console.log("Part Two:", autoScoreResults[(autoScoreResults.length - 1) / 2]);

0 comments on commit 282ea21

Please sign in to comment.