We ended up where we started!…

Perl 5, -plF 101 100 99 98 97 96 bytes

Replace the \0 by a literal 0 byte to get 96. Notice that the Try It Online link has 97 bytes because it doesn't seem possible to input a literal 0 character there.

#!/usr/bin/perl -plF
say;say$$l=~y/\0/ /runtil--$l,(s:\Q${\<>}:$$l|=v0 x((++$#$l%3*$l-$l+"@-")%@F).$&;v0:oreg^$$l)eq$_

The code golf perl highlighter thinks # start a comment. How naive

Try it online!

How it works

$l is a counter for which line after the first we are on (it counts down though, so e.g. -3 for 3 lines below the top string).After printing the initial string once it repeatedly does the following.

Search the first string for occurrences of the target character and calculate at which offset it should appear: (++$#$l%3*$l-$l+"@-")%@F which is the current position plus the line number (negative) times -1, 0, 1 (cyclic). Construct a string with that many times \0 followed by the target character and or that into an accumulator $$l (that is a different accumulator for each $l and the reason $l counts down instead of up because $1, $2 etc are read-only). Simularly $#$l refers to a different array each time through the loop. The result is the $lth line but with \0 instead of spaces.

The target charcters in the first string are replaced by \0 so you end up with the original string with "holes" (with \0) at the original positions of the target character. If you xor that with the accumulator the holes get filled if and only if the accumulator has the target characters in the original positions, so the result will be the original string. That is used to terminate the loop. If the loop is not finished yet print the accumulator with \0 replaced by space.

When the loop ends the -p option once more prints the first string and the program is done.

The target character is picked up in a rather tricky way. The ${\<>} converts a line read from STDIN to a reference which is then immediately dereferenced and substituted in the regex. The \Q prefix escapes all characters that are special in a regex (like . and *). The \E is implicit. The o modifier causes the search part to never be evaluated again but just repeated in all subsequent matches (which is good since there is nothing on STDIN anymore).


Python 2, 199 193 191 bytes

s,c=input()
print s;l=len(s);r=range;I=j=[i for i in r(l)if s[i]==c]
while 1:
 j=[(j[i]-i%3+1)%l for i in r(len(I))]
 if sorted(j)==I:print s;break
 print''.join((' '+c)[i in j]for i in r(l))

Try it online!


If the loop can exit via exception:

Python 2, 187 bytes

s,c=input()
print s;l=len(s);r=range;I=j=[i for i in r(l)if s[i]==c]
while 1:
 j=[(j[i]-i%3+1)%l for i in r(len(I))]
 if sorted(j)==I:print s;q
 print''.join((' '+c)[i in j]for i in r(l))

Try it online!


  • -4 bytes thanks to Jonathan Frech
  • -2 bytes thanks to Lynn

Ruby, 189 176 171 156 150 146 144 137 bytes

->s,c,r=0..k=s.size,b=[p,s]{d=0;g=s.tr c,n=' '*k;l=r=r.map{|i|l||c==s[i]?(n[z=(i+1-d%3)%k]=g[z]=c;d+=1;z):p}-b;g==s||b<<n&&redo;puts b,s}

Try it online!