Oops, I blew it up (with numbers)
Ruby, 162 bytes
->a{b=[*a=a.reduce{|x,y|[*x,*[(n=y.ord+~x[-1].ord)+n%2]*(n<1?0:2-n%2),y]}];i=-1;b.map{|x|d=-~i+=1;a.map!{|y|(z=x.to_i-(d-=1)*s=d<=>0)<1||x==b[i-s]?y:z+y.to_i}};a}
Try it online!
Takes input as an array of chars, returns a mixed array of chars/ints. (I'm still not sure whether this is allowed after reading the comments to the original post. If not allowed, it's +3 bytes to fix).
Explanation
First, we apply a reduce
operation, where we aren't actually reducing anything, but rather reconstruct the array a
as [*x,*[...],y]
, where the middle part contains the numbers inserted at ground zero.
We also make a copy of the array: b=[*a]
to serve as a reference, so that we don't get confused which numbers are at the center of the explosion, and which ones are introduced later on.
Then we iterate through b
and for each element x
, iterate through a
to record, what effect x
will have on each element y
of a
.
Conveniently, to_i
method returns 0 for non-numeric chars, so that we don't have to worry about distinction between numbers and text.
In my initial code, arrays were indexed by i
and j
, but this was golfed down to use a "delta" value d=i-j
.
Another helper variable is the sign of the delta: s=d<=>0
. At the first occurrence it performs the same function as abs
, and at the second - it helps us to block the other side of one-sided explosions as in 12344321
.
Ultimately, if z
is estimated to be positive, we add it up with the integer equivalent of y
and store in a
, otherwise, y
remains intact.
Perl 5, -p
110 109 bytes
Handling the even/odd rules waste around 20 bytes
#!/usr/bin/perl -p
s#.#$p+=$.;$c{$p-$_}+=$-=$z-abs$_+$z%2/2for-25..($z=ord($')-ord$&);$&x($.=$z<2||$z%2+2)#eg;s##$c{+pos}||$&#eg
Try it online!
Python 3, 351 348 bytes
Z=zip
R=range
E=lambda o:R(o,0,-1)
D=lambda d:[]if d<1else[[E(d),E(d+1)]]if d%2else[[E(d-1),[d]],[[],E(d)]]
def f(s):
s=sum(([c]+D(ord(n)+~ord(c))for c,n in Z(s,s[1:]+' ')),[]);l=len(s);v=[0]*l
for i,c in enumerate(s):
if len(c)>1:
for j,d in list(Z(R(i-1,-1,-1),c[0]))+list(Z(R(i,l),c[1])):v[j]+=d
print(*(d or c for c,d in Z(s,v)),sep='')
Try it online!
- −3 thanks to Kevin
The function f
takes one string as input and prints the result to standard output.
Ungolfed
def range_downto_0(o):
return range(o, 0, -1)
def D(d):
return (
[]
if d >= 0 else
[[range_downto_0(d), range_downto_0(d + 1)]]
if d % 2 else
[[range_downto_0(d - 1), [d]], [[], range_downto_0(d)]]
)
def f(s):
s = sum(([c] + D(ord(n) + ord(c) - 1) for c, n in zip(s, s[1:] + ' ')), [])
v = [0] * len(s)
for i, c in enumerate(s):
if len(c) == 2:
for j, d in itertools.chain(
zip(range(i - 1, -1, -1), c[0])),
zip(range(i, len(s)), c[1])
):
v[j] += d
return ''.join(str(d) if d else c for c, d in zip(s, v))
Explanation
Expand the string
s
to insert number range objects as placeholders for the "outward explosions". Save the result as a lists
.Create a list of zeros
v
with the same length ass
.Inspect
s
and, for every number range, add the numerical values to the value at their respective position inv
.v
now contains all the numbers that are going to be in the result at their correct positions.Iterate over
s
andv
simultaneously, take the entry fromv
if it is greater than 0 or the entry froms
otherwise and print each chosen entry in order.
The expensive repeated array concatenation in step 1 is at best in O(n2) but could be expressed in O(n) using a generator (assuming Python can construct lists from generators in O(n)). The remainder of this algorithm is in O(n ⋅ min{n, |Σ|} ⋅ log |Σ|) with Σ being the alphabet or, if you want to regard Σ as a constant, straight out O(n) (assuming that Python can join string generators in O(n)). “min{n, |Σ|}” comes from the outward range expansion and “log |Σ|” results from the conversion of numbers to strings.