convert line endings to unix
This commit is contained in:
parent
5215f3bb5f
commit
ca4fb1d3ad
444
src/bin/day17.rs
444
src/bin/day17.rs
@ -1,222 +1,222 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../../data/day17");
|
const INPUT: &str = include_str!("../../data/day17");
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
struct Dimension {
|
struct Dimension {
|
||||||
offset: u32,
|
offset: u32,
|
||||||
size: u32,
|
size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dimension {
|
impl Dimension {
|
||||||
fn map(&self, pos: i32) -> Option<usize> {
|
fn map(&self, pos: i32) -> Option<usize> {
|
||||||
let p = (self.offset as i32) + pos;
|
let p = (self.offset as i32) + pos;
|
||||||
if p >= 0 && p < (self.size as i32) {
|
if p >= 0 && p < (self.size as i32) {
|
||||||
Some(p as usize)
|
Some(p as usize)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn include(&self, pos: i32) -> Self {
|
fn include(&self, pos: i32) -> Self {
|
||||||
let left = std::cmp::min(pos, -(self.offset as i32));
|
let left = std::cmp::min(pos, -(self.offset as i32));
|
||||||
let right = std::cmp::max(pos, (self.size - self.offset - 1) as i32);
|
let right = std::cmp::max(pos, (self.size - self.offset - 1) as i32);
|
||||||
Self {
|
Self {
|
||||||
offset: (-left) as u32,
|
offset: (-left) as u32,
|
||||||
size: (right - left + 1) as u32,
|
size: (right - left + 1) as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend(&self) -> Self {
|
fn extend(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
offset: self.offset + 1,
|
offset: self.offset + 1,
|
||||||
size: self.size + 2,
|
size: self.size + 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for Dimension {
|
impl IntoIterator for Dimension {
|
||||||
type IntoIter = Range<i32>;
|
type IntoIter = Range<i32>;
|
||||||
type Item = i32;
|
type Item = i32;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
-(self.offset as i32)..((self.size - self.offset) as i32)
|
-(self.offset as i32)..((self.size - self.offset) as i32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Field {
|
struct Field {
|
||||||
x_dim: Dimension,
|
x_dim: Dimension,
|
||||||
y_dim: Dimension,
|
y_dim: Dimension,
|
||||||
z_dim: Dimension,
|
z_dim: Dimension,
|
||||||
w_dim: Dimension,
|
w_dim: Dimension,
|
||||||
cells: Vec<bool>,
|
cells: Vec<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Field {
|
impl Default for Field {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
x_dim: Dimension { offset: 0, size: 1},
|
x_dim: Dimension { offset: 0, size: 1},
|
||||||
y_dim: Dimension { offset: 0, size: 1},
|
y_dim: Dimension { offset: 0, size: 1},
|
||||||
z_dim: Dimension { offset: 0, size: 1},
|
z_dim: Dimension { offset: 0, size: 1},
|
||||||
w_dim: Dimension { offset: 0, size: 1},
|
w_dim: Dimension { offset: 0, size: 1},
|
||||||
cells: vec![false],
|
cells: vec![false],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Field {
|
impl Field {
|
||||||
fn alloc(x_dim: Dimension, y_dim: Dimension, z_dim: Dimension, w_dim: Dimension) -> Self {
|
fn alloc(x_dim: Dimension, y_dim: Dimension, z_dim: Dimension, w_dim: Dimension) -> Self {
|
||||||
let mut cells = Vec::new();
|
let mut cells = Vec::new();
|
||||||
let size = (x_dim.size * y_dim.size * z_dim.size * w_dim.size) as usize;
|
let size = (x_dim.size * y_dim.size * z_dim.size * w_dim.size) as usize;
|
||||||
cells.resize(size, false);
|
cells.resize(size, false);
|
||||||
Self { x_dim, y_dim, z_dim, w_dim, cells }
|
Self { x_dim, y_dim, z_dim, w_dim, cells }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _addr(&self, x: i32, y: i32, z: i32, w: i32) -> Option<usize> {
|
fn _addr(&self, x: i32, y: i32, z: i32, w: i32) -> Option<usize> {
|
||||||
let x = self.x_dim.map(x)?;
|
let x = self.x_dim.map(x)?;
|
||||||
let y = self.y_dim.map(y)?;
|
let y = self.y_dim.map(y)?;
|
||||||
let z = self.z_dim.map(z)?;
|
let z = self.z_dim.map(z)?;
|
||||||
let w = self.w_dim.map(w)?;
|
let w = self.w_dim.map(w)?;
|
||||||
let addr = ((w * (self.z_dim.size as usize) + z) * (self.y_dim.size as usize) + y) * (self.x_dim.size as usize) + x;
|
let addr = ((w * (self.z_dim.size as usize) + z) * (self.y_dim.size as usize) + y) * (self.x_dim.size as usize) + x;
|
||||||
Some(addr)
|
Some(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _get(&self, x: i32, y: i32, z: i32, w: i32) -> Option<bool> {
|
fn _get(&self, x: i32, y: i32, z: i32, w: i32) -> Option<bool> {
|
||||||
let addr = self._addr(x, y, z, w)?;
|
let addr = self._addr(x, y, z, w)?;
|
||||||
Some(self.cells[addr])
|
Some(self.cells[addr])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, x: i32, y: i32, z: i32, w: i32) -> bool {
|
fn get(&self, x: i32, y: i32, z: i32, w: i32) -> bool {
|
||||||
self._get(x, y, z, w).unwrap_or(false)
|
self._get(x, y, z, w).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, x: i32, y: i32, z: i32, w: i32, value: bool) {
|
fn set(&mut self, x: i32, y: i32, z: i32, w: i32, value: bool) {
|
||||||
if let Some(addr) = self._addr(x, y, z, w) {
|
if let Some(addr) = self._addr(x, y, z, w) {
|
||||||
self.cells[addr] = value;
|
self.cells[addr] = value;
|
||||||
} else if value {
|
} else if value {
|
||||||
// don't resize to set "false"
|
// don't resize to set "false"
|
||||||
let x_dim = self.x_dim.include(x);
|
let x_dim = self.x_dim.include(x);
|
||||||
let y_dim = self.y_dim.include(y);
|
let y_dim = self.y_dim.include(y);
|
||||||
let z_dim = self.z_dim.include(z);
|
let z_dim = self.z_dim.include(z);
|
||||||
let w_dim = self.w_dim.include(w);
|
let w_dim = self.w_dim.include(w);
|
||||||
let mut new_field = Self::alloc(x_dim, y_dim, z_dim, w_dim);
|
let mut new_field = Self::alloc(x_dim, y_dim, z_dim, w_dim);
|
||||||
for w in self.w_dim {
|
for w in self.w_dim {
|
||||||
for z in self.z_dim {
|
for z in self.z_dim {
|
||||||
for y in self.y_dim {
|
for y in self.y_dim {
|
||||||
for x in self.x_dim {
|
for x in self.x_dim {
|
||||||
let addr = new_field._addr(x, y, z, w).unwrap();
|
let addr = new_field._addr(x, y, z, w).unwrap();
|
||||||
new_field.cells[addr] = self.get(x, y, z, w);
|
new_field.cells[addr] = self.get(x, y, z, w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let addr = new_field._addr(x, y, z, w).unwrap();
|
let addr = new_field._addr(x, y, z, w).unwrap();
|
||||||
new_field.cells[addr] = value;
|
new_field.cells[addr] = value;
|
||||||
*self = new_field;
|
*self = new_field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(data: &str) -> Self {
|
fn parse(data: &str) -> Self {
|
||||||
let mut this = Self::default();
|
let mut this = Self::default();
|
||||||
for (y, line) in data.lines().enumerate() {
|
for (y, line) in data.lines().enumerate() {
|
||||||
for (x, chr) in line.chars().enumerate() {
|
for (x, chr) in line.chars().enumerate() {
|
||||||
this.set(x as i32, y as i32, 0, 0, chr == '#');
|
this.set(x as i32, y as i32, 0, 0, chr == '#');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_active_neighbors(&self, x: i32, y: i32, z: i32, w: i32) -> u32 {
|
fn count_active_neighbors(&self, x: i32, y: i32, z: i32, w: i32) -> u32 {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for dw in -1..=1 {
|
for dw in -1..=1 {
|
||||||
for dz in -1..=1 {
|
for dz in -1..=1 {
|
||||||
for dy in -1..=1 {
|
for dy in -1..=1 {
|
||||||
for dx in -1..=1 {
|
for dx in -1..=1 {
|
||||||
if dw == 0 && dz == 0 && dy == 0 && dx == 0 {
|
if dw == 0 && dz == 0 && dy == 0 && dx == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if self.get(x + dx, y + dy, z + dz, w + dw) {
|
if self.get(x + dx, y + dy, z + dz, w + dw) {
|
||||||
count += 1;
|
count += 1;
|
||||||
if count > 3 { return count; } // more than 3 is always bad
|
if count > 3 { return count; } // more than 3 is always bad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cycle_3dim(&self) -> Self {
|
fn cycle_3dim(&self) -> Self {
|
||||||
let mut result = self.clone();
|
let mut result = self.clone();
|
||||||
let w = 0;
|
let w = 0;
|
||||||
for z in self.z_dim.extend() {
|
for z in self.z_dim.extend() {
|
||||||
for y in self.y_dim.extend() {
|
for y in self.y_dim.extend() {
|
||||||
for x in self.x_dim.extend() {
|
for x in self.x_dim.extend() {
|
||||||
let count = self.count_active_neighbors(x, y, z, w);
|
let count = self.count_active_neighbors(x, y, z, w);
|
||||||
let active = count == 3 || (count == 2 && self.get(x, y, z, w));
|
let active = count == 3 || (count == 2 && self.get(x, y, z, w));
|
||||||
result.set(x, y, z, w, active);
|
result.set(x, y, z, w, active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cycle_4dim(&self) -> Self {
|
fn cycle_4dim(&self) -> Self {
|
||||||
let mut result = self.clone();
|
let mut result = self.clone();
|
||||||
for w in self.w_dim.extend() {
|
for w in self.w_dim.extend() {
|
||||||
for z in self.z_dim.extend() {
|
for z in self.z_dim.extend() {
|
||||||
for y in self.y_dim.extend() {
|
for y in self.y_dim.extend() {
|
||||||
for x in self.x_dim.extend() {
|
for x in self.x_dim.extend() {
|
||||||
let count = self.count_active_neighbors(x, y, z, w);
|
let count = self.count_active_neighbors(x, y, z, w);
|
||||||
let active = count == 3 || (count == 2 && self.get(x, y, z, w));
|
let active = count == 3 || (count == 2 && self.get(x, y, z, w));
|
||||||
result.set(x, y, z, w, active);
|
result.set(x, y, z, w, active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Field {
|
impl std::fmt::Debug for Field {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
for z in self.z_dim {
|
for z in self.z_dim {
|
||||||
for w in self.w_dim {
|
for w in self.w_dim {
|
||||||
writeln!(f, "z = {}, w = {}", z, w)?;
|
writeln!(f, "z = {}, w = {}", z, w)?;
|
||||||
for y in self.y_dim {
|
for y in self.y_dim {
|
||||||
for x in self.x_dim {
|
for x in self.x_dim {
|
||||||
if x == 0 && y == 0 {
|
if x == 0 && y == 0 {
|
||||||
f.write_char(
|
f.write_char(
|
||||||
if self.get(x, y, z, w) { 'X' } else { '+' }
|
if self.get(x, y, z, w) { 'X' } else { '+' }
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
f.write_char(
|
f.write_char(
|
||||||
if self.get(x, y, z, w) { '#' } else { '.' }
|
if self.get(x, y, z, w) { '#' } else { '.' }
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.write_char('\n')?;
|
f.write_char('\n')?;
|
||||||
}
|
}
|
||||||
f.write_char('\n')?;
|
f.write_char('\n')?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let input = Field::parse(INPUT);
|
let input = Field::parse(INPUT);
|
||||||
let mut field = input.clone();
|
let mut field = input.clone();
|
||||||
for _ in 0..6 {
|
for _ in 0..6 {
|
||||||
field = field.cycle_3dim();
|
field = field.cycle_3dim();
|
||||||
}
|
}
|
||||||
println!("Active after 6 3-dim rounds: {}", field.cells.iter().filter(|&&c| c).count());
|
println!("Active after 6 3-dim rounds: {}", field.cells.iter().filter(|&&c| c).count());
|
||||||
let mut field = input.clone();
|
let mut field = input.clone();
|
||||||
for _ in 0..6 {
|
for _ in 0..6 {
|
||||||
field = field.cycle_4dim();
|
field = field.cycle_4dim();
|
||||||
}
|
}
|
||||||
println!("Active after 6 4-dim rounds: {}", field.cells.iter().filter(|&&c| c).count());
|
println!("Active after 6 4-dim rounds: {}", field.cells.iter().filter(|&&c| c).count());
|
||||||
println!("{:?}", input);
|
println!("{:?}", input);
|
||||||
}
|
}
|
||||||
|
338
src/bin/day18.rs
338
src/bin/day18.rs
@ -1,169 +1,169 @@
|
|||||||
const INPUT: &str = include_str!("../../data/day18");
|
const INPUT: &str = include_str!("../../data/day18");
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
enum Op {
|
enum Op {
|
||||||
Sum,
|
Sum,
|
||||||
Mul,
|
Mul,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Op {
|
impl Op {
|
||||||
fn apply<T>(self, a: T, b: T) -> T
|
fn apply<T>(self, a: T, b: T) -> T
|
||||||
where
|
where
|
||||||
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T>,
|
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T>,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
Self::Sum => a + b,
|
Self::Sum => a + b,
|
||||||
Self::Mul => a * b,
|
Self::Mul => a * b,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
enum Elem<T> {
|
enum Elem<T> {
|
||||||
ParenLeft,
|
ParenLeft,
|
||||||
Op(Op),
|
Op(Op),
|
||||||
Item(T),
|
Item(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Elem<T> {
|
impl<T> Elem<T> {
|
||||||
fn expect_item(self) -> T {
|
fn expect_item(self) -> T {
|
||||||
if let Elem::Item(item) = self {
|
if let Elem::Item(item) = self {
|
||||||
item
|
item
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected item");
|
panic!("Expected item");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_op(self) -> Op {
|
fn expect_op(self) -> Op {
|
||||||
if let Elem::Op(op) = self {
|
if let Elem::Op(op) = self {
|
||||||
op
|
op
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected op");
|
panic!("Expected op");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_paren_left(self) {
|
fn expect_paren_left(self) {
|
||||||
if let Elem::ParenLeft = self {
|
if let Elem::ParenLeft = self {
|
||||||
()
|
()
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected '('");
|
panic!("Expected '('");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Stack<T> {
|
struct Stack<T> {
|
||||||
elems: Vec<Elem<T>>,
|
elems: Vec<Elem<T>>,
|
||||||
same_precedence: bool,
|
same_precedence: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Stack<T>
|
impl<T> Stack<T>
|
||||||
where
|
where
|
||||||
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::fmt::Debug,
|
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::fmt::Debug,
|
||||||
{
|
{
|
||||||
fn new(same_precedence: bool) -> Self {
|
fn new(same_precedence: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
elems: Vec::new(),
|
elems: Vec::new(),
|
||||||
same_precedence,
|
same_precedence,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_item(&mut self, item: T) {
|
fn push_item(&mut self, item: T) {
|
||||||
self.elems.push(Elem::Item(item))
|
self.elems.push(Elem::Item(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_op(&mut self, op: Op) {
|
fn push_op(&mut self, op: Op) {
|
||||||
self.elems.push(Elem::Op(op))
|
self.elems.push(Elem::Op(op))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_paren_left(&mut self) {
|
fn push_paren_left(&mut self) {
|
||||||
self.elems.push(Elem::ParenLeft)
|
self.elems.push(Elem::ParenLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _peek_top(&self) -> &Elem<T> {
|
fn _peek_top(&self) -> &Elem<T> {
|
||||||
&self.elems[self.elems.len() - 1]
|
&self.elems[self.elems.len() - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _peek_below_top(&self) -> &Elem<T> {
|
fn _peek_below_top(&self) -> &Elem<T> {
|
||||||
&self.elems[self.elems.len() - 2]
|
&self.elems[self.elems.len() - 2]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_item(&mut self) -> T {
|
fn pop_item(&mut self) -> T {
|
||||||
self.elems.pop().expect("expect non empty stack").expect_item()
|
self.elems.pop().expect("expect non empty stack").expect_item()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_op(&mut self) -> Op {
|
fn pop_op(&mut self) -> Op {
|
||||||
self.elems.pop().expect("expect non empty stack").expect_op()
|
self.elems.pop().expect("expect non empty stack").expect_op()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_paren_left(&mut self) {
|
fn pop_paren_left(&mut self) {
|
||||||
self.elems.pop().expect("expect non empty stack").expect_paren_left()
|
self.elems.pop().expect("expect non empty stack").expect_paren_left()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _do_reduce_op(&self, finish: bool) -> bool {
|
fn _do_reduce_op(&self, finish: bool) -> bool {
|
||||||
match self._peek_below_top() {
|
match self._peek_below_top() {
|
||||||
Elem::Op(Op::Sum) => true,
|
Elem::Op(Op::Sum) => true,
|
||||||
Elem::Op(Op::Mul) => finish || self.same_precedence,
|
Elem::Op(Op::Mul) => finish || self.same_precedence,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
self.elems.is_empty()
|
self.elems.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_top(&mut self, finish: bool) {
|
fn eval_top(&mut self, finish: bool) {
|
||||||
while self.elems.len() > 2 && matches!(self._peek_top(), Elem::Item(_)) && self._do_reduce_op(finish) {
|
while self.elems.len() > 2 && matches!(self._peek_top(), Elem::Item(_)) && self._do_reduce_op(finish) {
|
||||||
let value2 = self.pop_item();
|
let value2 = self.pop_item();
|
||||||
let op = self.pop_op();
|
let op = self.pop_op();
|
||||||
let value1 = self.pop_item();
|
let value1 = self.pop_item();
|
||||||
self.push_item(op.apply(value1, value2));
|
self.push_item(op.apply(value1, value2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse<T>(mut line: &str, same_precedence: bool) -> T
|
fn parse<T>(mut line: &str, same_precedence: bool) -> T
|
||||||
where
|
where
|
||||||
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::str::FromStr + std::fmt::Debug,
|
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::str::FromStr + std::fmt::Debug,
|
||||||
<T as std::str::FromStr>::Err: std::fmt::Debug,
|
<T as std::str::FromStr>::Err: std::fmt::Debug,
|
||||||
{
|
{
|
||||||
let mut stack = Stack::new(same_precedence);
|
let mut stack = Stack::new(same_precedence);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
line = line.trim();
|
line = line.trim();
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if let Some(rem) = line.strip_prefix('+') {
|
if let Some(rem) = line.strip_prefix('+') {
|
||||||
stack.push_op(Op::Sum);
|
stack.push_op(Op::Sum);
|
||||||
line = rem;
|
line = rem;
|
||||||
} else if let Some(rem) = line.strip_prefix('*') {
|
} else if let Some(rem) = line.strip_prefix('*') {
|
||||||
stack.push_op(Op::Mul);
|
stack.push_op(Op::Mul);
|
||||||
line = rem;
|
line = rem;
|
||||||
} else if let Some(rem) = line.strip_prefix('(') {
|
} else if let Some(rem) = line.strip_prefix('(') {
|
||||||
stack.push_paren_left();
|
stack.push_paren_left();
|
||||||
line = rem;
|
line = rem;
|
||||||
} else if let Some(rem) = line.strip_prefix(')') {
|
} else if let Some(rem) = line.strip_prefix(')') {
|
||||||
stack.eval_top(true);
|
stack.eval_top(true);
|
||||||
let item = stack.pop_item();
|
let item = stack.pop_item();
|
||||||
stack.pop_paren_left();
|
stack.pop_paren_left();
|
||||||
stack.push_item(item);
|
stack.push_item(item);
|
||||||
line = rem;
|
line = rem;
|
||||||
} else {
|
} else {
|
||||||
let num_end = line.find(|c: char| !c.is_ascii_digit()).unwrap_or(line.len());
|
let num_end = line.find(|c: char| !c.is_ascii_digit()).unwrap_or(line.len());
|
||||||
let num = line[..num_end].parse::<T>().unwrap();
|
let num = line[..num_end].parse::<T>().unwrap();
|
||||||
stack.push_item(num);
|
stack.push_item(num);
|
||||||
line = &line[num_end..];
|
line = &line[num_end..];
|
||||||
}
|
}
|
||||||
stack.eval_top(false);
|
stack.eval_top(false);
|
||||||
}
|
}
|
||||||
stack.eval_top(true);
|
stack.eval_top(true);
|
||||||
let item = stack.pop_item();
|
let item = stack.pop_item();
|
||||||
assert!(stack.is_empty());
|
assert!(stack.is_empty());
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Sum of evaluated lines (simple math): {}", INPUT.lines().map(|l| parse::<u64>(l, true)).sum::<u64>());
|
println!("Sum of evaluated lines (simple math): {}", INPUT.lines().map(|l| parse::<u64>(l, true)).sum::<u64>());
|
||||||
println!("Sum of evaluated lines (advanced math): {}", INPUT.lines().map(|l| parse::<u64>(l, false)).sum::<u64>());
|
println!("Sum of evaluated lines (advanced math): {}", INPUT.lines().map(|l| parse::<u64>(l, false)).sum::<u64>());
|
||||||
}
|
}
|
||||||
|
406
src/bin/day19.rs
406
src/bin/day19.rs
@ -1,203 +1,203 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../../data/day19");
|
const INPUT: &str = include_str!("../../data/day19");
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Rule {
|
enum Rule {
|
||||||
Char(char),
|
Char(char),
|
||||||
Chain(Vec<u32>),
|
Chain(Vec<u32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rule {
|
impl Rule {
|
||||||
fn parse_part(alt: &str) -> Self {
|
fn parse_part(alt: &str) -> Self {
|
||||||
let alt = alt.trim();
|
let alt = alt.trim();
|
||||||
if let Some(alt) = alt.strip_prefix('"') {
|
if let Some(alt) = alt.strip_prefix('"') {
|
||||||
let alt = alt.strip_suffix('"').unwrap();
|
let alt = alt.strip_suffix('"').unwrap();
|
||||||
assert_eq!(alt.len(), 1);
|
assert_eq!(alt.len(), 1);
|
||||||
Self::Char(alt.chars().next().unwrap())
|
Self::Char(alt.chars().next().unwrap())
|
||||||
} else {
|
} else {
|
||||||
Self::Chain(alt.split_whitespace().map(|s| s.parse::<u32>().unwrap()).collect())
|
Self::Chain(alt.split_whitespace().map(|s| s.parse::<u32>().unwrap()).collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_line(line: &str) -> (u32, CachedRule) {
|
fn parse_line(line: &str) -> (u32, CachedRule) {
|
||||||
let colon = line.find(": ").unwrap();
|
let colon = line.find(": ").unwrap();
|
||||||
let id = line[..colon].parse::<u32>().unwrap();
|
let id = line[..colon].parse::<u32>().unwrap();
|
||||||
let alts = line[colon+2..].split(" | ").map(Self::parse_part).collect();
|
let alts = line[colon+2..].split(" | ").map(Self::parse_part).collect();
|
||||||
(id, CachedRule::Alternatives(alts))
|
(id, CachedRule::Alternatives(alts))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _make_combs(target: &mut HashSet<String>, current: &mut String, list: &[Arc<HashSet<String>>]) {
|
fn _make_combs(target: &mut HashSet<String>, current: &mut String, list: &[Arc<HashSet<String>>]) {
|
||||||
if list.is_empty() {
|
if list.is_empty() {
|
||||||
target.insert(current.clone());
|
target.insert(current.clone());
|
||||||
} else {
|
} else {
|
||||||
let old_len = current.len();
|
let old_len = current.len();
|
||||||
for s in &*list[0] {
|
for s in &*list[0] {
|
||||||
current.push_str(s);
|
current.push_str(s);
|
||||||
_make_combs(target, current, &list[1..]);
|
_make_combs(target, current, &list[1..]);
|
||||||
current.truncate(old_len);
|
current.truncate(old_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum CachedRule {
|
enum CachedRule {
|
||||||
Alternatives(Vec<Rule>),
|
Alternatives(Vec<Rule>),
|
||||||
CachedStrings(Arc<HashSet<String>>, HashSet<usize>),
|
CachedStrings(Arc<HashSet<String>>, HashSet<usize>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct Grammar {
|
struct Grammar {
|
||||||
expansions: HashMap<u32, CachedRule>,
|
expansions: HashMap<u32, CachedRule>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Grammar {
|
impl Grammar {
|
||||||
fn parse(rule_lines: &str) -> Self {
|
fn parse(rule_lines: &str) -> Self {
|
||||||
let expansions = rule_lines.lines().map(Rule::parse_line).collect();
|
let expansions = rule_lines.lines().map(Rule::parse_line).collect();
|
||||||
Self { expansions }
|
Self { expansions }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _build_sets(&mut self, recursive_check: &mut HashSet<u32>, ndx: u32) -> Option<Arc<HashSet<String>>> {
|
fn _build_sets(&mut self, recursive_check: &mut HashSet<u32>, ndx: u32) -> Option<Arc<HashSet<String>>> {
|
||||||
let rules = self.expansions.get(&ndx).unwrap();
|
let rules = self.expansions.get(&ndx).unwrap();
|
||||||
let alts = match rules {
|
let alts = match rules {
|
||||||
CachedRule::Alternatives(alts) => alts,
|
CachedRule::Alternatives(alts) => alts,
|
||||||
CachedRule::CachedStrings(cs, _) => return Some(cs.clone()),
|
CachedRule::CachedStrings(cs, _) => return Some(cs.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if recursive_check.contains(&ndx) {
|
if recursive_check.contains(&ndx) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
recursive_check.insert(ndx);
|
recursive_check.insert(ndx);
|
||||||
|
|
||||||
let mut result = HashSet::<String>::new();
|
let mut result = HashSet::<String>::new();
|
||||||
|
|
||||||
let mut found_recursion = false;
|
let mut found_recursion = false;
|
||||||
for rule in alts.clone() {
|
for rule in alts.clone() {
|
||||||
match rule {
|
match rule {
|
||||||
Rule::Char(c) => {
|
Rule::Char(c) => {
|
||||||
result.insert(c.to_string());
|
result.insert(c.to_string());
|
||||||
},
|
},
|
||||||
Rule::Chain(parts) => {
|
Rule::Chain(parts) => {
|
||||||
let mut comb = Vec::new();
|
let mut comb = Vec::new();
|
||||||
for part_ndx in parts {
|
for part_ndx in parts {
|
||||||
// abort if nested lookup loops
|
// abort if nested lookup loops
|
||||||
if let Some(cs) = self._build_sets(recursive_check, part_ndx) {
|
if let Some(cs) = self._build_sets(recursive_check, part_ndx) {
|
||||||
comb.push(cs);
|
comb.push(cs);
|
||||||
} else {
|
} else {
|
||||||
found_recursion = true;
|
found_recursion = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found_recursion {
|
if !found_recursion {
|
||||||
_make_combs(&mut result, &mut String::new(), &comb);
|
_make_combs(&mut result, &mut String::new(), &comb);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if found_recursion {
|
if found_recursion {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let lengths: HashSet<usize> = result.iter().map(|s| s.len()).collect();
|
let lengths: HashSet<usize> = result.iter().map(|s| s.len()).collect();
|
||||||
let result = Arc::new(result);
|
let result = Arc::new(result);
|
||||||
recursive_check.remove(&ndx);
|
recursive_check.remove(&ndx);
|
||||||
self.expansions.insert(ndx, CachedRule::CachedStrings(result.clone(), lengths));
|
self.expansions.insert(ndx, CachedRule::CachedStrings(result.clone(), lengths));
|
||||||
Some(result)
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optimize(&mut self) {
|
fn optimize(&mut self) {
|
||||||
let mut recursive_check = HashSet::new();
|
let mut recursive_check = HashSet::new();
|
||||||
self._build_sets(&mut recursive_check, 0);
|
self._build_sets(&mut recursive_check, 0);
|
||||||
recursive_check.insert(0);
|
recursive_check.insert(0);
|
||||||
for id in recursive_check.clone() {
|
for id in recursive_check.clone() {
|
||||||
if let Some(CachedRule::Alternatives(alts)) = self.expansions.get(&id) {
|
if let Some(CachedRule::Alternatives(alts)) = self.expansions.get(&id) {
|
||||||
for alt in alts {
|
for alt in alts {
|
||||||
if let Rule::Chain(chain) = alt {
|
if let Rule::Chain(chain) = alt {
|
||||||
recursive_check.extend(chain);
|
recursive_check.extend(chain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let rule_ids: HashSet<u32> = self.expansions.keys().cloned().collect();
|
let rule_ids: HashSet<u32> = self.expansions.keys().cloned().collect();
|
||||||
for id in rule_ids.difference(&recursive_check) {
|
for id in rule_ids.difference(&recursive_check) {
|
||||||
self.expansions.remove(id);
|
self.expansions.remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simple_match(&self, data: &str) -> bool {
|
fn simple_match(&self, data: &str) -> bool {
|
||||||
match self.expansions.get(&0) {
|
match self.expansions.get(&0) {
|
||||||
Some(CachedRule::CachedStrings(cs, _)) => cs.contains(data),
|
Some(CachedRule::CachedStrings(cs, _)) => cs.contains(data),
|
||||||
_ => panic!("not simple enough"),
|
_ => panic!("not simple enough"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _complex_match_chain<'data>(&self, chain: &[u32], data: &'data str) -> Vec<&'data str> {
|
fn _complex_match_chain<'data>(&self, chain: &[u32], data: &'data str) -> Vec<&'data str> {
|
||||||
if chain.is_empty() {
|
if chain.is_empty() {
|
||||||
return vec![data];
|
return vec![data];
|
||||||
}
|
}
|
||||||
self._complex_match(chain[0], data).into_iter().map(|rem| {
|
self._complex_match(chain[0], data).into_iter().map(|rem| {
|
||||||
self._complex_match_chain(&chain[1..], rem)
|
self._complex_match_chain(&chain[1..], rem)
|
||||||
}).flatten().collect()
|
}).flatten().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _complex_match<'data>(&self, start: u32, data: &'data str) -> Vec<&'data str> {
|
fn _complex_match<'data>(&self, start: u32, data: &'data str) -> Vec<&'data str> {
|
||||||
let cr = self.expansions.get(&start).unwrap();
|
let cr = self.expansions.get(&start).unwrap();
|
||||||
match cr {
|
match cr {
|
||||||
CachedRule::Alternatives(alts) => {
|
CachedRule::Alternatives(alts) => {
|
||||||
alts.iter().map(|rule| {
|
alts.iter().map(|rule| {
|
||||||
let chain = match rule {
|
let chain = match rule {
|
||||||
Rule::Chain(chain) => chain,
|
Rule::Chain(chain) => chain,
|
||||||
_ => panic!("only chains here"),
|
_ => panic!("only chains here"),
|
||||||
};
|
};
|
||||||
self._complex_match_chain(&chain, data)
|
self._complex_match_chain(&chain, data)
|
||||||
}).flatten().collect()
|
}).flatten().collect()
|
||||||
},
|
},
|
||||||
CachedRule::CachedStrings(cs, lens) => {
|
CachedRule::CachedStrings(cs, lens) => {
|
||||||
lens.iter().filter_map(|&len| {
|
lens.iter().filter_map(|&len| {
|
||||||
if data.len() >= len && cs.contains(&data[..len]) {
|
if data.len() >= len && cs.contains(&data[..len]) {
|
||||||
Some(&data[len..])
|
Some(&data[len..])
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).collect()
|
}).collect()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complex_match(&self, data: &str) -> bool {
|
fn complex_match(&self, data: &str) -> bool {
|
||||||
self._complex_match(0, data).into_iter().any(str::is_empty)
|
self._complex_match(0, data).into_iter().any(str::is_empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let input_grammar;
|
let input_grammar;
|
||||||
let data;
|
let data;
|
||||||
{
|
{
|
||||||
let split_pos = INPUT.find("\n\n").unwrap();
|
let split_pos = INPUT.find("\n\n").unwrap();
|
||||||
input_grammar = Grammar::parse(&INPUT[..split_pos]);
|
input_grammar = Grammar::parse(&INPUT[..split_pos]);
|
||||||
data = INPUT[split_pos+2..].lines().map(str::to_string).collect::<Vec<String>>();
|
data = INPUT[split_pos+2..].lines().map(str::to_string).collect::<Vec<String>>();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let mut grammar = input_grammar.clone();
|
let mut grammar = input_grammar.clone();
|
||||||
grammar.optimize();
|
grammar.optimize();
|
||||||
println!("Lines matching simple grammar: {}", data.iter().filter(|line| grammar.simple_match(line)).count());
|
println!("Lines matching simple grammar: {}", data.iter().filter(|line| grammar.simple_match(line)).count());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let mut grammar = input_grammar.clone();
|
let mut grammar = input_grammar.clone();
|
||||||
grammar.expansions.extend(vec![
|
grammar.expansions.extend(vec![
|
||||||
Rule::parse_line("8: 42 | 42 8"),
|
Rule::parse_line("8: 42 | 42 8"),
|
||||||
Rule::parse_line("11: 42 31 | 42 11 31"),
|
Rule::parse_line("11: 42 31 | 42 11 31"),
|
||||||
]);
|
]);
|
||||||
grammar.optimize();
|
grammar.optimize();
|
||||||
/*
|
/*
|
||||||
println!("{:?}", grammar.expansions.keys());
|
println!("{:?}", grammar.expansions.keys());
|
||||||
for (rule_id, rule) in grammar.expansions {
|
for (rule_id, rule) in grammar.expansions {
|
||||||
match rule {
|
match rule {
|
||||||
CachedRule::Alternatives(alt) => println!("{} -> {:?}", rule_id, alt),
|
CachedRule::Alternatives(alt) => println!("{} -> {:?}", rule_id, alt),
|
||||||
CachedRule::CachedStrings(_, lens) => println!("{} -> string with lengths: {:?}", rule_id, lens),
|
CachedRule::CachedStrings(_, lens) => println!("{} -> string with lengths: {:?}", rule_id, lens),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
println!("Lines matching complex grammar: {}", data.iter().filter(|line| grammar.complex_match(line)).count());
|
println!("Lines matching complex grammar: {}", data.iter().filter(|line| grammar.complex_match(line)).count());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
932
src/bin/day20.rs
932
src/bin/day20.rs
@ -1,466 +1,466 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../../data/day20");
|
const INPUT: &str = include_str!("../../data/day20");
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
|
||||||
enum Side {
|
enum Side {
|
||||||
Top,
|
Top,
|
||||||
Right,
|
Right,
|
||||||
Bottom,
|
Bottom,
|
||||||
Left,
|
Left,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Side {
|
impl Side {
|
||||||
fn opposite(self) -> Self {
|
fn opposite(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Side::Top => Side::Bottom,
|
Side::Top => Side::Bottom,
|
||||||
Side::Right => Side::Left,
|
Side::Right => Side::Left,
|
||||||
Side::Bottom => Side::Top,
|
Side::Bottom => Side::Top,
|
||||||
Side::Left => Side::Right,
|
Side::Left => Side::Right,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform(self, width: usize, flip: bool, row: usize, col: usize) -> (usize, usize) {
|
fn transform(self, width: usize, flip: bool, row: usize, col: usize) -> (usize, usize) {
|
||||||
let (row, col) = if flip {
|
let (row, col) = if flip {
|
||||||
(row, width - col - 1)
|
(row, width - col - 1)
|
||||||
} else {
|
} else {
|
||||||
(row, col)
|
(row, col)
|
||||||
};
|
};
|
||||||
let (row, col) = match self {
|
let (row, col) = match self {
|
||||||
Side::Top => (row, col),
|
Side::Top => (row, col),
|
||||||
Side::Right => (col, width - row - 1),
|
Side::Right => (col, width - row - 1),
|
||||||
Side::Bottom => (width - row - 1, width - col - 1),
|
Side::Bottom => (width - row - 1, width - col - 1),
|
||||||
Side::Left => (width - col - 1, row),
|
Side::Left => (width - col - 1, row),
|
||||||
};
|
};
|
||||||
(row, col)
|
(row, col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||||
struct Edge {
|
struct Edge {
|
||||||
len: u32,
|
len: u32,
|
||||||
mask: u32,
|
mask: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Edge {
|
impl Edge {
|
||||||
fn new<I>(bits: I) -> Self
|
fn new<I>(bits: I) -> Self
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item=bool>,
|
I: IntoIterator<Item=bool>,
|
||||||
{
|
{
|
||||||
let (len, mask) = bits.into_iter().fold((0u32, 0u32), |(len, mask), bit| {
|
let (len, mask) = bits.into_iter().fold((0u32, 0u32), |(len, mask), bit| {
|
||||||
(len+1, (mask << 1) + if bit { 1 } else { 0 })
|
(len+1, (mask << 1) + if bit { 1 } else { 0 })
|
||||||
});
|
});
|
||||||
Edge { len, mask }
|
Edge { len, mask }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn norm_dir(self) -> Self {
|
fn norm_dir(self) -> Self {
|
||||||
let rev_mask = self.mask.reverse_bits() >> (32 - self.len);
|
let rev_mask = self.mask.reverse_bits() >> (32 - self.len);
|
||||||
if self.mask < rev_mask {
|
if self.mask < rev_mask {
|
||||||
Edge { len: self.len, mask: self.mask }
|
Edge { len: self.len, mask: self.mask }
|
||||||
} else {
|
} else {
|
||||||
Edge { len: self.len, mask: rev_mask }
|
Edge { len: self.len, mask: rev_mask }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Edge {
|
impl std::fmt::Debug for Edge {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
f.write_char('"')?;
|
f.write_char('"')?;
|
||||||
for ndx in 0..self.len {
|
for ndx in 0..self.len {
|
||||||
let v = 0 != self.mask & (1 << ndx);
|
let v = 0 != self.mask & (1 << ndx);
|
||||||
f.write_char(if v { '#' } else { '.' })?;
|
f.write_char(if v { '#' } else { '.' })?;
|
||||||
}
|
}
|
||||||
f.write_char('"')
|
f.write_char('"')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct Mask {
|
struct Mask {
|
||||||
lines: [[bool; 20]; 3],
|
lines: [[bool; 20]; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mask {
|
impl Mask {
|
||||||
const EMPTY: Mask = Mask { lines: [[false; 20]; 3] };
|
const EMPTY: Mask = Mask { lines: [[false; 20]; 3] };
|
||||||
const DEFAULT: Mask = {
|
const DEFAULT: Mask = {
|
||||||
let mut lines = Self::EMPTY.lines;
|
let mut lines = Self::EMPTY.lines;
|
||||||
lines[0][18] = true;
|
lines[0][18] = true;
|
||||||
lines[1][0] = true;
|
lines[1][0] = true;
|
||||||
lines[1][5] = true;
|
lines[1][5] = true;
|
||||||
lines[1][6] = true;
|
lines[1][6] = true;
|
||||||
lines[1][11] = true;
|
lines[1][11] = true;
|
||||||
lines[1][12] = true;
|
lines[1][12] = true;
|
||||||
lines[1][17] = true;
|
lines[1][17] = true;
|
||||||
lines[1][18] = true;
|
lines[1][18] = true;
|
||||||
lines[1][19] = true;
|
lines[1][19] = true;
|
||||||
lines[2][1] = true;
|
lines[2][1] = true;
|
||||||
lines[2][4] = true;
|
lines[2][4] = true;
|
||||||
lines[2][7] = true;
|
lines[2][7] = true;
|
||||||
lines[2][10] = true;
|
lines[2][10] = true;
|
||||||
lines[2][13] = true;
|
lines[2][13] = true;
|
||||||
lines[2][16] = true;
|
lines[2][16] = true;
|
||||||
Self { lines }
|
Self { lines }
|
||||||
};
|
};
|
||||||
|
|
||||||
fn _flip_y(self) -> Self {
|
fn _flip_y(self) -> Self {
|
||||||
let [a,b,c] = self.lines;
|
let [a,b,c] = self.lines;
|
||||||
Self { lines: [c, b, a] }
|
Self { lines: [c, b, a] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _flip_x(self) -> Self {
|
fn _flip_x(self) -> Self {
|
||||||
let [mut a, mut b, mut c] = self.lines;
|
let [mut a, mut b, mut c] = self.lines;
|
||||||
a.reverse();
|
a.reverse();
|
||||||
b.reverse();
|
b.reverse();
|
||||||
c.reverse();
|
c.reverse();
|
||||||
Self { lines: [a, b, c] }
|
Self { lines: [a, b, c] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn all_masks() -> Vec<Mask> {
|
fn all_masks() -> Vec<Mask> {
|
||||||
let def = Self::DEFAULT;
|
let def = Self::DEFAULT;
|
||||||
let flip_x = def._flip_x();
|
let flip_x = def._flip_x();
|
||||||
vec![
|
vec![
|
||||||
def,
|
def,
|
||||||
def._flip_y(),
|
def._flip_y(),
|
||||||
flip_x,
|
flip_x,
|
||||||
flip_x._flip_y(),
|
flip_x._flip_y(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_horizontal(&self, tile: &Tile, row: usize, col: usize) -> bool {
|
fn matches_horizontal(&self, tile: &Tile, row: usize, col: usize) -> bool {
|
||||||
if row + 3 > tile.width || col + 20 > tile.width {
|
if row + 3 > tile.width || col + 20 > tile.width {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for y in 0..3 {
|
for y in 0..3 {
|
||||||
for x in 0..20 {
|
for x in 0..20 {
|
||||||
if self.lines[y][x] && !tile[(row+y, col+x)] {
|
if self.lines[y][x] && !tile[(row+y, col+x)] {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_horizontal(&self, tile: &mut Tile, row: usize, col: usize) {
|
fn mark_horizontal(&self, tile: &mut Tile, row: usize, col: usize) {
|
||||||
for y in 0..3 {
|
for y in 0..3 {
|
||||||
for x in 0..20 {
|
for x in 0..20 {
|
||||||
if self.lines[y][x] {
|
if self.lines[y][x] {
|
||||||
tile[(row+y, col+x)] = true;
|
tile[(row+y, col+x)] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_vertical(&self, tile: &Tile, row: usize, col: usize) -> bool {
|
fn matches_vertical(&self, tile: &Tile, row: usize, col: usize) -> bool {
|
||||||
if row + 20 > tile.width || col + 3 > tile.width {
|
if row + 20 > tile.width || col + 3 > tile.width {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for y in 0..3 {
|
for y in 0..3 {
|
||||||
for x in 0..20 {
|
for x in 0..20 {
|
||||||
if self.lines[y][x] && !tile[(row+x, col+y)] {
|
if self.lines[y][x] && !tile[(row+x, col+y)] {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_vertical(&self, tile: &mut Tile, row: usize, col: usize) {
|
fn mark_vertical(&self, tile: &mut Tile, row: usize, col: usize) {
|
||||||
for y in 0..3 {
|
for y in 0..3 {
|
||||||
for x in 0..20 {
|
for x in 0..20 {
|
||||||
if self.lines[y][x] {
|
if self.lines[y][x] {
|
||||||
tile[(row+x, col+y)] = true;
|
tile[(row+x, col+y)] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Mask {
|
impl std::fmt::Debug for Mask {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
f.write_char('\n')?;
|
f.write_char('\n')?;
|
||||||
for y in 0..3 {
|
for y in 0..3 {
|
||||||
for x in 0..20 {
|
for x in 0..20 {
|
||||||
f.write_char(if self.lines[y][x] { '#' } else { '.' })?;
|
f.write_char(if self.lines[y][x] { '#' } else { '.' })?;
|
||||||
}
|
}
|
||||||
f.write_char('\n')?;
|
f.write_char('\n')?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
struct Tile {
|
struct Tile {
|
||||||
width: usize,
|
width: usize,
|
||||||
data: Vec<bool>,
|
data: Vec<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tile {
|
impl Tile {
|
||||||
fn parse(block: &str) -> (u32, Self) {
|
fn parse(block: &str) -> (u32, Self) {
|
||||||
let head_split = block.find(":\n").unwrap();
|
let head_split = block.find(":\n").unwrap();
|
||||||
let id = block[..head_split].strip_prefix("Tile ").unwrap().parse::<u32>().unwrap();
|
let id = block[..head_split].strip_prefix("Tile ").unwrap().parse::<u32>().unwrap();
|
||||||
let block = &block[head_split+2..];
|
let block = &block[head_split+2..];
|
||||||
let first_line = block.lines().next().unwrap();
|
let first_line = block.lines().next().unwrap();
|
||||||
let width = first_line.len();
|
let width = first_line.len();
|
||||||
let data = block.lines()
|
let data = block.lines()
|
||||||
.map(|l| {
|
.map(|l| {
|
||||||
assert_eq!(l.len(), width);
|
assert_eq!(l.len(), width);
|
||||||
l.chars().map(|c| c == '#')
|
l.chars().map(|c| c == '#')
|
||||||
})
|
})
|
||||||
.flatten().collect::<Vec<_>>();
|
.flatten().collect::<Vec<_>>();
|
||||||
assert_eq!(data.len(), width * width);
|
assert_eq!(data.len(), width * width);
|
||||||
|
|
||||||
(id, Self { width, data })
|
(id, Self { width, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edge(&self, side: Side) -> Edge {
|
fn edge(&self, side: Side) -> Edge {
|
||||||
match side {
|
match side {
|
||||||
Side::Top => Edge::new(self.data[..self.width].iter().copied()),
|
Side::Top => Edge::new(self.data[..self.width].iter().copied()),
|
||||||
Side::Bottom => Edge::new(self.data[self.width*(self.width-1)..].iter().copied()),
|
Side::Bottom => Edge::new(self.data[self.width*(self.width-1)..].iter().copied()),
|
||||||
Side::Left => Edge::new((0..self.width).map(|n| self.data[n*self.width])),
|
Side::Left => Edge::new((0..self.width).map(|n| self.data[n*self.width])),
|
||||||
Side::Right => Edge::new((0..self.width).map(|n| self.data[(n+1)*self.width-1])),
|
Side::Right => Edge::new((0..self.width).map(|n| self.data[(n+1)*self.width-1])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotate/flip given previous sides to top and left
|
// rotate/flip given previous sides to top and left
|
||||||
fn rotate(&self, top: Side, left: Side) -> Self {
|
fn rotate(&self, top: Side, left: Side) -> Self {
|
||||||
let flip = match (top, left) {
|
let flip = match (top, left) {
|
||||||
(Side::Top, Side::Left) => false,
|
(Side::Top, Side::Left) => false,
|
||||||
(Side::Top, Side::Right) => true,
|
(Side::Top, Side::Right) => true,
|
||||||
(Side::Right, Side::Top) => false,
|
(Side::Right, Side::Top) => false,
|
||||||
(Side::Right, Side::Bottom) => true,
|
(Side::Right, Side::Bottom) => true,
|
||||||
(Side::Bottom, Side::Right) => false,
|
(Side::Bottom, Side::Right) => false,
|
||||||
(Side::Bottom, Side::Left) => true,
|
(Side::Bottom, Side::Left) => true,
|
||||||
(Side::Left, Side::Bottom) => false,
|
(Side::Left, Side::Bottom) => false,
|
||||||
(Side::Left, Side::Top) => true,
|
(Side::Left, Side::Top) => true,
|
||||||
_ => panic!("invalid rotation: {:?} -> Top and {:?} -> Left", top, left),
|
_ => panic!("invalid rotation: {:?} -> Top and {:?} -> Left", top, left),
|
||||||
};
|
};
|
||||||
let data = (0..self.width).map(|row| {
|
let data = (0..self.width).map(|row| {
|
||||||
(0..self.width).map(move |col| {
|
(0..self.width).map(move |col| {
|
||||||
let (row, col) = top.transform(self.width, flip, row, col);
|
let (row, col) = top.transform(self.width, flip, row, col);
|
||||||
self[(row, col)]
|
self[(row, col)]
|
||||||
})
|
})
|
||||||
}).flatten().collect();
|
}).flatten().collect();
|
||||||
Self { width: self.width, data }
|
Self { width: self.width, data }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_monster(&self) -> Tile {
|
fn find_monster(&self) -> Tile {
|
||||||
let mut marked = Self { width: self.width, data: self.data.iter().map(|_| false).collect() };
|
let mut marked = Self { width: self.width, data: self.data.iter().map(|_| false).collect() };
|
||||||
let patterns = Mask::all_masks();
|
let patterns = Mask::all_masks();
|
||||||
for row in 0..self.width {
|
for row in 0..self.width {
|
||||||
for col in 0..self.width {
|
for col in 0..self.width {
|
||||||
for pattern in &patterns {
|
for pattern in &patterns {
|
||||||
if pattern.matches_horizontal(self, row, col) {
|
if pattern.matches_horizontal(self, row, col) {
|
||||||
pattern.mark_horizontal(&mut marked, row, col);
|
pattern.mark_horizontal(&mut marked, row, col);
|
||||||
}
|
}
|
||||||
if pattern.matches_vertical(self, row, col) {
|
if pattern.matches_vertical(self, row, col) {
|
||||||
pattern.mark_vertical(&mut marked, row, col);
|
pattern.mark_vertical(&mut marked, row, col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
marked
|
marked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Index<(usize, usize)> for Tile {
|
impl std::ops::Index<(usize, usize)> for Tile {
|
||||||
type Output = bool;
|
type Output = bool;
|
||||||
|
|
||||||
fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
|
fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
|
||||||
assert!(col <= self.width);
|
assert!(col <= self.width);
|
||||||
&self.data[row * self.width + col]
|
&self.data[row * self.width + col]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::IndexMut<(usize, usize)> for Tile {
|
impl std::ops::IndexMut<(usize, usize)> for Tile {
|
||||||
fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
|
fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
|
||||||
assert!(col <= self.width);
|
assert!(col <= self.width);
|
||||||
&mut self.data[row * self.width + col]
|
&mut self.data[row * self.width + col]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Tile {
|
impl std::fmt::Debug for Tile {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
f.write_char('\n')?;
|
f.write_char('\n')?;
|
||||||
for y in 0..self.width {
|
for y in 0..self.width {
|
||||||
for x in 0..self.width {
|
for x in 0..self.width {
|
||||||
f.write_char(if self[(y, x)] { '#' } else { '.' })?;
|
f.write_char(if self[(y, x)] { '#' } else { '.' })?;
|
||||||
}
|
}
|
||||||
f.write_char('\n')?;
|
f.write_char('\n')?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
struct TileDetailed {
|
struct TileDetailed {
|
||||||
tile: Tile,
|
tile: Tile,
|
||||||
edge_to_side: HashMap<Edge, Side>,
|
edge_to_side: HashMap<Edge, Side>,
|
||||||
side_to_edge: HashMap<Side, Edge>,
|
side_to_edge: HashMap<Side, Edge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TileDetailed {
|
impl TileDetailed {
|
||||||
fn edge(&self, side: Side) -> Edge {
|
fn edge(&self, side: Side) -> Edge {
|
||||||
*self.side_to_edge.get(&side).unwrap()
|
*self.side_to_edge.get(&side).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(block: &str) -> (u32, Self) {
|
fn parse(block: &str) -> (u32, Self) {
|
||||||
let (id, tile) = Tile::parse(block);
|
let (id, tile) = Tile::parse(block);
|
||||||
|
|
||||||
let edges = vec![
|
let edges = vec![
|
||||||
(Side::Top, tile.edge(Side::Top).norm_dir()),
|
(Side::Top, tile.edge(Side::Top).norm_dir()),
|
||||||
(Side::Right, tile.edge(Side::Right).norm_dir()),
|
(Side::Right, tile.edge(Side::Right).norm_dir()),
|
||||||
(Side::Bottom, tile.edge(Side::Bottom).norm_dir()),
|
(Side::Bottom, tile.edge(Side::Bottom).norm_dir()),
|
||||||
(Side::Left, tile.edge(Side::Left).norm_dir()),
|
(Side::Left, tile.edge(Side::Left).norm_dir()),
|
||||||
];
|
];
|
||||||
let edge_to_side = edges.iter().map(|&(side, edge)| (edge, side)).collect();
|
let edge_to_side = edges.iter().map(|&(side, edge)| (edge, side)).collect();
|
||||||
let side_to_edge = edges.into_iter().collect();
|
let side_to_edge = edges.into_iter().collect();
|
||||||
|
|
||||||
(id, Self { tile, edge_to_side, side_to_edge })
|
(id, Self { tile, edge_to_side, side_to_edge })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct TilePlacement {
|
struct TilePlacement {
|
||||||
tile: u32,
|
tile: u32,
|
||||||
top_side: Side,
|
top_side: Side,
|
||||||
bottom_edge: Edge,
|
bottom_edge: Edge,
|
||||||
left_side: Side,
|
left_side: Side,
|
||||||
right_edge: Edge,
|
right_edge: Edge,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let tiles: HashMap<u32, TileDetailed> = INPUT.trim().split("\n\n").map(TileDetailed::parse).collect();
|
let tiles: HashMap<u32, TileDetailed> = INPUT.trim().split("\n\n").map(TileDetailed::parse).collect();
|
||||||
let mut edges: HashMap<Edge, Vec<(u32, Side)>> = HashMap::new();
|
let mut edges: HashMap<Edge, Vec<(u32, Side)>> = HashMap::new();
|
||||||
for (&id, tile) in &tiles {
|
for (&id, tile) in &tiles {
|
||||||
for (&side, &edge) in &tile.side_to_edge {
|
for (&side, &edge) in &tile.side_to_edge {
|
||||||
edges.entry(edge).or_default().push((id, side));
|
edges.entry(edge).or_default().push((id, side));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let single_edges = edges.iter().filter_map(|(_edge, id_sides)| {
|
let single_edges = edges.iter().filter_map(|(_edge, id_sides)| {
|
||||||
if id_sides.len() == 1 {
|
if id_sides.len() == 1 {
|
||||||
Some(id_sides[0])
|
Some(id_sides[0])
|
||||||
} else {
|
} else {
|
||||||
assert!(id_sides.len() == 2);
|
assert!(id_sides.len() == 2);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
let mut single_edges_per_tile = HashMap::<u32, Vec<Side>>::new();
|
let mut single_edges_per_tile = HashMap::<u32, Vec<Side>>::new();
|
||||||
for &(tile_id, side) in &single_edges {
|
for &(tile_id, side) in &single_edges {
|
||||||
single_edges_per_tile.entry(tile_id).or_default().push(side);
|
single_edges_per_tile.entry(tile_id).or_default().push(side);
|
||||||
}
|
}
|
||||||
let corners = single_edges_per_tile.iter().filter_map(|(&tile_id, sides)| {
|
let corners = single_edges_per_tile.iter().filter_map(|(&tile_id, sides)| {
|
||||||
if sides.len() == 2 {
|
if sides.len() == 2 {
|
||||||
Some(tile_id)
|
Some(tile_id)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
let corners_prod = corners.iter().map(|&tile_id| tile_id as u64).product::<u64>();
|
let corners_prod = corners.iter().map(|&tile_id| tile_id as u64).product::<u64>();
|
||||||
println!("Product of corner tile ids: {}", corners_prod);
|
println!("Product of corner tile ids: {}", corners_prod);
|
||||||
|
|
||||||
let mut grid_tiles: [[Option<TilePlacement>; 12]; 12] = [[None; 12]; 12];
|
let mut grid_tiles: [[Option<TilePlacement>; 12]; 12] = [[None; 12]; 12];
|
||||||
for row in 0..12 {
|
for row in 0..12 {
|
||||||
if row == 0 {
|
if row == 0 {
|
||||||
// orient top left tile
|
// orient top left tile
|
||||||
let tile: u32 = corners.iter().copied().min().unwrap();
|
let tile: u32 = corners.iter().copied().min().unwrap();
|
||||||
let top_side: Side = *single_edges_per_tile.get(&tile).unwrap().iter().min().unwrap();
|
let top_side: Side = *single_edges_per_tile.get(&tile).unwrap().iter().min().unwrap();
|
||||||
let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite());
|
let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite());
|
||||||
let left_side: Side = *single_edges_per_tile.get(&tile).unwrap().iter().max().unwrap();
|
let left_side: Side = *single_edges_per_tile.get(&tile).unwrap().iter().max().unwrap();
|
||||||
let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite());
|
let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite());
|
||||||
grid_tiles[0][0] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge });
|
grid_tiles[0][0] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge });
|
||||||
} else {
|
} else {
|
||||||
// left column; look at tile above
|
// left column; look at tile above
|
||||||
let tile_top = grid_tiles[row-1][0].unwrap().tile;
|
let tile_top = grid_tiles[row-1][0].unwrap().tile;
|
||||||
let top_edge = grid_tiles[row-1][0].unwrap().bottom_edge;
|
let top_edge = grid_tiles[row-1][0].unwrap().bottom_edge;
|
||||||
let (tile, top_side) = edges.get(&top_edge).unwrap().iter().filter_map(|&(tile_id, side)| {
|
let (tile, top_side) = edges.get(&top_edge).unwrap().iter().filter_map(|&(tile_id, side)| {
|
||||||
if tile_id != tile_top {
|
if tile_id != tile_top {
|
||||||
Some((tile_id, side))
|
Some((tile_id, side))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).next().unwrap();
|
}).next().unwrap();
|
||||||
let bottom_side = top_side.opposite();
|
let bottom_side = top_side.opposite();
|
||||||
let left_side = *single_edges_per_tile.get(&tile).unwrap().iter().filter(|&&side| side != bottom_side).next().unwrap();
|
let left_side = *single_edges_per_tile.get(&tile).unwrap().iter().filter(|&&side| side != bottom_side).next().unwrap();
|
||||||
assert_ne!(top_side, left_side);
|
assert_ne!(top_side, left_side);
|
||||||
assert_ne!(top_side, left_side.opposite());
|
assert_ne!(top_side, left_side.opposite());
|
||||||
let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite());
|
let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite());
|
||||||
let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite());
|
let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite());
|
||||||
grid_tiles[row][0] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge });
|
grid_tiles[row][0] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge });
|
||||||
}
|
}
|
||||||
// complete row
|
// complete row
|
||||||
for col in 1..12 {
|
for col in 1..12 {
|
||||||
// look at tile to the left
|
// look at tile to the left
|
||||||
let tile_left = grid_tiles[row][col-1].unwrap().tile;
|
let tile_left = grid_tiles[row][col-1].unwrap().tile;
|
||||||
let left_edge = grid_tiles[row][col-1].unwrap().right_edge;
|
let left_edge = grid_tiles[row][col-1].unwrap().right_edge;
|
||||||
let (tile, left_side) = edges.get(&left_edge).unwrap().iter().filter_map(|&(tile_id, side)| {
|
let (tile, left_side) = edges.get(&left_edge).unwrap().iter().filter_map(|&(tile_id, side)| {
|
||||||
if tile_id != tile_left {
|
if tile_id != tile_left {
|
||||||
Some((tile_id, side))
|
Some((tile_id, side))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).next().unwrap();
|
}).next().unwrap();
|
||||||
let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite());
|
let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite());
|
||||||
let top_side;
|
let top_side;
|
||||||
if row == 0 {
|
if row == 0 {
|
||||||
if col == 11 {
|
if col == 11 {
|
||||||
// top right corner
|
// top right corner
|
||||||
let right_side = left_side.opposite();
|
let right_side = left_side.opposite();
|
||||||
top_side = *single_edges_per_tile.get(&tile).unwrap().iter().filter(|&&side| side != right_side).next().unwrap();
|
top_side = *single_edges_per_tile.get(&tile).unwrap().iter().filter(|&&side| side != right_side).next().unwrap();
|
||||||
} else {
|
} else {
|
||||||
top_side = *single_edges_per_tile.get(&tile).unwrap().iter().next().unwrap();
|
top_side = *single_edges_per_tile.get(&tile).unwrap().iter().next().unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let tile_top = grid_tiles[row-1][col].unwrap().tile;
|
let tile_top = grid_tiles[row-1][col].unwrap().tile;
|
||||||
let top_edge = grid_tiles[row-1][col].unwrap().bottom_edge;
|
let top_edge = grid_tiles[row-1][col].unwrap().bottom_edge;
|
||||||
top_side = edges.get(&top_edge).unwrap().iter().filter_map(|&(tile_id, side)| {
|
top_side = edges.get(&top_edge).unwrap().iter().filter_map(|&(tile_id, side)| {
|
||||||
if tile_id != tile_top {
|
if tile_id != tile_top {
|
||||||
assert_eq!(tile, tile_id);
|
assert_eq!(tile, tile_id);
|
||||||
Some(side)
|
Some(side)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).next().unwrap();
|
}).next().unwrap();
|
||||||
}
|
}
|
||||||
assert_ne!(left_side, top_side);
|
assert_ne!(left_side, top_side);
|
||||||
assert_ne!(left_side, top_side.opposite());
|
assert_ne!(left_side, top_side.opposite());
|
||||||
let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite());
|
let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite());
|
||||||
grid_tiles[row][col] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge });
|
grid_tiles[row][col] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rotated_tiles = Vec::new();
|
let mut rotated_tiles = Vec::new();
|
||||||
let mut picture = Tile { width: 12 * 8, data: Vec::new() };
|
let mut picture = Tile { width: 12 * 8, data: Vec::new() };
|
||||||
picture.data.resize(picture.width * picture.width, false);
|
picture.data.resize(picture.width * picture.width, false);
|
||||||
for row in 0..12 {
|
for row in 0..12 {
|
||||||
rotated_tiles.push(Vec::new());
|
rotated_tiles.push(Vec::new());
|
||||||
for col in 0..12 {
|
for col in 0..12 {
|
||||||
let tp = grid_tiles[row][col].unwrap();
|
let tp = grid_tiles[row][col].unwrap();
|
||||||
let tile = tiles.get(&tp.tile).unwrap().tile.rotate(tp.top_side, tp.left_side);
|
let tile = tiles.get(&tp.tile).unwrap().tile.rotate(tp.top_side, tp.left_side);
|
||||||
rotated_tiles[row].push(tile.clone());
|
rotated_tiles[row].push(tile.clone());
|
||||||
assert_eq!(rotated_tiles[row][col], tile);
|
assert_eq!(rotated_tiles[row][col], tile);
|
||||||
|
|
||||||
// check edges
|
// check edges
|
||||||
if row > 0 {
|
if row > 0 {
|
||||||
assert_eq!(rotated_tiles[row-1][col].edge(Side::Bottom), tile.edge(Side::Top));
|
assert_eq!(rotated_tiles[row-1][col].edge(Side::Bottom), tile.edge(Side::Top));
|
||||||
}
|
}
|
||||||
if col > 0 {
|
if col > 0 {
|
||||||
assert_eq!(rotated_tiles[row][col-1].edge(Side::Right), tile.edge(Side::Left));
|
assert_eq!(rotated_tiles[row][col-1].edge(Side::Right), tile.edge(Side::Left));
|
||||||
}
|
}
|
||||||
|
|
||||||
// assemble full picture
|
// assemble full picture
|
||||||
for y in 0..tile.width-2 {
|
for y in 0..tile.width-2 {
|
||||||
for x in 0..tile.width-2 {
|
for x in 0..tile.width-2 {
|
||||||
picture[(row*8+y, col*8+x)] = tile[(y+1, x+1)];
|
picture[(row*8+y, col*8+x)] = tile[(y+1, x+1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("{:?}", picture);
|
println!("{:?}", picture);
|
||||||
let monsters = picture.find_monster();
|
let monsters = picture.find_monster();
|
||||||
println!("{:?}", monsters);
|
println!("{:?}", monsters);
|
||||||
let sea_count = picture.data.iter().filter(|&&v| v).count();
|
let sea_count = picture.data.iter().filter(|&&v| v).count();
|
||||||
let monster_count = monsters.data.iter().filter(|&&v| v).count();
|
let monster_count = monsters.data.iter().filter(|&&v| v).count();
|
||||||
println!("Roughness: {}", sea_count - monster_count);
|
println!("Roughness: {}", sea_count - monster_count);
|
||||||
}
|
}
|
||||||
|
114
src/bin/day21.rs
114
src/bin/day21.rs
@ -1,57 +1,57 @@
|
|||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../../data/day21");
|
const INPUT: &str = include_str!("../../data/day21");
|
||||||
|
|
||||||
struct FoodDescription<'a> {
|
struct FoodDescription<'a> {
|
||||||
ingredients: HashSet<&'a str>,
|
ingredients: HashSet<&'a str>,
|
||||||
allergens: HashSet<&'a str>,
|
allergens: HashSet<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FoodDescription<'a> {
|
impl<'a> FoodDescription<'a> {
|
||||||
fn parse(line: &'a str) -> Self {
|
fn parse(line: &'a str) -> Self {
|
||||||
let pos = line.find(" (contains ").unwrap();
|
let pos = line.find(" (contains ").unwrap();
|
||||||
let ingredients = &line[..pos];
|
let ingredients = &line[..pos];
|
||||||
let allergens = line[pos..].strip_prefix(" (contains ").unwrap().strip_suffix(")").unwrap();
|
let allergens = line[pos..].strip_prefix(" (contains ").unwrap().strip_suffix(")").unwrap();
|
||||||
FoodDescription {
|
FoodDescription {
|
||||||
ingredients: ingredients.split_whitespace().collect(),
|
ingredients: ingredients.split_whitespace().collect(),
|
||||||
allergens: allergens.split(", ").collect(),
|
allergens: allergens.split(", ").collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let fds = INPUT.lines().map(FoodDescription::parse).collect::<Vec<_>>();
|
let fds = INPUT.lines().map(FoodDescription::parse).collect::<Vec<_>>();
|
||||||
let allergens: HashSet<&str> = fds.iter().map(|fd| fd.allergens.iter().cloned()).flatten().collect();
|
let allergens: HashSet<&str> = fds.iter().map(|fd| fd.allergens.iter().cloned()).flatten().collect();
|
||||||
let mut allergen_map_poss: HashMap<&str, HashSet<&str>> = allergens.iter().map(|&a| {
|
let mut allergen_map_poss: HashMap<&str, HashSet<&str>> = allergens.iter().map(|&a| {
|
||||||
let mut is = fds.iter()
|
let mut is = fds.iter()
|
||||||
.filter(|fd| fd.allergens.contains(a))
|
.filter(|fd| fd.allergens.contains(a))
|
||||||
.map(|fd| &fd.ingredients);
|
.map(|fd| &fd.ingredients);
|
||||||
let first_ingr: HashSet<&str> = is.next().unwrap().clone();
|
let first_ingr: HashSet<&str> = is.next().unwrap().clone();
|
||||||
let poss = is.fold(first_ingr, |memo, ingr| {
|
let poss = is.fold(first_ingr, |memo, ingr| {
|
||||||
memo.intersection(ingr).cloned().collect::<HashSet<_>>()
|
memo.intersection(ingr).cloned().collect::<HashSet<_>>()
|
||||||
});
|
});
|
||||||
(a, poss)
|
(a, poss)
|
||||||
}).collect();
|
}).collect();
|
||||||
let mut allergen_map: BTreeMap<&str, &str> = BTreeMap::new();
|
let mut allergen_map: BTreeMap<&str, &str> = BTreeMap::new();
|
||||||
while !allergen_map_poss.is_empty() {
|
while !allergen_map_poss.is_empty() {
|
||||||
let (next_allg, next_ingr) = allergen_map_poss.iter().filter_map(|(&a, is)| {
|
let (next_allg, next_ingr) = allergen_map_poss.iter().filter_map(|(&a, is)| {
|
||||||
if is.len() == 1 {
|
if is.len() == 1 {
|
||||||
Some((a, *is.iter().next().unwrap()))
|
Some((a, *is.iter().next().unwrap()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).next().unwrap();
|
}).next().unwrap();
|
||||||
allergen_map.insert(next_allg, next_ingr);
|
allergen_map.insert(next_allg, next_ingr);
|
||||||
allergen_map_poss.remove(next_allg);
|
allergen_map_poss.remove(next_allg);
|
||||||
for ingr in allergen_map_poss.values_mut() {
|
for ingr in allergen_map_poss.values_mut() {
|
||||||
ingr.remove(next_ingr);
|
ingr.remove(next_ingr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("Allergens contained by ingredients: {:?}", allergen_map);
|
println!("Allergens contained by ingredients: {:?}", allergen_map);
|
||||||
let all_ingredients: HashSet<&str> = allergen_map.values().cloned().collect();
|
let all_ingredients: HashSet<&str> = allergen_map.values().cloned().collect();
|
||||||
println!(
|
println!(
|
||||||
"Ingredients not allergens used {} times",
|
"Ingredients not allergens used {} times",
|
||||||
fds.iter().map(|fd| fd.ingredients.difference(&all_ingredients).count()).sum::<usize>(),
|
fds.iter().map(|fd| fd.ingredients.difference(&all_ingredients).count()).sum::<usize>(),
|
||||||
);
|
);
|
||||||
println!("Dangerous ingredients: {}", allergen_map.values().cloned().collect::<Vec<_>>().join(","));
|
println!("Dangerous ingredients: {}", allergen_map.values().cloned().collect::<Vec<_>>().join(","));
|
||||||
}
|
}
|
||||||
|
196
src/bin/day22.rs
196
src/bin/day22.rs
@ -1,98 +1,98 @@
|
|||||||
use std::collections::{VecDeque, HashSet};
|
use std::collections::{VecDeque, HashSet};
|
||||||
|
|
||||||
type Deck = VecDeque<u32>;
|
type Deck = VecDeque<u32>;
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../../data/day22");
|
const INPUT: &str = include_str!("../../data/day22");
|
||||||
|
|
||||||
fn parse_player(block: &str) -> (u32, Deck) {
|
fn parse_player(block: &str) -> (u32, Deck) {
|
||||||
let block = block.strip_prefix("Player ").unwrap();
|
let block = block.strip_prefix("Player ").unwrap();
|
||||||
let player = block[..1].parse::<u32>().unwrap();
|
let player = block[..1].parse::<u32>().unwrap();
|
||||||
let block = block[1..].strip_prefix(":").unwrap().trim();
|
let block = block[1..].strip_prefix(":").unwrap().trim();
|
||||||
let deck = block.lines().map(|s| s.parse::<u32>().unwrap()).collect();
|
let deck = block.lines().map(|s| s.parse::<u32>().unwrap()).collect();
|
||||||
(player, deck)
|
(player, deck)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn score(deck: &Deck) -> u64 {
|
fn score(deck: &Deck) -> u64 {
|
||||||
deck.iter().rev().enumerate().map(|(pos, &value)| {
|
deck.iter().rev().enumerate().map(|(pos, &value)| {
|
||||||
((pos + 1) as u64) * (value as u64)
|
((pos + 1) as u64) * (value as u64)
|
||||||
}).sum::<u64>()
|
}).sum::<u64>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn crab(mut deck1: Deck, mut deck2: Deck) {
|
fn crab(mut deck1: Deck, mut deck2: Deck) {
|
||||||
while !deck1.is_empty() && !deck2.is_empty() {
|
while !deck1.is_empty() && !deck2.is_empty() {
|
||||||
let c1 = deck1.pop_front().unwrap();
|
let c1 = deck1.pop_front().unwrap();
|
||||||
let c2 = deck2.pop_front().unwrap();
|
let c2 = deck2.pop_front().unwrap();
|
||||||
if c1 > c2 {
|
if c1 > c2 {
|
||||||
deck1.push_back(c1);
|
deck1.push_back(c1);
|
||||||
deck1.push_back(c2);
|
deck1.push_back(c2);
|
||||||
} else {
|
} else {
|
||||||
assert!(c2 > c1);
|
assert!(c2 > c1);
|
||||||
deck2.push_back(c2);
|
deck2.push_back(c2);
|
||||||
deck2.push_back(c1);
|
deck2.push_back(c1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if deck2.is_empty() {
|
if deck2.is_empty() {
|
||||||
println!("Player 1 won: {:?}", deck1);
|
println!("Player 1 won: {:?}", deck1);
|
||||||
println!("Score: {}", score(&deck1));
|
println!("Score: {}", score(&deck1));
|
||||||
} else {
|
} else {
|
||||||
println!("Player 2 won: {:?}", deck2);
|
println!("Player 2 won: {:?}", deck2);
|
||||||
println!("Score: {}", score(&deck2));
|
println!("Score: {}", score(&deck2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _crab_rec(deck1: &mut Deck, deck2: &mut Deck) -> bool {
|
fn _crab_rec(deck1: &mut Deck, deck2: &mut Deck) -> bool {
|
||||||
// println!("Rec Combat: {:?}, {:?}", deck1, deck2);
|
// println!("Rec Combat: {:?}, {:?}", deck1, deck2);
|
||||||
let mut previous_rounds = HashSet::new();
|
let mut previous_rounds = HashSet::new();
|
||||||
while !deck1.is_empty() && !deck2.is_empty() {
|
while !deck1.is_empty() && !deck2.is_empty() {
|
||||||
if !previous_rounds.insert((deck1.clone(), deck2.clone())) {
|
if !previous_rounds.insert((deck1.clone(), deck2.clone())) {
|
||||||
// prevent infinite loop; player 1 wins
|
// prevent infinite loop; player 1 wins
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let c1 = deck1.pop_front().unwrap();
|
let c1 = deck1.pop_front().unwrap();
|
||||||
let c2 = deck2.pop_front().unwrap();
|
let c2 = deck2.pop_front().unwrap();
|
||||||
let p1_won = if deck1.len() >= c1 as usize && deck2.len() >= c2 as usize {
|
let p1_won = if deck1.len() >= c1 as usize && deck2.len() >= c2 as usize {
|
||||||
let mut deck1: Deck = deck1.iter().copied().take(c1 as usize).collect();
|
let mut deck1: Deck = deck1.iter().copied().take(c1 as usize).collect();
|
||||||
let mut deck2: Deck = deck2.iter().copied().take(c2 as usize).collect();
|
let mut deck2: Deck = deck2.iter().copied().take(c2 as usize).collect();
|
||||||
assert_eq!(deck1.len(), c1 as usize);
|
assert_eq!(deck1.len(), c1 as usize);
|
||||||
assert_eq!(deck2.len(), c2 as usize);
|
assert_eq!(deck2.len(), c2 as usize);
|
||||||
// recurse
|
// recurse
|
||||||
_crab_rec(&mut deck1, &mut deck2)
|
_crab_rec(&mut deck1, &mut deck2)
|
||||||
} else {
|
} else {
|
||||||
assert_ne!(c1, c2);
|
assert_ne!(c1, c2);
|
||||||
c1 > c2
|
c1 > c2
|
||||||
};
|
};
|
||||||
if p1_won {
|
if p1_won {
|
||||||
deck1.push_back(c1);
|
deck1.push_back(c1);
|
||||||
deck1.push_back(c2);
|
deck1.push_back(c2);
|
||||||
} else {
|
} else {
|
||||||
deck2.push_back(c2);
|
deck2.push_back(c2);
|
||||||
deck2.push_back(c1);
|
deck2.push_back(c1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deck2.is_empty()
|
deck2.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn crab_rec(mut deck1: Deck, mut deck2: Deck) {
|
fn crab_rec(mut deck1: Deck, mut deck2: Deck) {
|
||||||
let p1_won = _crab_rec(&mut deck1, &mut deck2);
|
let p1_won = _crab_rec(&mut deck1, &mut deck2);
|
||||||
if p1_won {
|
if p1_won {
|
||||||
println!("Player 1 won recursive combat: {:?}", deck1);
|
println!("Player 1 won recursive combat: {:?}", deck1);
|
||||||
println!("Score: {}", score(&deck1));
|
println!("Score: {}", score(&deck1));
|
||||||
} else {
|
} else {
|
||||||
println!("Player 2 won recursive combat: {:?}", deck2);
|
println!("Player 2 won recursive combat: {:?}", deck2);
|
||||||
println!("Score: {}", score(&deck2));
|
println!("Score: {}", score(&deck2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (deck1, deck2) = {
|
let (deck1, deck2) = {
|
||||||
let pos = INPUT.find("\n\n").unwrap();
|
let pos = INPUT.find("\n\n").unwrap();
|
||||||
let (id1, deck1) = parse_player(&INPUT[..pos]);
|
let (id1, deck1) = parse_player(&INPUT[..pos]);
|
||||||
let (id2, deck2) = parse_player(&INPUT[pos+2..]);
|
let (id2, deck2) = parse_player(&INPUT[pos+2..]);
|
||||||
assert_eq!(id1, 1);
|
assert_eq!(id1, 1);
|
||||||
assert_eq!(id2, 2);
|
assert_eq!(id2, 2);
|
||||||
(deck1, deck2)
|
(deck1, deck2)
|
||||||
};
|
};
|
||||||
|
|
||||||
crab(deck1.clone(), deck2.clone());
|
crab(deck1.clone(), deck2.clone());
|
||||||
crab_rec(deck1, deck2);
|
crab_rec(deck1, deck2);
|
||||||
}
|
}
|
||||||
|
434
src/bin/day23.rs
434
src/bin/day23.rs
@ -1,217 +1,217 @@
|
|||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../../data/day23");
|
const INPUT: &str = include_str!("../../data/day23");
|
||||||
|
|
||||||
type Item = usize;
|
type Item = usize;
|
||||||
|
|
||||||
const MARK_MISSING: usize = !0;
|
const MARK_MISSING: usize = !0;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
struct OptionItem(Item);
|
struct OptionItem(Item);
|
||||||
|
|
||||||
impl OptionItem {
|
impl OptionItem {
|
||||||
pub const NONE: Self = Self(MARK_MISSING);
|
pub const NONE: Self = Self(MARK_MISSING);
|
||||||
|
|
||||||
fn item(self) -> Option<Item> {
|
fn item(self) -> Option<Item> {
|
||||||
if self.0 == MARK_MISSING {
|
if self.0 == MARK_MISSING {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(self.0)
|
Some(self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Item> for OptionItem {
|
impl From<Item> for OptionItem {
|
||||||
fn from(i: Item) -> Self {
|
fn from(i: Item) -> Self {
|
||||||
assert_ne!(i, MARK_MISSING);
|
assert_ne!(i, MARK_MISSING);
|
||||||
Self(i)
|
Self(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Option<Item>> for OptionItem {
|
impl From<Option<Item>> for OptionItem {
|
||||||
fn from(i: Option<Item>) -> Self {
|
fn from(i: Option<Item>) -> Self {
|
||||||
match i {
|
match i {
|
||||||
None => Self(MARK_MISSING),
|
None => Self(MARK_MISSING),
|
||||||
Some(i) => Self::from(i),
|
Some(i) => Self::from(i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
struct Cups {
|
struct Cups {
|
||||||
current: OptionItem,
|
current: OptionItem,
|
||||||
last: OptionItem,
|
last: OptionItem,
|
||||||
next_cup: Vec<OptionItem>,
|
next_cup: Vec<OptionItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Cups {
|
impl Default for Cups {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
current: OptionItem::NONE,
|
current: OptionItem::NONE,
|
||||||
last: OptionItem::NONE,
|
last: OptionItem::NONE,
|
||||||
next_cup: Vec::new(),
|
next_cup: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cups {
|
impl Cups {
|
||||||
fn last(&self) -> Option<Item> {
|
fn last(&self) -> Option<Item> {
|
||||||
self.last.item()
|
self.last.item()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_after(&mut self, prev: Item, value: Item) {
|
fn insert_after(&mut self, prev: Item, value: Item) {
|
||||||
let next = replace(&mut self.next_cup[prev], value.into()).item().unwrap();
|
let next = replace(&mut self.next_cup[prev], value.into()).item().unwrap();
|
||||||
if value >= self.next_cup.len() {
|
if value >= self.next_cup.len() {
|
||||||
self.next_cup.resize(value, OptionItem::NONE);
|
self.next_cup.resize(value, OptionItem::NONE);
|
||||||
self.next_cup.push(next.into());
|
self.next_cup.push(next.into());
|
||||||
assert_eq!(self.next_cup[value], next.into());
|
assert_eq!(self.next_cup[value], next.into());
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(self.next_cup[value], OptionItem::NONE);
|
assert_eq!(self.next_cup[value], OptionItem::NONE);
|
||||||
self.next_cup[value] = next.into();
|
self.next_cup[value] = next.into();
|
||||||
}
|
}
|
||||||
if self.last.item().unwrap() == prev {
|
if self.last.item().unwrap() == prev {
|
||||||
self.last = value.into();
|
self.last = value.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_after<I>(&mut self, mut after: Item, iter: I)
|
fn extend_after<I>(&mut self, mut after: Item, iter: I)
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item=Item>,
|
I: IntoIterator<Item=Item>,
|
||||||
{
|
{
|
||||||
for item in iter {
|
for item in iter {
|
||||||
self.insert_after(after, item);
|
self.insert_after(after, item);
|
||||||
after = item;
|
after = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend<I>(&mut self, iter: I)
|
fn extend<I>(&mut self, iter: I)
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item=Item>,
|
I: IntoIterator<Item=Item>,
|
||||||
{
|
{
|
||||||
let mut iter = iter.into_iter();
|
let mut iter = iter.into_iter();
|
||||||
if let Some(first) = iter.next() {
|
if let Some(first) = iter.next() {
|
||||||
if let Some(prev) = self.last() {
|
if let Some(prev) = self.last() {
|
||||||
self.insert_after(prev, first);
|
self.insert_after(prev, first);
|
||||||
} else {
|
} else {
|
||||||
assert!(self.next_cup.is_empty());
|
assert!(self.next_cup.is_empty());
|
||||||
self.next_cup.resize(first, OptionItem::NONE);
|
self.next_cup.resize(first, OptionItem::NONE);
|
||||||
self.next_cup.push(first.into());
|
self.next_cup.push(first.into());
|
||||||
self.current = first.into();
|
self.current = first.into();
|
||||||
self.last = first.into();
|
self.last = first.into();
|
||||||
}
|
}
|
||||||
self.extend_after(first, iter);
|
self.extend_after(first, iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_from(&self, start: Item) -> impl Iterator<Item=Item> + '_ {
|
fn iter_from(&self, start: Item) -> impl Iterator<Item=Item> + '_ {
|
||||||
let mut current = start;
|
let mut current = start;
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
if !first && current == start {
|
if !first && current == start {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
let item = current;
|
let item = current;
|
||||||
current = self.next_cup[item].item().unwrap();
|
current = self.next_cup[item].item().unwrap();
|
||||||
Some(item)
|
Some(item)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter(&self) -> impl Iterator<Item=Item> + '_ {
|
fn iter(&self) -> impl Iterator<Item=Item> + '_ {
|
||||||
self.iter_from(self.current.item().unwrap())
|
self.iter_from(self.current.item().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_after(&mut self, previous: Item) -> Item {
|
fn remove_after(&mut self, previous: Item) -> Item {
|
||||||
let item = self.next_cup[previous].item().unwrap();
|
let item = self.next_cup[previous].item().unwrap();
|
||||||
let follow = self.next_cup[item].item().unwrap();
|
let follow = self.next_cup[item].item().unwrap();
|
||||||
self.next_cup[item] = OptionItem::NONE;
|
self.next_cup[item] = OptionItem::NONE;
|
||||||
self.next_cup[previous] = follow.into();
|
self.next_cup[previous] = follow.into();
|
||||||
if item == self.current.item().unwrap() {
|
if item == self.current.item().unwrap() {
|
||||||
if item == previous {
|
if item == previous {
|
||||||
// last item
|
// last item
|
||||||
self.current = OptionItem::NONE;
|
self.current = OptionItem::NONE;
|
||||||
self.last = OptionItem::NONE;
|
self.last = OptionItem::NONE;
|
||||||
self.next_cup.clear();
|
self.next_cup.clear();
|
||||||
} else {
|
} else {
|
||||||
// if we removed the "current" item: move current pointer forward
|
// if we removed the "current" item: move current pointer forward
|
||||||
self.current = follow.into();
|
self.current = follow.into();
|
||||||
}
|
}
|
||||||
} else if item == self.last.item().unwrap() {
|
} else if item == self.last.item().unwrap() {
|
||||||
// just removed the last item
|
// just removed the last item
|
||||||
self.last = previous.into();
|
self.last = previous.into();
|
||||||
}
|
}
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drain_after(&mut self, previous: Item, mut len: usize) -> impl Iterator<Item=Item> + '_ {
|
fn drain_after(&mut self, previous: Item, mut len: usize) -> impl Iterator<Item=Item> + '_ {
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
len -= 1;
|
len -= 1;
|
||||||
Some(self.remove_after(previous))
|
Some(self.remove_after(previous))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drain(&mut self, range: std::ops::Range<usize>) -> impl Iterator<Item=Item> + '_ {
|
fn drain(&mut self, range: std::ops::Range<usize>) -> impl Iterator<Item=Item> + '_ {
|
||||||
let len = range.end - range.start;
|
let len = range.end - range.start;
|
||||||
let after = if len == 0 {
|
let after = if len == 0 {
|
||||||
0 // don't care
|
0 // don't care
|
||||||
} else if range.start > 0 {
|
} else if range.start > 0 {
|
||||||
self.iter().skip(range.start - 1).next().unwrap()
|
self.iter().skip(range.start - 1).next().unwrap()
|
||||||
} else {
|
} else {
|
||||||
self.last().unwrap()
|
self.last().unwrap()
|
||||||
};
|
};
|
||||||
self.drain_after(after, len)
|
self.drain_after(after, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(cups: &str) -> Self {
|
fn parse(cups: &str) -> Self {
|
||||||
let mut result = Cups::default();
|
let mut result = Cups::default();
|
||||||
result.extend(cups.bytes().map(|c| (c - b'0') as Item));
|
result.extend(cups.bytes().map(|c| (c - b'0') as Item));
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_left(&mut self, items: usize) {
|
fn rotate_left(&mut self, items: usize) {
|
||||||
let next = self.iter().skip(items).next().unwrap();
|
let next = self.iter().skip(items).next().unwrap();
|
||||||
self.current = next.into();
|
self.current = next.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix(&mut self) {
|
fn mix(&mut self) {
|
||||||
let max_entry = self.next_cup.len() - 1;
|
let max_entry = self.next_cup.len() - 1;
|
||||||
let pickup = self.drain(1..4).collect::<Vec<_>>();
|
let pickup = self.drain(1..4).collect::<Vec<_>>();
|
||||||
let mut insert_after = self.current.item().unwrap();
|
let mut insert_after = self.current.item().unwrap();
|
||||||
loop {
|
loop {
|
||||||
insert_after = match insert_after.checked_sub(1) {
|
insert_after = match insert_after.checked_sub(1) {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
None => max_entry,
|
None => max_entry,
|
||||||
};
|
};
|
||||||
if self.next_cup[insert_after] != OptionItem::NONE {
|
if self.next_cup[insert_after] != OptionItem::NONE {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.extend_after(insert_after, pickup);
|
self.extend_after(insert_after, pickup);
|
||||||
self.rotate_left(1);
|
self.rotate_left(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_to_one(&mut self) {
|
fn rotate_to_one(&mut self) {
|
||||||
assert_ne!(self.next_cup[1], OptionItem::NONE);
|
assert_ne!(self.next_cup[1], OptionItem::NONE);
|
||||||
self.current = 1.into();
|
self.current = 1.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut cups = Cups::parse(INPUT.trim());
|
let mut cups = Cups::parse(INPUT.trim());
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
cups.mix();
|
cups.mix();
|
||||||
}
|
}
|
||||||
cups.rotate_to_one();
|
cups.rotate_to_one();
|
||||||
println!("Cups: {}", cups.iter().skip(1).map(|c| format!("{}", c)).collect::<Vec<_>>().join(""));
|
println!("Cups: {}", cups.iter().skip(1).map(|c| format!("{}", c)).collect::<Vec<_>>().join(""));
|
||||||
|
|
||||||
let mut cups = Cups::parse(INPUT.trim());
|
let mut cups = Cups::parse(INPUT.trim());
|
||||||
cups.extend(10..=1000000);
|
cups.extend(10..=1000000);
|
||||||
for _ in 0..10000000 {
|
for _ in 0..10000000 {
|
||||||
cups.mix()
|
cups.mix()
|
||||||
}
|
}
|
||||||
cups.rotate_to_one();
|
cups.rotate_to_one();
|
||||||
println!("Cup product: {}", cups.drain(1..3).map(|c| c as u64).product::<u64>());
|
println!("Cup product: {}", cups.drain(1..3).map(|c| c as u64).product::<u64>());
|
||||||
}
|
}
|
||||||
|
280
src/bin/day24.rs
280
src/bin/day24.rs
@ -1,140 +1,140 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
const INPUT: &str = include_str!("../../data/day24");
|
const INPUT: &str = include_str!("../../data/day24");
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
enum Direction {
|
enum Direction {
|
||||||
East,
|
East,
|
||||||
SouthEast,
|
SouthEast,
|
||||||
SouthWest,
|
SouthWest,
|
||||||
West,
|
West,
|
||||||
NorthWest,
|
NorthWest,
|
||||||
NorthEast,
|
NorthEast,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Direction {
|
impl Direction {
|
||||||
const ALL: [Direction; 6] = [
|
const ALL: [Direction; 6] = [
|
||||||
Self::East,
|
Self::East,
|
||||||
Self::SouthEast,
|
Self::SouthEast,
|
||||||
Self::SouthWest,
|
Self::SouthWest,
|
||||||
Self::West,
|
Self::West,
|
||||||
Self::NorthWest,
|
Self::NorthWest,
|
||||||
Self::NorthEast,
|
Self::NorthEast,
|
||||||
];
|
];
|
||||||
|
|
||||||
fn symbol(self) -> &'static str {
|
fn symbol(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::East => "e",
|
Self::East => "e",
|
||||||
Self::SouthEast => "se",
|
Self::SouthEast => "se",
|
||||||
Self::SouthWest => "sw",
|
Self::SouthWest => "sw",
|
||||||
Self::West => "w",
|
Self::West => "w",
|
||||||
Self::NorthWest => "nw",
|
Self::NorthWest => "nw",
|
||||||
Self::NorthEast => "ne",
|
Self::NorthEast => "ne",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_list(line: &str) -> Vec<Self> {
|
fn parse_list(line: &str) -> Vec<Self> {
|
||||||
let mut line = line.trim();
|
let mut line = line.trim();
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
'outer: while !line.is_empty() {
|
'outer: while !line.is_empty() {
|
||||||
for &d in Self::ALL.iter() {
|
for &d in Self::ALL.iter() {
|
||||||
if let Some(rem) = line.strip_prefix(d.symbol()) {
|
if let Some(rem) = line.strip_prefix(d.symbol()) {
|
||||||
result.push(d);
|
result.push(d);
|
||||||
line = rem;
|
line = rem;
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic!("Invalid directions: {:?}", line);
|
panic!("Invalid directions: {:?}", line);
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// -> (row, col)
|
// -> (row, col)
|
||||||
fn as_offset(self) -> (i32, i32) {
|
fn as_offset(self) -> (i32, i32) {
|
||||||
match self {
|
match self {
|
||||||
Self::East => (0, 1),
|
Self::East => (0, 1),
|
||||||
Self::SouthEast => (1, 1),
|
Self::SouthEast => (1, 1),
|
||||||
Self::SouthWest => (1, 0),
|
Self::SouthWest => (1, 0),
|
||||||
Self::West => (0, -1),
|
Self::West => (0, -1),
|
||||||
Self::NorthWest => (-1, -1),
|
Self::NorthWest => (-1, -1),
|
||||||
Self::NorthEast => (-1, 0),
|
Self::NorthEast => (-1, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Debug)]
|
||||||
struct Position {
|
struct Position {
|
||||||
row: i32,
|
row: i32,
|
||||||
// the "0"-column goes from south-west to north-east through (0/0)
|
// the "0"-column goes from south-west to north-east through (0/0)
|
||||||
col: i32,
|
col: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
fn adjacent(self) -> impl Iterator<Item=Self> {
|
fn adjacent(self) -> impl Iterator<Item=Self> {
|
||||||
Direction::ALL.iter().map(move |&d| self + d)
|
Direction::ALL.iter().map(move |&d| self + d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Add<Direction> for Position {
|
impl std::ops::Add<Direction> for Position {
|
||||||
type Output = Position;
|
type Output = Position;
|
||||||
|
|
||||||
fn add(self, rhs: Direction) -> Self::Output {
|
fn add(self, rhs: Direction) -> Self::Output {
|
||||||
let (row, col) = rhs.as_offset();
|
let (row, col) = rhs.as_offset();
|
||||||
Self {
|
Self {
|
||||||
row: self.row + row,
|
row: self.row + row,
|
||||||
col: self.col + col,
|
col: self.col + col,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn daily_flip(black_tiles: HashSet<Position>) -> HashSet<Position> {
|
fn daily_flip(black_tiles: HashSet<Position>) -> HashSet<Position> {
|
||||||
let mut new_black_tiles: HashSet<Position> = HashSet::new();
|
let mut new_black_tiles: HashSet<Position> = HashSet::new();
|
||||||
let mut dont_flip_to_black: HashSet<Position> = HashSet::new();
|
let mut dont_flip_to_black: HashSet<Position> = HashSet::new();
|
||||||
for &tile in &black_tiles {
|
for &tile in &black_tiles {
|
||||||
let mut count_adj_black = 0;
|
let mut count_adj_black = 0;
|
||||||
for adj_tile in tile.adjacent() {
|
for adj_tile in tile.adjacent() {
|
||||||
if black_tiles.contains(&adj_tile) {
|
if black_tiles.contains(&adj_tile) {
|
||||||
count_adj_black += 1;
|
count_adj_black += 1;
|
||||||
} else if !new_black_tiles.contains(&adj_tile) && !dont_flip_to_black.contains(&adj_tile) {
|
} else if !new_black_tiles.contains(&adj_tile) && !dont_flip_to_black.contains(&adj_tile) {
|
||||||
// white adjacent: maybe needs to be flipped to black
|
// white adjacent: maybe needs to be flipped to black
|
||||||
let mut white_count_adj_black = 0;
|
let mut white_count_adj_black = 0;
|
||||||
for adj_tile_2 in adj_tile.adjacent() {
|
for adj_tile_2 in adj_tile.adjacent() {
|
||||||
if black_tiles.contains(&adj_tile_2) {
|
if black_tiles.contains(&adj_tile_2) {
|
||||||
white_count_adj_black += 1;
|
white_count_adj_black += 1;
|
||||||
if white_count_adj_black > 2 { break; }
|
if white_count_adj_black > 2 { break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if white_count_adj_black == 2 {
|
if white_count_adj_black == 2 {
|
||||||
new_black_tiles.insert(adj_tile);
|
new_black_tiles.insert(adj_tile);
|
||||||
} else {
|
} else {
|
||||||
// don't check again
|
// don't check again
|
||||||
dont_flip_to_black.insert(adj_tile);
|
dont_flip_to_black.insert(adj_tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if count_adj_black == 1 || count_adj_black == 2 {
|
if count_adj_black == 1 || count_adj_black == 2 {
|
||||||
new_black_tiles.insert(tile);
|
new_black_tiles.insert(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_black_tiles
|
new_black_tiles
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let directions = INPUT.lines().map(Direction::parse_list).collect::<Vec<_>>();
|
let directions = INPUT.lines().map(Direction::parse_list).collect::<Vec<_>>();
|
||||||
let flip_tiles = directions.iter().map(|tile| {
|
let flip_tiles = directions.iter().map(|tile| {
|
||||||
tile.iter().fold(Position::default(), |tile, &dir| tile + dir)
|
tile.iter().fold(Position::default(), |tile, &dir| tile + dir)
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
let mut black_tiles = HashSet::new();
|
let mut black_tiles = HashSet::new();
|
||||||
for &tile in &flip_tiles {
|
for &tile in &flip_tiles {
|
||||||
if black_tiles.contains(&tile) {
|
if black_tiles.contains(&tile) {
|
||||||
black_tiles.remove(&tile);
|
black_tiles.remove(&tile);
|
||||||
} else {
|
} else {
|
||||||
black_tiles.insert(tile);
|
black_tiles.insert(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("Black tiles: {}", black_tiles.len());
|
println!("Black tiles: {}", black_tiles.len());
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
black_tiles = daily_flip(black_tiles);
|
black_tiles = daily_flip(black_tiles);
|
||||||
}
|
}
|
||||||
println!("Black tiles: {}", black_tiles.len());
|
println!("Black tiles: {}", black_tiles.len());
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,44 @@
|
|||||||
const INPUT: &str = include_str!("../../data/day25");
|
const INPUT: &str = include_str!("../../data/day25");
|
||||||
|
|
||||||
const MOD: u32 = 20201227;
|
const MOD: u32 = 20201227;
|
||||||
|
|
||||||
// transform(subject, key) = (subject ** key) mod 20201227
|
// transform(subject, key) = (subject ** key) mod 20201227
|
||||||
//
|
//
|
||||||
// public_key := transform(7, private_key)
|
// public_key := transform(7, private_key)
|
||||||
// shared_secret := transform(peer_public_key, local_private_key)
|
// shared_secret := transform(peer_public_key, local_private_key)
|
||||||
|
|
||||||
fn transform(subject: u32, key: u32) -> u32 {
|
fn transform(subject: u32, key: u32) -> u32 {
|
||||||
let mut value = 1u64;
|
let mut value = 1u64;
|
||||||
let subject = subject as u64;
|
let subject = subject as u64;
|
||||||
for _ in 0..key {
|
for _ in 0..key {
|
||||||
value = (value * subject) % (MOD as u64);
|
value = (value * subject) % (MOD as u64);
|
||||||
}
|
}
|
||||||
value as u32
|
value as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_key(subject: u32, transformed: u32) -> u32 {
|
fn find_key(subject: u32, transformed: u32) -> u32 {
|
||||||
let mut value = 1u64;
|
let mut value = 1u64;
|
||||||
let mut key = 0;
|
let mut key = 0;
|
||||||
let subject = subject as u64;
|
let subject = subject as u64;
|
||||||
let transformed = transformed as u64;
|
let transformed = transformed as u64;
|
||||||
while value != transformed {
|
while value != transformed {
|
||||||
value = (value * subject) % (MOD as u64);
|
value = (value * subject) % (MOD as u64);
|
||||||
key += 1;
|
key += 1;
|
||||||
}
|
}
|
||||||
key
|
key
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let card_pub_key;
|
let card_pub_key;
|
||||||
let door_pub_key;
|
let door_pub_key;
|
||||||
{
|
{
|
||||||
let mut lines = INPUT.lines();
|
let mut lines = INPUT.lines();
|
||||||
card_pub_key = lines.next().unwrap().parse::<u32>().unwrap();
|
card_pub_key = lines.next().unwrap().parse::<u32>().unwrap();
|
||||||
door_pub_key = lines.next().unwrap().parse::<u32>().unwrap();
|
door_pub_key = lines.next().unwrap().parse::<u32>().unwrap();
|
||||||
assert!(lines.next().is_none());
|
assert!(lines.next().is_none());
|
||||||
}
|
}
|
||||||
let card_priv_key = find_key(7, card_pub_key);
|
let card_priv_key = find_key(7, card_pub_key);
|
||||||
println!("Card private key: {}", card_priv_key);
|
println!("Card private key: {}", card_priv_key);
|
||||||
let shared_secret = transform(door_pub_key, card_priv_key);
|
let shared_secret = transform(door_pub_key, card_priv_key);
|
||||||
println!("Shared secret: {}", shared_secret);
|
println!("Shared secret: {}", shared_secret);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user