adventofcode2022/day05/common.rs

168 lines
5.0 KiB
Rust

// Copyright 2022 Christian Ulrich
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use std::io;
#[derive(Debug)]
pub struct Stacks {
inner: Vec<Vec<char>>,
}
pub struct Moves {}
#[derive(Debug)]
pub struct Move {
count: usize,
from: usize,
to: usize,
}
impl Stacks {
pub fn from_stdin() -> Result<Self, &'static str> {
let mut stacks = vec![];
for line in io::stdin().lines() {
match Self::parse_line(&line.unwrap())? {
None => break,
Some(items) => {
if items.len() != stacks.len() {
if !stacks.is_empty() {
return Err("input file contains invalid line");
}
stacks = vec![vec![]; items.len()];
}
for i in 0..items.len() {
if let Some(item) = items[i] {
stacks[i].push(item);
}
}
}
}
}
for stack in stacks.iter_mut() {
stack.reverse();
}
Ok(Self { inner: stacks })
}
fn parse_line(input: &str) -> Result<Option<Vec<Option<char>>>, &'static str> {
if input.starts_with(" 1 ") {
return Ok(None);
}
let mut result = vec![];
for chunk in input.chars().collect::<Vec<_>>().chunks(4) {
match chunk.iter().collect::<String>().as_str() {
"" => break,
item => result.push(Self::parse_item(item)?),
}
}
Ok(Some(result))
}
fn parse_item(input: &str) -> Result<Option<char>, &'static str> {
match input.trim() {
"" => return Ok(None),
item => {
if item.len() == 3
&& item.chars().nth(0).unwrap() == '['
&& item.chars().nth(2).unwrap() == ']'
{
return Ok(item.chars().nth(1));
}
}
}
Err("cannot parse item")
}
pub fn move_items(&mut self, spec: Move) -> Result<(), &'static str> {
if spec.from >= self.inner.len()
|| spec.to >= self.inner.len()
|| spec.count > self.inner[spec.from].len()
{
return Err("invalid move");
}
for _ in 0..spec.count {
let item = self.inner[spec.from].pop().unwrap();
self.inner[spec.to].push(item);
}
Ok(())
}
pub fn move_items_retain_order(&mut self, spec: Move) -> Result<(), &'static str> {
if spec.from >= self.inner.len()
|| spec.to >= self.inner.len()
|| spec.count > self.inner[spec.from].len()
{
return Err("invalid move");
}
let split_index = self.inner[spec.from].len() - spec.count;
let mut items = self.inner[spec.from].split_off(split_index);
self.inner[spec.to].append(&mut items);
Ok(())
}
}
impl Iterator for Stacks {
type Item = Vec<char>;
fn next(&mut self) -> Option<Self::Item> {
match self.inner.len() {
0 => None,
_ => Some(self.inner.remove(0)),
}
}
}
impl Move {
pub fn from_str(input: &str) -> Result<Self, &'static str> {
let parts = input.split(' ').collect::<Vec<_>>();
if parts.len() != 6 || parts[0] != "move" || parts[2] != "from" || parts[4] != "to" {
return Err("cannot parse move");
}
let from = match parts[3].parse::<usize>().map_err(|_| "cannot parse move")? {
value if { value > 0 } => Ok(value - 1),
_ => Err("integer out of range"),
};
let to = match parts[5].parse::<usize>().map_err(|_| "cannot parse move")? {
value if { value > 0 } => Ok(value - 1),
_ => Err("integer out of range"),
};
Ok(Self {
count: parts[1].parse().map_err(|_| "cannot parse integer")?,
from: from?,
to: to?,
})
}
}
impl Moves {
pub fn from_stdin() -> Self {
Self {}
}
}
impl Iterator for Moves {
type Item = Result<Move, &'static str>;
fn next(&mut self) -> Option<Self::Item> {
for line in io::stdin().lines() {
let line = line.unwrap();
if line.len() > 0 {
return Some(Move::from_str(&line));
}
}
None
}
}