|
|
|
@ -7,6 +7,7 @@ enum TokenType
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Empty,
|
|
|
|
Empty,
|
|
|
|
Number,
|
|
|
|
Number,
|
|
|
|
|
|
|
|
NumberRef, // This spot is taken up by a number but the number doesn't begin in this spot
|
|
|
|
Symbol,
|
|
|
|
Symbol,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -16,7 +17,8 @@ struct Token
|
|
|
|
ttype: TokenType,
|
|
|
|
ttype: TokenType,
|
|
|
|
length: u32,
|
|
|
|
length: u32,
|
|
|
|
value: i32,
|
|
|
|
value: i32,
|
|
|
|
symbol: u8
|
|
|
|
symbol: u8,
|
|
|
|
|
|
|
|
id: u32,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -25,27 +27,238 @@ fn main()
|
|
|
|
let data = load_data("data/input");
|
|
|
|
let data = load_data("data/input");
|
|
|
|
let schematic = parse_schematic(&data);
|
|
|
|
let schematic = parse_schematic(&data);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// PART 1
|
|
|
|
let mut sum = 0;
|
|
|
|
let mut sum = 0;
|
|
|
|
|
|
|
|
let mut gear_ratio_sum = 0;
|
|
|
|
for (row_idx, row) in schematic.iter().enumerate()
|
|
|
|
for (row_idx, row) in schematic.iter().enumerate()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
for (col_idx, token) in row.iter().enumerate()
|
|
|
|
for (col_idx, token) in row.iter().enumerate()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// PART 1
|
|
|
|
if token.ttype == TokenType::Number
|
|
|
|
if token.ttype == TokenType::Number
|
|
|
|
&& has_symbol_neighbor(row_idx, col_idx, &schematic)
|
|
|
|
&& has_symbol_neighbor(row_idx, col_idx, &schematic)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
sum += token.value;
|
|
|
|
sum += token.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// else
|
|
|
|
|
|
|
|
// {
|
|
|
|
|
|
|
|
// if token.ttype == TokenType::Number
|
|
|
|
|
|
|
|
// {
|
|
|
|
|
|
|
|
// println!("DEBUG: Num does not have neighbor: {}", token.value);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// PART 2
|
|
|
|
|
|
|
|
if token.symbol == b'*'
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
let gear_ratio = get_gear_ratio(row_idx, col_idx, &schematic);
|
|
|
|
|
|
|
|
gear_ratio_sum += gear_ratio;
|
|
|
|
|
|
|
|
// println!("DEBUG: Running gear ratio: {}", gear_ratio_sum);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
println!("PART 1: Sum: {}", sum);
|
|
|
|
|
|
|
|
println!("PART 2: Sum of Gear Ratios: {}", gear_ratio_sum);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn get_gear_ratio(row: usize, col: usize, schematic: &Vec<Vec<Token>>) -> i32
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut first_part_num = 0;
|
|
|
|
|
|
|
|
let mut first_num_id = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut offset_row = -1;
|
|
|
|
|
|
|
|
while offset_row < 2
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
let mut offset_col = - 1;
|
|
|
|
|
|
|
|
while offset_col < 2
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
let r_idx: isize = offset_row + (row as isize);
|
|
|
|
|
|
|
|
let c_idx: isize = offset_col + (col as isize);
|
|
|
|
|
|
|
|
if r_idx >= 0 && r_idx < schematic.len() as isize
|
|
|
|
|
|
|
|
&& c_idx >= 0 && c_idx < schematic[r_idx as usize].len() as isize
|
|
|
|
|
|
|
|
&& (schematic[r_idx as usize][c_idx as usize].ttype == TokenType::Number
|
|
|
|
|
|
|
|
|| schematic[r_idx as usize][c_idx as usize].ttype == TokenType::NumberRef)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num == 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
first_part_num = schematic[r_idx as usize][c_idx as usize].value;
|
|
|
|
|
|
|
|
first_num_id = schematic[r_idx as usize][c_idx as usize].id;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_num_id != schematic[r_idx as usize][c_idx as usize].id
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return first_part_num * schematic[r_idx as usize][c_idx as usize].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
offset_col += 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
offset_row += 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code, non_snake_case)]
|
|
|
|
|
|
|
|
fn get_gear_ratio_OLD(row: usize, col: usize, schematic: &Vec<Vec<Token>>) -> i32
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
let mut first_part_num = 0;
|
|
|
|
|
|
|
|
let mut second_part_num = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Find part nums
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TOP ROW
|
|
|
|
|
|
|
|
if row as isize - 1 >= 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// TOP LEFT
|
|
|
|
|
|
|
|
if col as isize - 1 > 0 && schematic[row - 1][col - 1].ttype == TokenType::Number || schematic[row - 1][col - 1].ttype == TokenType::NumberRef
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num == 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
first_part_num = schematic[row - 1][col - 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if token.ttype == TokenType::Number
|
|
|
|
// This is to try to avoid using the same part num twice
|
|
|
|
|
|
|
|
// since numbers take up multiple columns.
|
|
|
|
|
|
|
|
// It's assuming a gear will never be connected to two different
|
|
|
|
|
|
|
|
// part nums with the same value though. It will break if that happens!
|
|
|
|
|
|
|
|
if first_part_num != schematic[row - 1][col - 1].value
|
|
|
|
{
|
|
|
|
{
|
|
|
|
println!("DEBUG: Num does not have neighbor: {}", token.value);
|
|
|
|
return first_part_num * schematic[row - 1][col - 1].value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TOP CENTER
|
|
|
|
|
|
|
|
if schematic[row - 1][col].ttype == TokenType::Number
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num == 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
first_part_num = schematic[row - 1][col].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num != schematic[row - 1][col].value
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return first_part_num * schematic[row - 1][col].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TOP RIGHT
|
|
|
|
|
|
|
|
if col + 1 < schematic[row - 1].len() && schematic[row - 1][col + 1].ttype == TokenType::Number
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num == 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
first_part_num = schematic[row - 1][col + 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num != schematic[row - 1][col + 1].value
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return first_part_num * schematic[row - 1][col + 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// BOTTOM ROW
|
|
|
|
|
|
|
|
if row + 1 < schematic.len()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// BOTTOM LEFT
|
|
|
|
|
|
|
|
if col as isize - 1 > 0 && schematic[row + 1][col - 1].ttype == TokenType::Number
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num == 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
first_part_num = schematic[row + 1][col - 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num != schematic[row + 1][col - 1].value
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return first_part_num * schematic[row + 1][col - 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// BOTTOM CENTER
|
|
|
|
|
|
|
|
if schematic[row + 1][col].ttype == TokenType::Number
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num == 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
first_part_num = schematic[row + 1][col].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num != schematic[row + 1][col].value
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return first_part_num * schematic[row + 1][col].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// BOTTOM RIGHT
|
|
|
|
|
|
|
|
if col + 1 < schematic[row + 1].len() && schematic[row + 1][col + 1].ttype == TokenType::Number
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num == 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
first_part_num = schematic[row + 1][col + 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num != schematic[row + 1][col + 1].value
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return first_part_num * schematic[row + 1][col + 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CENTER ROW
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CENTER LEFT
|
|
|
|
|
|
|
|
if col as isize - 1 > 0 && schematic[row][col - 1].ttype == TokenType::Number
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num == 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
first_part_num = schematic[row][col - 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num != schematic[row][col - 1].value
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return first_part_num * schematic[row][col - 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// BOTTOM RIGHT
|
|
|
|
|
|
|
|
if col + 1 < schematic[row].len() && schematic[row + 1][col + 1].ttype == TokenType::Number
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num == 0
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
first_part_num = schematic[row][col + 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if first_part_num != schematic[row][col + 1].value
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return first_part_num * schematic[row][col + 1].value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
println!("Sum: {}", sum);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn has_symbol_neighbor(row: usize, col: usize, schematic: &Vec<Vec<Token>>) -> bool
|
|
|
|
fn has_symbol_neighbor(row: usize, col: usize, schematic: &Vec<Vec<Token>>) -> bool
|
|
|
|
@ -125,13 +338,14 @@ fn parse_schematic(input: &str) -> Vec<Vec<Token>>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// EMPTY
|
|
|
|
// EMPTY
|
|
|
|
b'.' => parsed[row].push(Token { ttype: TokenType::Empty, length: 1, value: 0, symbol: b'.' }),
|
|
|
|
b'.' => parsed[row].push(Token { ttype: TokenType::Empty, length: 1, value: 0, symbol: b'.', id: idx as u32 }),
|
|
|
|
|
|
|
|
|
|
|
|
// NUMBER
|
|
|
|
// NUMBER
|
|
|
|
b'0'..=b'9' =>
|
|
|
|
b'0'..=b'9' =>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
let mut num: String = String::new();
|
|
|
|
let mut num: String = String::new();
|
|
|
|
let mut len = 0;
|
|
|
|
let mut len = 0;
|
|
|
|
|
|
|
|
let id = idx as u32;
|
|
|
|
while bytes[idx] >= b'0' && bytes[idx] <= b'9'
|
|
|
|
while bytes[idx] >= b'0' && bytes[idx] <= b'9'
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
@ -145,19 +359,19 @@ fn parse_schematic(input: &str) -> Vec<Vec<Token>>
|
|
|
|
idx -= 1;
|
|
|
|
idx -= 1;
|
|
|
|
|
|
|
|
|
|
|
|
let num = num.parse::<i32>().expect(&format!("Failed to parse number: {} on line: {}", num, row));
|
|
|
|
let num = num.parse::<i32>().expect(&format!("Failed to parse number: {} on line: {}", num, row));
|
|
|
|
parsed[row].push(Token { ttype: TokenType::Number, length: len, value: num, symbol: bytes[idx] });
|
|
|
|
parsed[row].push(Token { ttype: TokenType::Number, length: len, value: num, symbol: bytes[idx], id });
|
|
|
|
|
|
|
|
|
|
|
|
// Pad out the grid with extra empties to account for the digits being
|
|
|
|
// Pad out the grid with extra empties to account for the digits being
|
|
|
|
// squished into 1 grid space
|
|
|
|
// squished into 1 grid space
|
|
|
|
for i in 1..len
|
|
|
|
for i in 1..len
|
|
|
|
{
|
|
|
|
{
|
|
|
|
parsed[row].push(Token { ttype: TokenType::Empty, length: 1, value: 0, symbol: b'.'});
|
|
|
|
parsed[row].push(Token { ttype: TokenType::NumberRef, length: 1, value: num, symbol: b'0', id});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SYMBOL
|
|
|
|
// SYMBOL
|
|
|
|
_ => parsed[row].push(Token { ttype: TokenType::Symbol, length: 1, value: 0, symbol: bytes[idx] }),
|
|
|
|
_ => parsed[row].push(Token { ttype: TokenType::Symbol, length: 1, value: 0, symbol: bytes[idx], id: idx as u32 }),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
idx += 1;
|
|
|
|
idx += 1;
|
|
|
|
|