Convert an ASCII art table to a UTF-8 table
Python 3, 914 898 827 823 594 587 569 540 469 bytes
Edit: significantly changed strategy, now making a bitfield of neighbors (similar to dead-possum's answer). I've left the earlier version below.
H='─│═-|=└╘++++┌╒├╞++┘╛++┴╧┐╕┤╡┬╤┼╪'
def n(l):
def j(r,c,t=0):O=(0,r-1,c),(1,r,c+1),(2,r+1,c),(3,r,c-1);v=f(r,c);b=t|any(f(Y,X)=='='for i,Y,X in O);l[r][c]={'+':H[b+2*sum((f(Y,X)in H)<<x for x,Y,X in O)],**dict(zip(H[3:6],H))}.get(v,v);[f(Y,X)!=';'and v in'+++-|='[i%2::2]and j(Y,X,v=='=')for i,Y,X in O]
for i,I in enumerate(l):
if'+'in I:f=lambda r,c:l[r][c]if len(l)>r>=0and 0<=c<len(l[r])else';';j(i,I.index('+'));break
Try it online!
Input is in the form of a list of lists of characters, which is modified in place. Recurses from the first + that it finds.
x=range
C='┌┐└┘','╒╕╘╛'
D='┬┤┴├','╤╡╧╞'
A='┼╪'
H,V,T='─│═'
J={'-':H,'|':V,'=':T}
K=C[1]+D[1]+A[1]+'='+T
E=('+|','+-=')*2
F=['+|'+V,'+-='+H+T]*2
O=(0,-1,0),(1,0,1),(2,1,0),(3,0,-1)
for i in x(4):
for j in{0,1,2,3}-{i}:F[i+2&3]+=D[0][j]+D[1][j]
h=C[0][i]+C[1][i];F[i&2]+=h;F[3-2*(i&1)]+=h
def n(l):
for i,I in enumerate(l):
if'+'in I:r=i;c=I.index('+');break
else:return l
def f(r,c):
try:assert c>=0 and r>=0;return l[r][c]
except:return'\0'
def j(r,c):
v=f(r,c)
l[r][c]=J.get(v,v)
if v=='+':
X=[f(r+Y,c+X)for i,Y,X in O];B=any(x in K for x in X);X=[X[x]in F[x]for x in x(4)];L=sum(X)
if L in(2,3,4):l[r][c]=D[B][X.index(False)]if L==3 else C[B][X[0]*2+X[3]]if L==2 else A[B]
for i,Y,X in O:
if v in E[i]and f(r+Y,c+X)in E[i]:j(r+Y,c+X)
j(r,c);return l
Try it online!
Here's the closest thing I have to an ungolfed version:
def tr(s):
t='┌┐└┘','╒╕╘╛'
t2='┬┤┴├','╤╡╧╞'
A = '┼','╪'
H,V,T = '─│═'
Th = ''.join(x[1]for x in (t,t2,A))+'='+T
ps = ['+|'+V, '+-='+H+T, '+|'+V, '+-='+H+T]
ps2 = ('+|', '+-=')*2
for i in range(4):
for j in {0,1,2,3}-{i}:
ps[(i+2)%4] += t2[0][j]+t2[1][j]
h=t[0][i] + t[1][i]
ps[i & 2] += h
ps[3 - 2 * (i & 1)] += h
l = [list(x) for x in s.split('\n')]
r = 0
for i,I in enumerate(l):
if'+'in I:
r=i;c=I.index('+')
break
def g(r,c): return l[r][c]
def G(r,c):
if r >= 0 and r < len(l) and c >= 0 and c < len(l[r]):
return g(r,c)
return '\0'
def process(r,c):
v = g(r,c)
if v == '-': l[r][c] = H
elif v == '|': l[r][c] = V
elif v == '=': l[r][c] = T
elif v == '+':
all=[G(r-1,c),G(r,c+1),G(r+1,c),G(r,c-1)]
bold=any(x in Th for x in all)
for i in range(4):all[i] = all[i] in ps[i]
N,E,S,W=all
tt=sum(all)
if tt == 3:
l[r][c]=t2[bold][all.index(False)]
elif tt == 2:
l[r][c]=t[bold][N*2+W]
elif tt == 4:
l[r][c]=A[bold]
else: return
for i,(dy,dx) in enumerate(((-1,0),(0,1),(1,0),(0,-1))):
if v in ps2[i] and G(r+dy,c+dx) in ps2[i]:
process(r+dy,c+dx)
process(r,c)
return l
Python 3, 392 281 bytes
Golfed it quite a bit more and converted to a recursive solution instead of an iterative one:
def h(a):
def g(i):
k=-3;d=a[i]=='=';z[i]=''
for p,j,r in zip((1,2,4,8),(i+1,i+w,i-1,i-w),('+-=','+|')*2):
if 0<=j<len(a)and{a[i],a[j]}<={*r}:k+=p;d|=a[j]=='=';z[j]and g(j)
z[i]="┌╒!!─═┐╕┬╤@@└╘││├╞┘╛┴╧┤╡┼╪"[2*k+d]
w=a.find('\n')+1;z=[*a];g(a.find('+'))
return''.join(z)
Takes a string of equal length rows separated by newlines, and returns a string in the same format. May throw an exception on invalid input.
Previous solution:
def h(a):
i=a.find('+');q=[i];w=a.find('\n')+1;z=[*a]
while q:
i=q.pop();c=a[i];k=-5
for p,j in enumerate((i+1,i-1,i+w,i-w)):
r='++-|='[p>1::2]
if 0<=j<len(a)and a[i]in r and a[j]in r:
k+=1<<p;q+=[j][:z[j]<'─']
z[i]='│'if c>'='else'─═'[a[i]>'-']if c>'+'else"┌╒┐╕┬╤@@└╘┘╛┴╧##├╞┤╡┼╪$$"['='in a[abs(i-1):i+2]::2][k]
return''.join(z)
Ungolfed version:
def h(a):
i = a.find('+') # find index of first '+'. It is first node
q = [i] # in the queue of indexes to convert to unicode
w = a.find('\n')+1 # width of the table
z = [*a] # strings are immutable, so copy it to a list
while q: # while the queue isn't empty
i=q.pop() # get the next index to process
c=a[i] # and the associated character
k=-5 # 'k' is the index into the unicode string, U. The way they
# are encoded, the first unicode value is at index 5.
# directions E W S N
for p,j in enumerate((i+1,i-1,i+w,i-w)): # j is the index of an adjacent cell
# r='++-|='[p>1::2] is equivalent to:
if p > 1:
r = '+|' # compatible symbols for vertical connections
else:
r = '+-=' # compatible symbols for horizontal connections
# if adjacent cell index is valid and the characters are compatible
if 0 <= j < len(a) and a[i] in r and a[j] in r:
k += 1<<p # update the unicode symbol index
# q += [j][:z[j]<'─'] is equivalent to:
if z[j] < '-': # if the adjacent cell hasn't been converted already
q.append(j) # append it's index to the queue
if c > '=':
z[i] = '│' # replace a '|' with a '│'
elif c > '+':
z[i] = '─═'[a[i]>'-'] # replace a '-' or '=' with '─' or '═' respectively
else: # it's a '+'
U = "┌╒┐╕┬╤@@└╘┘╛┴╧##├╞┤╡┼╪$$" # even indexes are single horizontal line,
# double horizontal lines are at odd indexes
z[i] = U['='in a[abs(i-1):i+2]::2][k] # '='in a[abs(i-1):i+2] is true if there is an '=' to the left or right
# so this selects the odd chars from U
# then [k] selects the correct char
return''.join(z)