168 lines
5.0 KiB
Rust
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
|
|
}
|
|
}
|