Skip to content

Instantly share code, notes, and snippets.

@oisin
Created December 5, 2023 19:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oisin/153bcfdfc408997cc69b744846731547 to your computer and use it in GitHub Desktop.
Save oisin/153bcfdfc408997cc69b744846731547 to your computer and use it in GitHub Desktop.
AOC 23 Day3 Part 2
#![allow(dead_code, unused_variables, unused_mut)]
use std::env;
use std::fs::File;
use std::io::{self, BufRead};
// PartNumber contains the i32 parsed value of the string slice that
// represents this part number, and records the start and end positions
// of the slice in the row.
#[derive(Clone, Copy)]
struct PartNumber {
value: i32,
start_pos: i32,
end_pos: i32
}
// Row can contain a collection of part numbers and/or symbols.
// A row is a single line read from the file and parsed
struct Row {
part_numbers: Vec<PartNumber>,
symbols: Vec<i32>
}
use std::fmt;
impl fmt::Display for PartNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} @ {}..{}", self.value, self.start_pos, self.end_pos)
}
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: {} <filename>", args[0]);
std::process::exit(1);
}
let filename = &args[1];
let file = match File::open(filename) {
Ok(file) => file,
Err(error) => {
eprintln!("Error opening file {}: {}", filename, error);
std::process::exit(1);
}
};
let reader = io::BufReader::new(file);
let mut field: Vec<Row> = Vec::new();
for (line_number, line) in reader.lines().enumerate() {
match line {
Ok(line_content) => {
field.push(parse(line_number + 1, line_content));
}
Err(error) => {
eprintln!("Error reading line {}: {}", line_number + 1, error);
}
}
}
// Process the field to get valid part numbers
//
// BUG a number can be counted as valid more than once
// eg
//
// ...33*44...
// ........*..
//
// Valid part number found on row 0, 33
// Valid part number found on row 0, 44
// Valid part number found on row 0, 44
// Total = 121
//
let mut accum: i32 = 0;
for (row_index, row) in field.iter().enumerate() {
// For each row in the field...
for sym_pos in row.symbols.iter() {
let mut nums: Vec<i32> = Vec::new();
// Iterate through all of the part numbers on the current row and
// if they align with this symbol.
for pn in row.part_numbers.iter() {
//println!("Symbol @ {} proximity check to part number {}", sym_pos, pn);
if *sym_pos == pn.end_pos + 1 {
// This symbol immediately follows a number.
// println!("{}: '{}' @ {}", row_index+1, pn.value, *sym_pos);
nums.push(pn.value);
}
if pn.start_pos > 0 && *sym_pos == pn.start_pos - 1 {
// This symbol immediately precedes a number.
//println!("{}: '{}' @ {}", row_index+1, pn.value, *sym_pos);
nums.push(pn.value);
}
}
// Iterate through all of the part numbers on the PREVIOUS row, if there is one, and
// if they align with this symbol.
if row_index > 0 {
for pn in field[row_index - 1].part_numbers.iter() {
//println!("Symbol @ {} above line proximity check to part number {}", sym_pos, pn);
// eg if symbol is at [1][3] then digit at [0][2/3/4]
let neg_off: i32 = if pn.start_pos == 0 { 0 } else { -1 };
if *sym_pos >= pn.start_pos + neg_off && *sym_pos <= pn.end_pos + 1 {
// println!("{}: '{}' @ {}", row_index, pn.value, *sym_pos);
nums.push(pn.value);
}
}
}
// Iterate through all of the part numbers on the NEXT row, if there is one, and
// if they align with this symbol.
if row_index < field.len()-1 {
for pn in field[row_index + 1].part_numbers.iter() {
//println!("Symbol @ {} below line proximity check to part number {}", sym_pos, pn);
// eg if symbol is at [1][3] then digit at [0][2/3/4]
let neg_off: i32 = if pn.start_pos == 0 { 0 } else { -1 };
if *sym_pos >= pn.start_pos + neg_off && *sym_pos <= pn.end_pos + 1 {
//println!("{}: '{}' @ {}", row_index+2, pn.value, *sym_pos);
nums.push(pn.value);
}
}
}
if nums.len() == 2 {
println!("Row {} nums are {:?}", row_index + 1, nums);
accum += nums[0] * nums[1];
}
}
//println!("row {} : syms = {}, part numbers = {}", row_index + 1, row.symbols.len(), row.part_numbers.len());
}
println!("Total = {}", accum);
}
#[derive(PartialEq)]
enum ParseState {
Start,
PartNumber
}
fn parse(line_number: usize, line_content: String) -> Row {
let mut row = Row {
part_numbers: Vec::new(),
symbols: vec![],
};
let mut state = ParseState::Start;
let mut context = PartNumber {
value: 0,
start_pos: 0,
end_pos: 0
};
for (pos, c) in line_content.chars().enumerate() {
match c {
('0'..='9') => {
match state {
ParseState::Start => {
context = PartNumber {
value: c.to_digit(10).expect("Bum not a digit") as i32,
start_pos: pos as i32,
end_pos: 0
};
state = ParseState::PartNumber;
}
ParseState::PartNumber => {
context.value = context.value * 10 + c.to_digit(10).expect("Bum not a digit") as i32
}
}
},
'*' => { // Gears only for part 2
if state == ParseState::PartNumber {
context.end_pos = pos as i32 - 1;
row.part_numbers.push(context);
}
row.symbols.push(pos as i32);
state = ParseState::Start;
},
_ => {
if state == ParseState::PartNumber {
context.end_pos = pos as i32 - 1;
row.part_numbers.push(context);
}
// row.symbols.push(pos as i32); ignore a non
state = ParseState::Start;
}
}
}
if state == ParseState::PartNumber { // We have come to the end of the line while we are parsing a number!
context.end_pos = (line_content.len() - 1) as i32;
row.part_numbers.push(context);
}
row
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment