// 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 . use std::io; #[derive(Debug)] pub struct Stacks { inner: Vec>, } pub struct Moves {} #[derive(Debug)] pub struct Move { count: usize, from: usize, to: usize, } impl Stacks { pub fn from_stdin() -> Result { 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>>, &'static str> { if input.starts_with(" 1 ") { return Ok(None); } let mut result = vec![]; for chunk in input.chars().collect::>().chunks(4) { match chunk.iter().collect::().as_str() { "" => break, item => result.push(Self::parse_item(item)?), } } Ok(Some(result)) } fn parse_item(input: &str) -> Result, &'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; fn next(&mut self) -> Option { match self.inner.len() { 0 => None, _ => Some(self.inner.remove(0)), } } } impl Move { pub fn from_str(input: &str) -> Result { let parts = input.split(' ').collect::>(); if parts.len() != 6 || parts[0] != "move" || parts[2] != "from" || parts[4] != "to" { return Err("cannot parse move"); } let from = match parts[3].parse::().map_err(|_| "cannot parse move")? { value if { value > 0 } => Ok(value - 1), _ => Err("integer out of range"), }; let to = match parts[5].parse::().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; fn next(&mut self) -> Option { for line in io::stdin().lines() { let line = line.unwrap(); if line.len() > 0 { return Some(Move::from_str(&line)); } } None } }