#!/usr/bin/env julia using Test function parse_stacks_line(line) parts = Iterators.partition(line, 4) return [s[2] for s in parts] end function read_stacks(io) stacks = Vector{Vector{Char}}() for line in eachline(io) if isempty(line) return stacks end if line[1:3] == " 1 " continue end stacks_row = parse_stacks_line(line) while length(stacks_row) > length(stacks) push!(stacks, []) end for (i, c) in Iterators.enumerate(stacks_row) if c == ' ' continue end pushfirst!(stacks[i], c) end end return stacks end struct Move count :: Int from :: Int to :: Int end function parse_move_line(line) # move 1 from 2 to 1 parts = split(line) return Move(parse(Int, parts[2]), parse(Int, parts[4]), parse(Int, parts[6])) end function read_apply_moves(stacks, io; rev::Bool = true) for line in eachline(io) move = parse_move_line(line) if rev for i in 1:move.count push!(stacks[move.to], pop!(stacks[move.from])) end else from_len = length(stacks[move.from]) from_range = UnitRange(from_len - move.count + 1, from_len) append!(stacks[move.to], splice!(stacks[move.from], from_range)) end end return stacks end function operate_crane(infile; rev::Bool = true) open(infile, "r") do io stacks = read_stacks(io) stacks2 = read_apply_moves(stacks, io, rev=rev) return stacks2 end end function top_crates(stacks) return join([last(s) for s in stacks]) end function test() @testset "elf crates 2000" verbose=true begin @test top_crates(operate_crane("example.txt")) == "CMZ" @test top_crates(operate_crane("input.txt")) == "QNHWJVJZW" end @testset "elf crates 2001" verbose=true begin @test top_crates(operate_crane("example.txt", rev=false)) == "MCD" @test top_crates(operate_crane("input.txt", rev=false)) == "BPCZJLFJW" end end function main() if size(ARGS, 1) == 0 test() else infile = ARGS[1] println("infile = ", infile) println("crates: ", read_stacks(infile)) stacks2 = operate_crane(infile) println("after : ", stacks2) println("top : ", top_crates(stacks2)) end end main()