# Compute the antipode of a point on curve

A curve is a set of points on a square grid such that each point has exactly two neighbors in the four-neighbor neighborhood and the points form a single connected component. That is, the graph induced by the points on a grid graph is isomorphic to a single cycle. "Induced" means that two points cannot touch in the input without being neighbors in the cycle.

An antipode of a vertex V in a graph is a vertex furthest away from V. The antipode is always unique on an even-length cycle (and every cycle on a grid graph is even-length). The distance shall be measured as induced by the cycle itself without respect for the underlying square grid.

Your input shall be an image of a curve. The curve will be marked out with a sequence of number sign characters (#) on a background out of space characters (). One of the points on the curve will be marked with the P character ("pode"). Your output shall be the same as the input except one curve point shall be replaced with A ("antipode").

You may assume the characters will be padded to a rectangular shape. You may assume the first and last row and column of input will be composed entirely of spaces (input is padded with background). Alternatively you may assume that the first and last row and column will each contain a curve point (input has minimum padding).

You may input and output this grid as a single newline-separated string, as an array of rows, or as a 2D array of individual characters. This choice shall be the same for the input and output. If your language allows this, you may output by modifying the input in place instead of returning the modified string or array.

Possible inputs:

P#    P##   #P#   #####      #####P# #######   #####P#########   #####P#########
##    # #   # #   #   #      #     # #     #   #             #   #             #
###   ###   ## ##      # ### # # ### #   # ### ### ### #   #             #
###                # # ###   # # # # # # # #   # # # # # # # #   #             #
# P#    ### ###    # ### #   # # ### ### # #   # # ### ### # #   #             #
## #    # ### #    #     #   # #         # #   # #         # #   #             #
# #    P     #    ##### P   # ########### #   # ##### ##### #   #             #
###    #######        ###   #             #   #     # #     #   #             #
###############   ####### #######   ###############


Corresponding outputs:

P#    P##   #P#   #A###      #####P# #A#####   #####P#########   #####P#########
#A    # #   # #   #   #      #     # #     #   #             #   #             #
##A   #A#   ## ##      # ### # # ### #   # ### ### ### #   #             #
###                # # ###   # # # # # # # #   # # # # A # # #   #             #
# P#    ### ##A    # ### #   # # ### ### # #   # # ### ### # #   #             #
## #    # ### #    #     #   # #         # #   # #         # #   #             #
A #    P     #    ##### P   # ########### #   # ##### ##### #   #             #
###    #######        ###   #             #   #     # #     #   #             #
###############   ####### #######   #########A#####


Vertex distances from the podes (modulo 10) (do not output these):

P1    P12   1P1   5A543      54321P1 9A98765   54321P123456789   54321P123456789
1A    1 3   2 2   4   2      6     2 8     4   6             0   6             0
23A   3A3   32 01      7 109 3 7 109 3   7 901 789 543 1   7             1
321                1 9 543   8 2 8 4 6 2 8 2   8 8 2 6 A 6 2 2   8             2
4 P1    234 89A    0 876 2   9 3 765 543 7 1   9 7 345 987 1 3   9             3
56 2    1 567 9    9     1   0 4         6 0   0 6         0 4   0             4
A 3    P     8    87654 P   1 56789012345 9   1 54321 56789 5   1             5
654    1234567        321   2             8   2     0 4     6   2             6
345678901234567   3456789 3210987   345678901A10987


# Python 2, 333221 215 bytes

-17 bytes thanks to @JanDvorak

g=input()
y=map(max,g).index('P')
x=g[y].index('P')
m=[k[:]for k in g]
v=x;w=y
while'#'in sum(m,[]):x,y,(v,w)=v,w,[(x+a,y+b)for a,b in((1,0),(-1,0),(0,1),(0,-1))if'#'==m[y+b][x+a]][0];m[w][v]='_'
g[w][v]='A'
print g


Try it online!

# Python 3, 402288 282 bytes, String IO

g=[[*k]for k in open(0).read().split('\n')]
y=[max(k)for k in g].index('P')
x=g[y].index('P')
m=[k[:]for k in g]
v=x;w=y
while'#'in sum(m,[]):x,y,(v,w)=v,w,[(x+a,y+b)for a,b in((1,0),(-1,0),(0,1),(0,-1))if'#'==m[y+b][x+a]][0];m[w][v]='_'
g[w][v]='A'
print('\n'.join(map(''.join,g)))


Try it online!

Animation of the code running:

## JavaScript (ES6), 193 181 bytes

f=s=>s==(P#1P#21#12#221A[r=replace](/.../g,([n,f,t])=>s=s[r](eval(/([${n+=f}])([^]{${s.search\n}})?(?!\\1)[${n}]/),m=>m[r](eval(/^${f}|${f}$/),t))),s)?s[r](/\d/g,#):f(s)


Version that provides a looping animation:

f=s=>s==(#A#1#12#221AP#1P#2[r=replace](/.../g,([n,f,t])=>s=s[r](eval(/([${n+=f}])([^]{${s.search\n}})?(?!\\1)[${n}]/),m=>m[r](eval(/^${f}|${f}$/),t))),s)?s[r](/\d/g,#):s
;setInterval(_=>i.value=f(i.value),1e3)
<textarea rows=10 cols=20 id=i style="font-family:monospace"></textarea>

# MATL, 43 42 bytes

32-I#fbbJ*+!w3>y"&)yy-|&X<]vJQ2/)G65b&Zj&(


The code accepts an arbitrary amount of space padding in the first and last rows and columns. Input is a rectangular array of chars, using ; as row separator. For example, the input

#####
#   #
## ##
# # ###
# ### #
#     #
##### P
###


is represented as

['#####   ';
'#   #   ';
'## ##   ';
' # # ###';
' # ### #';
' #     #';
' ##### P';
'     ###']


or, in one line (so it can be entered through STDIN),

['#####   '; '#   #   '; '## ##   '; ' # # ###'; ' # ### #'; ' #     #'; ' ##### P'; '     ###']


Try it online! Or verify the last four cases: 1, 2, 3, 4 (these have been chosen because they have the most complicated curves; the last one has some space padding).

### Explanation

TL;WR: Complex numbers, lots of indexing, no convolution.

32-     % Implicitly input char matrix. Subtract 32. Space becomes zero
I#f     % 3-output find: pushes nonzero values, their row indices,
% and their column indices, as column vectors
bb      % Bubble up twice, so row and column indices are on top
J*+     % Times 1j, add. This transforms row and column indices into
% complex numbers, where real is row and imaginary is column
!       % Transpose into a row vector
w       % Swap, so vector of nonzero values is on top
3>      % Logical index of elements exceeding 3. ASCII codes of space,
% '#' and 'P0 are 32, 35 and 80 respectively. Since 32 was
% subtracted these became 0, 3 and 48. So the only entry with
% value exceeding 3 is that corresponding to the original 'P'.
y"      % Do this as many times as the number of complex positions
%   The stack now contains the vector of complex positions and an
%   index into that vector. The index points to the complex position
%   to be processed next.
&)    %   Two-output reference indexing: pushes the indexed entry and
%   a vector with the remaining entries. This pulls off the
%   current complex position, which is initially that of 'P'
yy    %   Duplicate top two elements, i.e. current position and vector
%   of remaining positions
-|    %   Absolute differences
&X<   %   Index of minimum. Gives the index of the complex position
%   that is closest to the current one. In case of tie (which
%   only happens in the first iteration) it picks the first. The
%   result is the index of the complex position to be processed in
%   the next iteration. This index will be empty if this is the last
%   iteration.
]       % End
% The stack now contains the complex positions as individual
% values, starting from 'P' and sorted as per the curve; plus two
% empty arrays. These empty arrays have been produced by the last
% iteration as the index for the "next" iteration and the array of
% "remaining" complex positions
v       % Concatenate into a column vector. The empty arrays have no effect.
% The resulting vector contains the sorted complex positions
JQ      % Push 1j and add 1
2/      % Divide by 2. This gives (1+1j)/2. When used as an index this is
% interpreted as (1+end)/2. Since the length of the curve is even
% this gives a non-integer number, which will be implicitly
% rounded up (because of .5 fracctional part). As an example, for
% length 32 this gives 16.5, which rounded becomes 17. Position 17
% along the curve is the antipode of position 1
)       % Reference indexing. Gives the complex position of the antipode
G       % Push input char matrix again
65      % Push 65 (ASCII for 'A')
b       % Bubble up, so complex position is on top
&Zj     % Separate into real and imagimary parts, corresponding to row and
% column indices
&(      % 4-input assignment indexing. This writes 'A' at the specified row
% and column of the char matrix. Implicitly display


# Python 3, 421 bytes

l,e=len,enumerate
n=lambda x:[(x[0]-1,x[1]),(x[0]+1,x[1]),(x[0],x[1]-1),(x[0],x[1]+1)]
p=a={(i,j):y for i,x in e(r.split('\n'))for j,y in e(x)}
t=l(r.split('\n'));s=l(r.split('\n')[0])
for i in a:p=[p,i][a[i]=='P']
w=[p]
while l(w)!=r.count('#')+1:
for x in a:
if x in n(w[-1])and a[x]!=' 'and x not in w:w+=[x]
a[w[(l(w)+1)//2]]='A';print('\n'.join(''.join(a[j,i]for i in range(s))for j in range(t)))


Try it online!

# Mathematica, 234 223 bytes

(p=Position;v=p[#,"#"|"P"];n=Length@v;q=v[[#]]&;h=FindCycle[Graph[v,Join@@Table[If[Norm[q@i-q@j]==1,q@i<->q@j,Nothing],{i,n},{j,i-1}]]][[1,#,1]]&;ReplacePart[#,h@Mod[p[Table[h@x,{x,n}],p[#,"P"][[1]]][[1,1]]+n/2,n,1]->"A"])&


This code makes v be the vertex list for the graph: the indices of the "#" and "P"s. Then n is the length (necessarily even) and q extracts the input-th vertex (so ignoring the shape of the loop).

Then h is a function that builds the graph with vertices in v and undirected edges between vertices when the length of the difference of their index pairs is exactly 1 (so their difference is something like {-1,0} or {0,1}) and then finds a list of all cycles and provides the first (only) cycle (as a list of edges) and then takes the input-th edge and takes the first vertex making up that edge.

Using h, we can find the index of the "P" in the cycle, and go halfway around (using Mod to wrap around if we go past the bounds of the cycle list) to find the antipode, and then we can replace the corresponding entry of the original input m with "A"

You can try it online with by pasting the following at the Wolfram Cloud Sandbox and clicking "evaluate cell" or hitting Shift+Enter or Numpad Enter:

(p=Position;v=p[#,"#"|"P"];n=Length@v;q=v[[#]]&;h=FindCycle[Graph[v,Join@@Table[If[Norm[q@i-q@j]==1,q@i<->q@j,Nothing],{i,n},{j,i-1}]]][[1,#,1]]&;ReplacePart[#,h@Mod[p[Table[h@x,{x,n}],p[#,"P"][[1]]][[1,1]]+n/2,n,1]->"A"])&@{{"#","#","#","#","#"," "," "," "},{"#"," "," "," ","#"," "," "," "},{"#","#"," ","#","#"," "," "," "},{" ","#"," ","#"," ","#","#","#"},{" ","#"," ","#","#","#"," ","#"},{" ","#"," "," "," "," "," ","#"},{" ","#","#","#","#","#"," ","P"},{" "," "," "," "," ","#","#","#"}}//MatrixForm


## Alternative idea, 258 bytes

Slightly inspired by ovs's Python solutions, I tried to write code that would not use any graph theory features of Mathematica and just blindly calculate the distances. I couldn't get it as short, but suspect someone could improve it:

f[m_]:=(x="#";t=ReplacePart;p=Position;l=t[m,p[m,"P"][[1]]->0];v=p[l,x|0];n=Length[v];q=v[[#]]&;r=l[[q[#][[1]],q[#][[2]]]]&;y=t[l,q@#->(r@#2+1)]&;Do[If[Norm[q@i-q@j]==1&&Xor[r@i==x,r@j==x],If[r@i==x,l=y[i,j],l=y[j,i]]],n,{i,n},{j,n}];t[m,p[l,n/2][[1]]->"A"])


This code is very inefficient. Basically, it replaces "P" with 0 and then looks for a "#" next to something that's not a "#" by looping through the entire thing twice and replaces them with numbers representing the distance from "P", and to make sure it finishes, it does that process n times. This actually doesn't even calculate distances correctly since one branch could go past the antipode, but only one location will be numbered with n/2` no matter what.