F=
->input{
rows = input.split'
'
node = Struct.new :incoming_connection_count, :next_node
nodes = Hash.new{|h,k|h[k] = node.new 0}
rows.size.times{|y|
x=0
rows[y].chars{|c|
case c
when ?>
from = [y,x-4]
to = [y,x+2]
when ?<
from = [y,x+4]
to = [y,x-2]
when ?^
from = [y+3,x]
to = [y-1,x]
when ?v
from = [y-3,x]
to = [y+1,x]
end
if from
nodes[from].next_node = nodes[to]
nodes[to].incoming_connection_count += 1
end
x+=1
}
}
current = nodes.values.find{|n|n.incoming_connection_count < 1}
loop_start = nodes.values.find{|n|n.incoming_connection_count > 1}
unless loop_start
# no loop; adjust the total node count
[nodes.size-1, 0]
else
# there's a loop; measure length of the tail
tail = 0
(tail+=1;current = current.next_node) while current!=loop_start
[tail, nodes.size-tail]
end
}
require 'minitest/autorun'
describe F do
def test_simple_case
assert_equal [2, 6], F[<<-DOC]
# --> # --> # --> #
^ ^ |
| | |
| | v
# # <-- # <-- #
DOC
end
def test_harder_case
assert_equal [3, 10], F[<<-DOC]
# --> # --> #
^ |
| |
| v
# --> # <-- # # --> #
^ ^ |
| | |
| | v
# --> # # <-- # <-- #
DOC
end
def test_no_loop
assert_equal [6, 0], F[<<-DOC]
# --> # --> # --> #
|
|
v
<-- # <-- #
DOC
end
end
CkY9Ci0+aW5wdXR7Cgpyb3dzID0gaW5wdXQuc3BsaXQnCicKbm9kZSA9IFN0cnVjdC5uZXcgOmluY29taW5nX2Nvbm5lY3Rpb25fY291bnQsIDpuZXh0X25vZGUKCm5vZGVzID0gSGFzaC5uZXd7fGgsa3xoW2tdID0gbm9kZS5uZXcgMH0KCnJvd3Muc2l6ZS50aW1lc3t8eXwKICB4PTAKICByb3dzW3ldLmNoYXJze3xjfAogICAgY2FzZSBjCiAgICB3aGVuID8+CiAgICAgIGZyb20gPSBbeSx4LTRdCiAgICAgIHRvID0gW3kseCsyXQogICAgd2hlbiA/PAogICAgICBmcm9tID0gW3kseCs0XQogICAgICB0byA9IFt5LHgtMl0gICAgIAogICAgd2hlbiA/XgogICAgICBmcm9tID0gW3krMyx4XQogICAgICB0byA9IFt5LTEseF0KICAgIHdoZW4gP3YKICAgICAgZnJvbSA9IFt5LTMseF0KICAgICAgdG8gPSBbeSsxLHhdCiAgICBlbmQKCiAgICBpZiBmcm9tCiAgICAgIG5vZGVzW2Zyb21dLm5leHRfbm9kZSA9IG5vZGVzW3RvXQogICAgICBub2Rlc1t0b10uaW5jb21pbmdfY29ubmVjdGlvbl9jb3VudCArPSAxCiAgICBlbmQKCiAgICB4Kz0xCiAgfQp9CgpjdXJyZW50ID0gbm9kZXMudmFsdWVzLmZpbmR7fG58bi5pbmNvbWluZ19jb25uZWN0aW9uX2NvdW50IDwgMX0KbG9vcF9zdGFydCA9IG5vZGVzLnZhbHVlcy5maW5ke3xufG4uaW5jb21pbmdfY29ubmVjdGlvbl9jb3VudCA+IDF9Cgp1bmxlc3MgbG9vcF9zdGFydAogICMgbm8gbG9vcDsgYWRqdXN0IHRoZSB0b3RhbCBub2RlIGNvdW50CiAgW25vZGVzLnNpemUtMSwgMF0KZWxzZQogICMgdGhlcmUncyBhIGxvb3A7IG1lYXN1cmUgbGVuZ3RoIG9mIHRoZSB0YWlsCiAgdGFpbCA9IDAKICAodGFpbCs9MTtjdXJyZW50ID0gY3VycmVudC5uZXh0X25vZGUpIHdoaWxlIGN1cnJlbnQhPWxvb3Bfc3RhcnQKICBbdGFpbCwgbm9kZXMuc2l6ZS10YWlsXQplbmQKCn0KCnJlcXVpcmUgJ21pbml0ZXN0L2F1dG9ydW4nCgpkZXNjcmliZSBGIGRvCgogIGRlZiB0ZXN0X3NpbXBsZV9jYXNlCiAgICBhc3NlcnRfZXF1YWwgWzIsIDZdLCBGWzw8LURPQ10KIyAtLT4gIyAtLT4gIyAtLT4gIwpeICAgICBeICAgICAgICAgICB8CnwgICAgIHwgICAgICAgICAgIHwKfCAgICAgfCAgICAgICAgICAgdgojICAgICAjIDwtLSAjIDwtLSAjCkRPQwogIGVuZAoKICBkZWYgdGVzdF9oYXJkZXJfY2FzZQogICAgYXNzZXJ0X2VxdWFsIFszLCAxMF0sIEZbPDwtRE9DXQogICAgICAgICAgICAjIC0tPiAjIC0tPiAjCiAgICAgICAgICAgIF4gICAgICAgICAgIHwKICAgICAgICAgICAgfCAgICAgICAgICAgfAogICAgICAgICAgICB8ICAgICAgICAgICB2CiAgICAgICMgLS0+ICMgPC0tICMgICAgICMgLS0+ICMKICAgICAgXiAgICAgICAgICAgXiAgICAgICAgICAgfAogICAgICB8ICAgICAgICAgICB8ICAgICAgICAgICB8CiAgICAgIHwgICAgICAgICAgIHwgICAgICAgICAgIHYKIyAtLT4gIyAgICAgICAgICAgIyA8LS0gIyA8LS0gIwpET0MKICBlbmQKCiAgZGVmIHRlc3Rfbm9fbG9vcAogICAgYXNzZXJ0X2VxdWFsIFs2LCAwXSwgRls8PC1ET0NdCiMgLS0+ICMgLS0+ICMgLS0+ICMKICAgICAgICAgICAgICAgICAgfAogICAgICAgICAgICAgICAgICB8CiAgICAgICAgICAgICAgICAgIHYKICAgICAgICA8LS0gIyA8LS0gIwpET0MKICBlbmQKCmVuZA==