Ruby 295
F=->i{l=i.lines
g={}
l.size.times{|y|i.size.times{|x|l[y][x]==?+&&g[[y,x]]=[[y,x]]}}
c=->a,b{w=g[b]+g[a];w.map{|x|g[x]=w}}
k=g.keys
k.product(k).map{|n,o|
r,p=n
s,q=o
((r==s&&p<q&&l[r][p...q]=~/^\+-[|-]*$/)||(p==q&&r<s&&l[r...s].map{|l|l[p]||c}.join=~/^\+\|[|-]*$/))&&c[n,o]}
g.values.uniq.size}
The program can be run in Ruby 2.1.5+. Here's a version which includes unit tests: http://pastebin.com/a8KTatL1
The approach is the following:
- make a list of all the
+
signs in the input and consider each of them a distinct shape
- find horizontal and vertical lines that link two
+
signs and combine their shapes into one
- in the end, the number of distinct shapes matches the result
Here's a more readable version:
def ascii_topology_count(input)
lines = input.lines
max_length = lines.map(&:size).max
# hash in which the keys are corners ("+"s), represented by their [y, x] coords
# and the values are arrays of corners, representing all corners in that group
corner_groups = {}
lines.size.times { |y|
max_length.times { |x|
if lines[y][x] == ?+
corner_groups[[y, x]] = [[y, x]]
end
}
}
# function that combines the groups of two different corners
# into only one group
combine_groups =-> c1, c2 {
g1 = corner_groups[c1]
g2 = corner_groups[c2]
g2 += g1
corner_groups[c1] = g2
g2.map{|x| corner_groups[x] = g2}
}
corner_groups.keys.product(corner_groups.keys).map{|c1, c2|
y1,x1=c1
y2,x2=c2
if y1 == y2 && x1 < x2
# test horizontal edge
t = lines[y1][x1...x2]
if t =~ /^\+-[|-]*$/
# p "#{c1}, #{c2}, [H] #{t}"
combine_groups[c1, c2]
end
end
if x1 == x2 && y1 < y2
# test vertical edge
t=lines[y1...y2].map{|l|l[x1]||' '}.join
if t =~ /^\+\|[|-]*$/
# p "#{c1}, #{c2}, [V] #{t}"
combine_groups[c1, c2]
end
end
}
corner_groups.values.uniq.count
end