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

  1. Expand the string s to insert number range objects as placeholders for the "outward explosions". Save the result as a list s.

  2. Create a list of zeros v with the same length as s.

  3. Inspect s and, for every number range, add the numerical values to the value at their respective position in v. v now contains all the numbers that are going to be in the result at their correct positions.

  4. Iterate over s and v simultaneously, take the entry from v if it is greater than 0 or the entry from s 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.