input = DATA.readlines(chomp: true) .flat_map.with_index {|line, j| line.chars.map.with_index {|x, i| [[j,i], x] } }.to_h TURNS = { [-1,0] => [0,1], [0,1] => [1,0], [1,0] => [0,-1], [0,-1] => [-1,0], } Loop = Class.new(Exception) def patrol(lab) return enum_for(__method__, lab) unless block_given? start = lab.find { _2 == ?^ }.first guard = [start, [-1,0]] seen = Set.new loop do pos, dir = guard yield pos peek = pos.zip(dir).map {|i,di| i + di } return unless lab.has_key?(peek) raise Loop if seen.include?([peek, dir]) seen << [peek, dir] case lab.fetch(peek) when ?., ?^ guard[0] = peek when ?# guard[1] = TURNS.fetch(dir) when nil return else fail end end end def loop?(lab) patrol(lab).each {} false rescue Loop true end path = patrol(input).to_set pp path.length pp input .select { path.include?(_1) && _2 != ?^ } .select {|xy, _| # pp xy input[xy] = ?# res = loop?(input) input[xy] = ?. res }.size __END__ ....#..... .........# .......... ..#....... .......#.. .......... .#..^..... ........#. #......... ......#...