#!/usr/bin/env julia using Test struct Move direction :: Char count :: Int end mutable struct KnotPair head_x :: Int head_y :: Int tail_x :: Int tail_y :: Int end function parse_move(line) parts = split(line, limit=2) return Move(parts[1][1], parse(Int, parts[2])) end function state_changed(old_state, new_state) return (new_state.head_x != old_state.head_x || new_state.head_y != old_state.head_y || new_state.tail_x != old_state.tail_x || new_state.tail_y != old_state.tail_y) end function read_moves(io) return [parse_move(line) for line in readlines(io)] end function apply_move(state, direction) if direction == 'U' state.head_y += 1 elseif direction == 'D' state.head_y -= 1 elseif direction == 'L' state.head_x -= 1 elseif direction == 'R' state.head_x += 1 elseif direction != 'O' throw(DomainError("Unknown direction")) end x_diff = state.head_x - state.tail_x y_diff = state.head_y - state.tail_y if abs(x_diff) <= 1 && abs(y_diff) <= 1 return state end if abs(x_diff) <= 1 state.tail_x += x_diff state.tail_y += sign(y_diff) * (abs(y_diff) - 1) elseif abs(y_diff) <= 1 state.tail_y += y_diff state.tail_x += sign(x_diff) * (abs(x_diff) - 1) elseif abs(x_diff) == 2 && abs(y_diff) == 2 # possible with more than 2 knots state.tail_x += sign(x_diff) * (abs(x_diff) - 1) state.tail_y += sign(y_diff) * (abs(y_diff) - 1) else throw(DomainError("invalid state " * string(state))) end return state end function get_visited(moves; npairs::Int=1) visited = Set{NTuple{2, Int}}() states = [KnotPair(0, 0, 0, 0) for _ in 1:npairs] push!(visited, (states[npairs].tail_x, states[npairs].tail_y)) for move in moves for m in 1:move.count # println(move, " ", m) # print(" 1 ", states[1], " -> ") states[1] = apply_move(states[1], move.direction) # println(states[1]) for i in 2:npairs states[i].head_x = states[i-1].tail_x states[i].head_y = states[i-1].tail_y # old_state = deepcopy(states[i]) states[i] = apply_move(states[i], 'O') # if state_changed(states[i], old_state) # println(" ", i, " ", old_state, " -> ", states[i]) # end end push!(visited, (states[npairs].tail_x, states[npairs].tail_y)) end end return visited end function visited_map(visited) (min_x, max_x) = extrema([t[1] for t in visited]) (min_y, max_y) = extrema([t[2] for t in visited]) nrows = max_y - min_y + 1 ncols = max_x - min_x + 1 vmap = zeros(Int, nrows, ncols) for (x, y) in visited vmap[nrows - (y - min_y), (x - min_x + 1)] = 1 end return vmap end function test() @testset "visited count 2 knot" verbose=true begin @test length(get_visited(read_moves("example.txt"))) == 13 @test length(get_visited(read_moves("input.txt"))) == 6037 end @testset "visited count 10 knot" verbose=true begin @test length(get_visited(read_moves("example.txt"), npairs=9)) == 1 @test length(get_visited(read_moves("input.txt"), npairs=9)) == 2485 end end function main() if size(ARGS, 1) == 0 test() else infile = ARGS[1] println("infile = ", infile) moves = read_moves(infile) println("moves: ", moves) visited = get_visited(moves) println("visited: ", visited) println("count : ", length(visited)) display(visited_map(visited)) println() visited10 = get_visited(moves, npairs=9) println("visited10: ", visited10) println("count10 : ", length(visited10)) display(visited_map(visited10)) println() end end main()