ah, uiua, the only thing that makes me wish Unicode hadn't been invented :)
Rust
pub fn solve_part_1(input: &str) -> String {
let numbers: Vec<i32> = input.split(",").map(|x| x.parse().unwrap()).collect();
let mut count = 0;
for i in 1..numbers.len() {
if numbers[i].abs_diff(numbers[i - 1]) == 16 {
count += 1;
}
}
count.to_string()
}
pub fn solve_part_2(input: &str) -> String {
let numbers: Vec<i32> = input.split(",").map(|x| x.parse().unwrap()).collect();
let mut lines: Vec<(i32, i32)> = vec![];
for i in 1..numbers.len() {
let (a, b) = (numbers[i - 1], numbers[i]);
if a > b {
lines.push((b, a));
} else {
lines.push((a, b));
}
}
let mut knots = 0;
for i in 0..lines.len() {
for j in 0..i {
let (a, b) = lines[i];
let (c, d) = lines[j];
if a == c || a == d || b == c || b == d {
continue;
}
let c_inside = c > a && c < b;
let d_inside = d > a && d < b;
if c_inside != d_inside {
knots += 1;
}
}
}
knots.to_string()
}
pub fn solve_part_3(input: &str) -> String {
let numbers: Vec<i32> = input.split(",").map(|x| x.parse().unwrap()).collect();
let mut lines: Vec<(i32, i32)> = vec![];
for i in 1..numbers.len() {
let (a, b) = (numbers[i - 1], numbers[i]);
if a > b {
lines.push((b, a));
} else {
lines.push((a, b));
}
}
let mut best_cut_threads = i64::MIN;
for d in 1..=256 {
for c in 1..d {
let mut cut_threads = 0;
for (a, b) in lines.iter().copied() {
if a == c || a == d || b == c || b == d {
if a == c && b == d {
cut_threads += 1;
}
continue;
}
let c_inside = c > a && c < b;
let d_inside = d > a && d < b;
if c_inside != d_inside {
cut_threads += 1;
}
}
if cut_threads > best_cut_threads {
best_cut_threads = cut_threads;
}
}
}
best_cut_threads.to_string()
}
Rust
Technically you don't need to store the names in part 3, but I was too lazy.
use std::collections::{HashMap, HashSet};
pub fn solve_part_1(input: &str) -> String {
let (names, rules) = input.split_once("\n\n").unwrap();
let names: Vec<&str> = names.split(",").collect();
let rules: HashMap<char, HashSet<char>> = rules
.lines()
.map(|line| {
let (from, to) = line.split_once(" > ").unwrap();
let to = to.split(",");
(
from.chars().next().unwrap(),
to.map(|s| s.chars().next().unwrap()).collect(),
)
})
.collect();
for name in names {
let mut allowed_chars = rules.get(&name.chars().next().unwrap());
let mut acceptable = true;
for ch in name.chars().skip(1) {
match allowed_chars {
Some(allowed) => {
if !allowed.contains(&ch) {
acceptable = false;
break;
}
allowed_chars = rules.get(&ch);
}
None => {
panic!("no rules for letter {ch} in name {name}");
}
}
}
if acceptable {
return name.to_string();
}
}
panic!("all names bad");
}
pub fn solve_part_2(input: &str) -> String {
let (names, rules) = input.split_once("\n\n").unwrap();
let names: Vec<&str> = names.split(",").collect();
let rules: HashMap<char, HashSet<char>> = rules
.lines()
.map(|line| {
let (from, to) = line.split_once(" > ").unwrap();
let to = to.split(",");
(
from.chars().next().unwrap(),
to.map(|s| s.chars().next().unwrap()).collect(),
)
})
.collect();
let mut sum_of_indices = 0;
for (i, name) in names.into_iter().enumerate() {
let mut allowed_chars = rules.get(&name.chars().next().unwrap());
let mut acceptable = true;
for ch in name.chars().skip(1) {
match allowed_chars {
Some(allowed) => {
if !allowed.contains(&ch) {
acceptable = false;
break;
}
allowed_chars = rules.get(&ch);
}
None => {
panic!("no rules for letter {ch} in name {name}");
}
}
}
if acceptable {
sum_of_indices += 1 + i;
}
}
sum_of_indices.to_string()
}
fn gen_names_with_prefix(
prefix: &str,
rules: &HashMap<char, HashSet<char>>,
result: &mut HashSet<String>,
) {
if prefix.len() >= 7 {
result.insert(prefix.to_string());
}
if prefix.len() == 11 {
return;
}
let last_char = prefix.chars().last().unwrap();
if let Some(next_chars) = rules.get(&last_char) {
for next_char in next_chars {
let new_prefix = format!("{prefix}{next_char}");
gen_names_with_prefix(new_prefix.as_str(), rules, result);
}
}
}
pub fn solve_part_3(input: &str) -> String {
let (prefix, rules) = input.split_once("\n\n").unwrap();
let prefixes: Vec<_> = prefix.split(",").collect();
let rules: HashMap<char, HashSet<char>> = rules
.lines()
.map(|line| {
let (from, to) = line.split_once(" > ").unwrap();
let to = to.split(",");
(
from.chars().next().unwrap(),
to.map(|s| s.chars().next().unwrap()).collect(),
)
})
.collect();
let mut results: HashSet<String> = HashSet::new();
prefixes
.into_iter()
.filter(|&name| {
let mut allowed_chars = rules.get(&name.chars().next().unwrap());
let mut acceptable = true;
for ch in name.chars().skip(1) {
match allowed_chars {
Some(allowed) => {
if !allowed.contains(&ch) {
acceptable = false;
break;
}
allowed_chars = rules.get(&ch);
}
None => {
panic!("no rules for letter {ch} in name {name}");
}
}
}
acceptable
})
.for_each(|prefix| gen_names_with_prefix(prefix, &rules, &mut results));
results.len().to_string()
}
Rust
use std::collections::HashMap;
use itertools::Itertools;
pub fn solve_part_1(input: &str) -> String {
let mut mentors = 0;
let mut pairs = 0;
for ch in input.chars() {
match ch {
'A' => mentors += 1,
'a' => pairs += mentors,
_ => {}
}
}
pairs.to_string()
}
pub fn solve_part_2(input: &str) -> String {
let mut mentors: HashMap<char, i64> = HashMap::new();
let mut pairs = 0;
for ch in input.chars() {
match ch {
'A'..='Z' => *mentors.entry(ch).or_default() += 1,
'a'..='z' => pairs += *mentors.entry(ch.to_ascii_uppercase()).or_default(),
_ => panic!("unexpected character {ch}"),
}
}
pairs.to_string()
}
pub fn solve_part_3(input: &str) -> String {
let data: Vec<_> = input.chars().collect();
let len = data.len();
let mentors: HashMap<char, Vec<usize>> = data
.iter()
.enumerate()
.map(|(i, ch)| (*ch, i))
.into_group_map();
let mut pairs: i64 = 0;
for (squire_position, ch) in data.into_iter().enumerate() {
if ch.is_ascii_lowercase() {
for mentor_position in mentors.get(&ch.to_ascii_uppercase()).unwrap() {
if squire_position.abs_diff(*mentor_position) <= 1000 {
pairs += 1000;
} else if (squire_position as isize)
.wrapping_sub_unsigned(len)
.abs_diff(*mentor_position as isize)
<= 1000
|| (*mentor_position as isize)
.wrapping_sub_unsigned(len)
.abs_diff(squire_position as isize)
<= 1000
{
pairs += 999;
}
}
}
}
pairs.to_string()
}
Rust
use itertools::Itertools;
type Fishbone = Vec<(i64, Option<i64>, Option<i64>)>;
fn parse_fishbone(quality_str: &str) -> Fishbone {
let mut fishbone: Fishbone = vec![];
'outer: for num in quality_str.split(",").map(|x| x.parse().unwrap()) {
for e in fishbone.iter_mut() {
if num < e.0 && e.1.is_none() {
e.1 = Some(num);
continue 'outer;
}
if num > e.0 && e.2.is_none() {
e.2 = Some(num);
continue 'outer;
}
}
fishbone.push((num, None, None));
}
fishbone
}
fn compute_quality(fishbone: &Fishbone) -> i64 {
fishbone
.iter()
.map(|(c, _, _)| c.to_string())
.join("")
.parse()
.unwrap()
}
pub fn solve_part_1(input: &str) -> String {
let (_, data) = input.split_once(":").unwrap();
compute_quality(&parse_fishbone(data)).to_string()
}
pub fn solve_part_2(input: &str) -> String {
let mut worst_quality = i64::MAX;
let mut best_quality = i64::MIN;
for sword in input.lines() {
let (_, data) = sword.split_once(":").unwrap();
let quality = compute_quality(&parse_fishbone(data));
worst_quality = worst_quality.min(quality);
best_quality = best_quality.max(quality);
}
(best_quality - worst_quality).to_string()
}
pub fn solve_part_3(input: &str) -> String {
let mut swords: Vec<_> = input
.lines()
.map(|def| {
let (id, data) = def.split_once(":").unwrap();
let fishbone = parse_fishbone(data);
(id.parse::<i64>().unwrap(), fishbone)
})
.collect();
swords.sort_by(|a, b| {
let cmp = compute_quality(&a.1).cmp(&compute_quality(&b.1));
if !matches!(cmp, std::cmp::Ordering::Equal) {
return cmp;
}
for (a_seg, b_seg) in a.1.iter().zip(b.1.iter()) {
let a_val = match a_seg {
(a, Some(b), Some(c)) => format!("{b}{a}{c}"),
(a, Some(b), None) => format!("{b}{a}"),
(a, None, Some(c)) => format!("{a}{c}"),
(a, None, None) => format!("{a}"),
};
let b_val = match b_seg {
(a, Some(b), Some(c)) => format!("{b}{a}{c}"),
(a, Some(b), None) => format!("{b}{a}"),
(a, None, Some(c)) => format!("{a}{c}"),
(a, None, None) => format!("{a}"),
};
let cmp = a_val.parse::<i64>().unwrap().cmp(&b_val.parse().unwrap());
if !matches!(cmp, std::cmp::Ordering::Equal) {
return cmp;
}
}
a.0.cmp(&b.0)
});
swords.reverse();
swords
.into_iter()
.enumerate()
.map(|(pos, (id, _))| id * (pos as i64 + 1))
.sum::<i64>()
.to_string()
}
Rust
use num::{BigInt, Integer};
pub fn solve_part_1(input: &str) -> String {
let gears: Vec<i64> = input.trim().lines().map(|g| g.parse().unwrap()).collect();
(2025 * gears[0] / gears.last().unwrap()).to_string()
}
pub fn solve_part_2(input: &str) -> String {
let gears: Vec<i64> = input.trim().lines().map(|g| g.parse().unwrap()).collect();
let res = (BigInt::parse_bytes(b"10000000000000", 10).unwrap() * gears.last().unwrap())
.div_ceil(&(BigInt::ZERO + gears[0]));
res.to_string()
}
pub fn solve_part_3(input: &str) -> String {
let mut lines = input.trim().lines();
let first_gear = BigInt::parse_bytes(lines.next().unwrap().as_bytes(), 10).unwrap();
let mut nominator: BigInt = first_gear * 100;
let mut denominator: BigInt = BigInt::ZERO + 1;
for line in lines {
let mut split = line.split("|");
denominator *= BigInt::parse_bytes(split.next().unwrap().as_bytes(), 10).unwrap();
match split.next() {
Some(size) => {
nominator *= BigInt::parse_bytes(size.as_bytes(), 10).unwrap();
}
None => {
break;
}
}
}
(nominator / denominator).to_string()
}
Rust
pub fn solve_part_1(input: &str) -> String {
let mut crates: Vec<i64> = input.split(",").map(|s| s.parse().unwrap()).collect();
crates.sort();
let mut monotonic_subsequence = vec![crates[0]];
for size in crates.into_iter().skip(1) {
if size == *monotonic_subsequence.last().unwrap() {
continue;
}
monotonic_subsequence.push(size);
}
monotonic_subsequence.iter().sum::<i64>().to_string()
}
pub fn solve_part_2(input: &str) -> String {
let mut crates: Vec<i64> = input.split(",").map(|s| s.parse().unwrap()).collect();
crates.sort();
let mut monotonic_subsequence = vec![crates[0]];
for size in crates.into_iter().skip(1) {
if size == *monotonic_subsequence.last().unwrap() {
continue;
}
monotonic_subsequence.push(size);
if monotonic_subsequence.len() >= 20 {
break;
}
}
monotonic_subsequence.iter().sum::<i64>().to_string()
}
pub fn solve_part_3(input: &str) -> String {
let mut crates: Vec<i64> = input.split(",").map(|s| s.parse().unwrap()).collect();
crates.sort();
let mut monotonic_subsequences = vec![vec![crates[0]]];
for size in crates.into_iter().skip(1) {
let updateable_sequence = monotonic_subsequences
.iter_mut()
.find(|v| *v.last().unwrap() < size);
match updateable_sequence {
Some(v) => {
v.push(size);
}
None => {
monotonic_subsequences.push(vec![size]);
}
}
}
monotonic_subsequences.len().to_string()
}
Rust
use log::debug;
use std::collections::HashSet;
use regex::Regex;
#[derive(PartialEq, Eq, Hash, Clone)]
struct Number(isize, isize);
impl Number {
fn add(self: &Number, b: &Number) -> Number {
Number(self.0 + b.0, self.1 + b.1)
}
fn mul(self: &Number, b: &Number) -> Number {
Number(self.0 * b.0 - self.1 * b.1, self.0 * b.1 + self.1 * b.0)
}
fn div(self: &Number, b: &Number) -> Number {
Number(self.0 / b.0, self.1 / b.1)
}
}
pub fn solve_part_1(input: &str) -> String {
let re = Regex::new(r"A=\[(\d+),(\d+)\]").unwrap();
let (_, [x, y]) = re.captures(input).unwrap().extract();
let a = Number(x.parse().unwrap(), y.parse().unwrap());
let mut res = Number(0, 0);
for _ in 0..3 {
res = res.mul(&res);
res = res.div(&Number(10, 10));
res = res.add(&a);
}
format!("[{},{}]", res.0, res.1)
}
pub fn solve_part_2(input: &str) -> String {
let re = Regex::new(r"A=\[([-0-9]+),([-0-9]+)\]").unwrap();
let (_, [x, y]) = re.captures(input).unwrap().extract();
let a = Number(x.parse().unwrap(), y.parse().unwrap());
let mut engraved_points = 0;
let mut pts: HashSet<_> = HashSet::new();
for i in 0..=100 {
for j in 0..=100 {
let pt = Number(a.0 + 10 * i, a.1 + 10 * j);
let mut res = Number(0, 0);
engraved_points += 1;
pts.insert(pt.clone());
for _ in 0..100 {
res = res.mul(&res);
res = res.div(&Number(100_000, 100_000));
res = res.add(&pt);
if res.0.abs() > 1_000_000 || res.1.abs() > 1_000_000 {
engraved_points -= 1;
pts.remove(&pt);
break;
}
}
}
}
for i in 0..=100 {
debug!("{}", (0..=100).map(|j| if pts.contains(&Number(a.0 + 10*i, a.1 + 10*j)) { 'X' } else {'.'}).collect::<String>());
}
engraved_points.to_string()
}
pub fn solve_part_3(input: &str) -> String {
let re = Regex::new(r"A=\[([-0-9]+),([-0-9]+)\]").unwrap();
let (_, [x, y]) = re.captures(input).unwrap().extract();
let a = Number(x.parse().unwrap(), y.parse().unwrap());
let mut engraved_points = 0;
for i in 0..=1000 {
for j in 0..=1000 {
let pt = Number(a.0 + i, a.1 + j);
let mut res = Number(0, 0);
engraved_points += 1;
for _ in 0..100 {
res = res.mul(&res);
res = res.div(&Number(100_000, 100_000));
res = res.add(&pt);
if res.0.abs() > 1_000_000 || res.1.abs() > 1_000_000 {
engraved_points -= 1;
break;
}
}
}
}
engraved_points.to_string()
}
Just an ad article. "Did you also encounter skill issues while building web applications? Try my platform."
but how many COMEFROMs are there
imagine the diffs
I don't think there's such a thing as a "spirit of the question", but you're free to set your own challenges of course :)