2019-11-15 16:11:58 +01:00
|
|
|
use std::collections::HashMap;
|
2019-11-15 17:25:40 +01:00
|
|
|
use std::collections::HashSet;
|
2019-11-17 20:42:25 +01:00
|
|
|
use std::error::Error;
|
2019-11-15 16:11:58 +01:00
|
|
|
|
2019-11-17 20:42:25 +01:00
|
|
|
use super::err;
|
2019-11-15 16:11:58 +01:00
|
|
|
use super::Result;
|
|
|
|
|
|
|
|
const INPUT: &str = include_str!("../input/day03.txt");
|
|
|
|
|
|
|
|
pub fn run() -> Result<()> {
|
|
|
|
println!("part 1: {}", part1(INPUT)?);
|
2019-11-15 17:25:40 +01:00
|
|
|
println!("part 2: {}", part2(INPUT)?);
|
2019-11-15 16:11:58 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-11-15 16:31:45 +01:00
|
|
|
struct Claim {
|
|
|
|
x: usize,
|
|
|
|
y: usize,
|
|
|
|
width: usize,
|
|
|
|
height: usize,
|
|
|
|
id: usize,
|
|
|
|
}
|
|
|
|
|
2019-11-15 16:41:38 +01:00
|
|
|
/// Parses a claim from a line
|
|
|
|
///
|
|
|
|
/// Fails if the line is badly formatted. The expected format is:
|
|
|
|
///
|
|
|
|
/// ```text
|
|
|
|
/// #ID @ X,Y: WxH
|
|
|
|
/// ```
|
2019-11-15 16:31:45 +01:00
|
|
|
fn parse(line: &str) -> Option<Claim> {
|
2019-11-15 16:41:38 +01:00
|
|
|
// skip '#' in line
|
|
|
|
let line = &line[1..];
|
2019-11-15 16:11:58 +01:00
|
|
|
|
2019-11-15 16:41:38 +01:00
|
|
|
// find ' @ ' separator
|
|
|
|
let at = line.find(" @ ")?;
|
|
|
|
let id = line[..at].parse().ok()?;
|
|
|
|
let line = &line[(at + 3)..];
|
|
|
|
|
|
|
|
// parse 'X,Y: WxH
|
2019-11-15 16:11:58 +01:00
|
|
|
let comma = line.find(',')?;
|
|
|
|
let colon = line.find(':')?;
|
|
|
|
let x = line[..comma].parse().ok()?;
|
|
|
|
let y = line[(comma + 1)..colon].parse().ok()?;
|
|
|
|
|
2019-11-15 16:41:38 +01:00
|
|
|
// reduce line to 'WxH'
|
2019-11-15 16:11:58 +01:00
|
|
|
let line = &line[(colon + 2)..];
|
|
|
|
|
|
|
|
let sep = line.find('x')?;
|
|
|
|
let width = line[..sep].parse().ok()?;
|
|
|
|
let height = line[(sep + 1)..].parse().ok()?;
|
|
|
|
|
2019-11-15 16:31:45 +01:00
|
|
|
Some(Claim {
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
width,
|
|
|
|
height,
|
2019-11-15 16:41:38 +01:00
|
|
|
id,
|
2019-11-15 16:31:45 +01:00
|
|
|
})
|
2019-11-15 16:11:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn part1(input: &str) -> Result<u64> {
|
|
|
|
let mut res = 0;
|
2019-11-18 13:56:50 +01:00
|
|
|
let mut map: HashMap<(usize, usize), u64> = HashMap::new();
|
2019-11-15 16:11:58 +01:00
|
|
|
|
|
|
|
for line in input.lines() {
|
2019-11-18 13:56:50 +01:00
|
|
|
let claim = parse(line).ok_or_else(|| err!("Couldn't parse line: {}", line))?;
|
2019-11-15 16:11:58 +01:00
|
|
|
|
2019-11-15 16:31:45 +01:00
|
|
|
for i in 0..claim.width {
|
|
|
|
for j in 0..claim.height {
|
|
|
|
let x = claim.x + i;
|
|
|
|
let y = claim.y + j;
|
2019-11-15 16:11:58 +01:00
|
|
|
|
|
|
|
// add tissue patch at coordinates (x, y)
|
|
|
|
let entry = map.entry((x, y)).or_default();
|
|
|
|
*entry += 1;
|
|
|
|
|
|
|
|
// count overlap the first time only
|
|
|
|
if *entry == 2 {
|
|
|
|
res += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
|
2019-11-15 17:25:40 +01:00
|
|
|
fn part2(input: &str) -> Result<usize> {
|
2019-11-18 13:56:50 +01:00
|
|
|
let mut map: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
|
2019-11-15 17:25:40 +01:00
|
|
|
let mut set = HashSet::new();
|
|
|
|
|
|
|
|
for line in input.lines() {
|
2019-11-18 13:56:50 +01:00
|
|
|
let claim = parse(line).ok_or_else(|| err!("Couldn't parse line: {}", line))?;
|
2019-11-15 17:25:40 +01:00
|
|
|
set.insert(claim.id);
|
|
|
|
|
|
|
|
for i in 0..claim.width {
|
|
|
|
for j in 0..claim.height {
|
|
|
|
let x = claim.x + i;
|
|
|
|
let y = claim.y + j;
|
|
|
|
|
|
|
|
// add tissue patch at coordinates (x, y)
|
|
|
|
let entry = map.entry((x, y)).or_default();
|
|
|
|
entry.push(claim.id);
|
|
|
|
|
|
|
|
// if overlap, remove claims from possible solutions
|
|
|
|
if entry.len() > 1 {
|
|
|
|
for id in entry {
|
|
|
|
set.remove(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert!(!set.is_empty());
|
|
|
|
Ok(set.into_iter().next().unwrap())
|
|
|
|
}
|
|
|
|
|
2019-11-15 16:11:58 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2019-11-15 17:25:40 +01:00
|
|
|
const PROVIDED: &str = "#1 @ 1,3: 4x4
|
2019-11-15 16:11:58 +01:00
|
|
|
#2 @ 3,1: 4x4
|
|
|
|
#3 @ 5,5: 2x2
|
|
|
|
";
|
2019-11-15 17:25:40 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part1_provided() {
|
|
|
|
assert_eq!(part1(PROVIDED).unwrap(), 4);
|
2019-11-15 16:11:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part1_real() {
|
|
|
|
assert_eq!(part1(INPUT).unwrap(), 114946);
|
|
|
|
}
|
2019-11-15 17:25:40 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part2_provided() {
|
|
|
|
assert_eq!(part2(PROVIDED).unwrap(), 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part2_real() {
|
|
|
|
assert_eq!(part2(INPUT).unwrap(), 877);
|
|
|
|
}
|
2019-11-15 16:11:58 +01:00
|
|
|
}
|