g | x | w | all
Bytes Lang Time Link
054Google Sheets250815T102449Zdoubleun
162Python250815T125721ZCloudfer
072JavaScript ES6250815T161618ZArnauld
090Retina 0.8.2250815T225125ZNeil
024MATL250815T145018ZLuis Men
099Maple250815T213436Zdharr
012Jelly250815T182520ZJonathan
034Pip250815T193419ZDLosc
047Dyalog APL250815T165728ZAaron
02805AB1E250815T123949ZKevin Cr
025Uiua250815T085609Zmousetai
082JavaScript Node.js250815T093534Zl4m2

Google Sheets, 54 bytes

=index(ifs(B2:D4,1,len(A1:C3&C1:E3&A3:C5&C3:E5),2,1,))

Expects 1 instead of X, and blanks, i.e., empty values, instead of 0. Outputs the whole grid in one go as 1s, 2s and blanks.

To output x and * (58 bytes):

=index(ifs(B2:D4,"x",len(A1:C3&C1:E3&A3:C5&C3:E5),"*",1,))

screenshot

To change output grid size, use Insert > Cells within the input grid.

Python, 162 bytes

The input is given as a 2d grid of characters g.

def f(g):
 for _ in[0]*4:
  for r,o in zip(g,g[1:]):
   for j in range(len(r)-1):
    if'W'<r[j]and'X'!=o[j+1]:o[j+1]='*'
  g=[*map(list,zip(*g[::-1]))]
 return g

Try it online!

JavaScript (ES6), 72 bytes

Expects a string made of 0's and 1's for O and X. Returns another string with additional 2's for *.

s=>s.replace(/0/g,(_,i)=>s[i-=w=s.search`
`]|s[i-2]|s[i+=w*2]|s[i+2]&&2)

Try it online! (with post-processing for readability)

Commented

s =>                 // s = input string
s.replace(           // replace in s
  /0/g,              // each 0
  (_, i) =>          // at index i:
  s[                 //
    i -=             //   subtract from i
    w = s.search`\n` //   the width w of the matrix
  ] |                //   to test the top-right neighbor
  s[i - 2] |         //   subtract 2 to test the top-left neighbor
  s[i += w * 2] |    //   add 2w to test the bottom-left neighbor
  s[i + 2]           //   add 2 to test the bottom-right neighbor
  && 2               //   replace with 2 if any of them is 1
                     //   (or leave it to 0 otherwise)
)                    // end of replace()

Retina 0.8.2, 90 bytes

(?<=(.)*)O(?=.*¶(?<-1>.)*(?(1)^)(.X|(?<=X)))|O(?=(.)*)(?<=(X.|(?=X))(?(3)$)(?<-3>.)*¶.*)
*

Try it online! Link is to test suite that accepts double-spaced test cases. Explanation: The regex has two sets of two alternations; the first pair detect Os diagonally to the left or right above Xs while the second pair detect Os diagonally to the right or left below Xs.

Unless there's some other feature of Retina 1 that I'm forgetting that would make this shorter, this would actually be longer in Retina 1 as that requires * to be quoted with $ in the replacement string.

MATL, 24 bytes

'*XO'5Y"i79>2Y6~o6 5(Z+)

Try it online! Or verify all test cases.

How it works

'*XO'   % Push this string
5Y"     % Have each entry appear 5 times. Gives '*****XXXXXOOOOO' 
i       % Push input: 2D char array
79>     % Is each entry greater than 79 (code point of 'O')?
2Y6     % Push predefined literal [FTF; TTT; FTF]
~o      % Negate, convert to number. Gives [1 0 1; 0 0 0; 1 0 1]
6 5(    % Write 6 at the 5th entry. Gives [1 0 1; 0 6 0; 1 0 1]
Z+      % 2D convolution, without enlarging the array
)       % Index into the string. Gives a 2D char array
        % Implicitly display

Maple, 99 bytes

proc(M)for j in op(2,M)do for i,c in M do if add((i-lhs(j))^~2)=2and c=0then M[i]:=2fi od od;M end;
proc(M)              # accepts matrix of 0=sky, 1=star
for j in op(2,M) do  # lhs(j) has coords of stars
  for i,c in M do      # i = coords, c = contents
    # if squared distance to star is 2 and c = sky then shine (set = 2)
    if add((i-lhs(j))^~2)=2 and c=0 then M[i]:=2 fi
  od
od;
M
end;

Jelly, 12 bytes

ŒĠạċØ1ɗƇ/ŒṬ^

A monadic Link that accepts a list of lists of OX character codepoints (where O is sky, and X is a star), containing both character codepoints, and yields a list of lists of NOX character codepoints (where N is the shine).

Try it online!

How?

ŒĠạċØ1ɗƇ/ŒṬ^ - Link: 2d-array of OX characters, Map (both O and X present)
ŒĠ           - group multidimensional indices by their values
        /    - reduce this pair of lists of pairs by:
       Ƈ     -   keep those for which:
      ɗ      -     last three links as a dyad
                   - f([SkyR,SkyC], [[Star1R,Star1C],[Star2R,Star2C],...]):
  ạ          -       {[SkyR,SkyC]} absolute difference {[[Star1R,Star1C],[Star2R,Star2C],...]}
   ċ         -       count occurrences of...
    Ø1       -       ...literal [1,1]
         ŒṬ  - multidimensional array with 1 at {those} indices & 0 elsewhere
           ^ - XOR {Map}

Pip, 34 bytes

L$+Ya@?s+:[vo];R:aR:0.XXX*y.2o.S_a

Input and output via strings, rows separated by spaces, OX* -> 021.

Attempt This Online!

Explanation

This is a less-cryptic 35-byte version.

L2*Ya@?s;R:aR:0.XXX*[y-1y+1].2o.S_a
    a                                Command-line argument
     @?s                             Index of first space (= length of first row)
   Y                                 Yank (save as y)
 2*                                  Twice that amount
L       ;                            Loop that many times:
         R:a                          Reverse a and assign back to a
            R:                        Replace and assign back to a...
                                      Find this pattern:
                    [y-1y+1]           List of y plus or minus 1
                 XX*                   Make two regexes matching that many chars
                X                      Combine into one regex matching either option
              0.                       Concatenate 0 on the front
                            .2         Concatenate 2 on the end
                                      This regex matches a 0 on one line with a
                                      diagonally adjacent 2 on the next line
                                      Replace with:
                                S_     All but the first character of the match
                              o.       Concatenate 1 to the front
                                     The loop runs enough times to catch all the
                                     replacements
                                  a  Afterwards, output the final value of a

To get to the 34-byte version, observe that the sum of the plus-or-minus 1 array is exactly two times the length of the first row, which is the number we need for our loop count. [vo] is [-1;1].

Dyalog APL, 47 chars

{{⍵⌷'O*X'}¨1+(q×2)⌈∨⌿2=(⍸q←'X'=⍵)∘.(+/2*⍨-)⍳⍴⍵}­⁡​‎‎⁡⁠⁣⁣⁤‏⁠‎⁡⁠⁣⁤⁡‏⁠‎⁡⁠⁣⁤⁢‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁢⁢⁤‏⁠‎⁡⁠⁢⁣⁡‏⁠‎⁡⁠⁢⁣⁢‏⁠‎⁡⁠⁢⁣⁣‏⁠‎⁡⁠⁢⁣⁤‏⁠‎⁡⁠⁢⁤⁡‏⁠‎⁡⁠⁢⁤⁢‏⁠‎⁡⁠⁢⁤⁣‏⁠‎⁡⁠⁢⁤⁤‏⁠‎⁡⁠⁣⁡⁡‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁣⁡⁢‏⁠‎⁡⁠⁣⁡⁣‏‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁣⁡⁤‏⁠‎⁡⁠⁣⁢⁡‏⁠‎⁡⁠⁣⁢⁢‏⁠‎⁡⁠⁣⁢⁣‏⁠‎⁡⁠⁣⁢⁤‏⁠‎⁡⁠⁣⁣⁡‏⁠‎⁡⁠⁣⁣⁢‏⁠‎⁡⁠⁣⁣⁣‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁢⁢⁢‏⁠‎⁡⁠⁢⁢⁣‏‏​⁡⁠⁡‌⁢⁢​‎‎⁡⁠⁢⁡⁤‏⁠‎⁡⁠⁢⁢⁡‏‏​⁡⁠⁡‌⁢⁣​‎‎⁡⁠⁢⁡⁣‏‏​⁡⁠⁡‌⁢⁤​‎‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏⁠‎⁡⁠⁢⁡⁡‏⁠‎⁡⁠⁢⁡⁢‏‏​⁡⁠⁡‌⁣⁡​‎‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏‏​⁡⁠⁡‌⁣⁢​‎‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏‏​⁡⁠⁡‌­
                                           ⍳⍴⍵   # ‎⁡Get coordinates of every point of this input matrix
                       (⍸q←'X'=⍵)                # ‎⁢Find bitmask of stars, store in q, and then get list of their coordinates
                                 ∘.              # ‎⁣Outer product
                                   (+/2*⍨-)      # ‎⁤Sum of the square of their difference to find the square of the distance
                     2=                          # ‎⁢⁡Every diagonal connection to a star has a distance of exactly 2
                   ∨⌿                            # ‎⁢⁢Or-Over the multiple matrices generated (one for each star) to get full map of shine positions
                  ⌈                              # ‎⁢⁣Maximum with
             (q×2)                               # ‎⁢⁤The original star mask multiplied by two, so stars always win over any shine
           1+                                    # ‎⁣⁡Add 1 for indexing purposes
 {⍵⌷'O*X'}¨                                      # ‎⁣⁢Index each element of the map into this string
💎

Created with the help of Luminespire.

05AB1E, 35 33 28 bytes

4F¬ašøí}2Fø€ü3}εε˜ιнDÅssài_>

I/O as a matrix of digits, with 012 for OX* respectively.

It feels way too long.. :/

Try it online or verify all test cases.

Explanation:

4F¬ašøí}       # Add a border (of 0s) around the (implicit) input-matrix:
4F     }       #  Loop 4 times:
  ¬            #   Get the first row (without popping the matrix)
               #   (which will use the implicit input in the first iteration)
   a           #   Convert each 1 to a 0 with an isLetter check
    š          #   Prepend that list of 0s to the matrix
     øí        #   Rotate once 90 degrees clockwise:
     ø         #    Zip/transpose; swapping rows/columns
      í        #    Reverse each inner list
2Fø€ü3}        # Then convert it to a matrix of overlapping 3x3 blocks:
2F    }        #  Loop 2 times:
  ø            #   Zip/transpose; swapping rows/columns
   €           #   Map over each inner list:
    ü3         #    Convert it to overlapping triplets
εε             # Nested map over each 3x3 blocks:
  ˜            #   Flatten it to a list of nine items
   ι           #   Uninterleave it into two parts: [corners+center,edges]
    н          #   Pop and leave just the first item with the corners+center
     D         #   Duplicate it
      Ås       #   Pop the copy and push its center
        s      #   Swap so the corners+center are at the top again
         ài    #   If the max of these corners+center is 1
               #   (aka, it includes an "X"):
           _   #    ==0 check on the center that's on the stack
            >  #    +1
               #    (aka, 1/"X" remains 1/"X", but 0/"O" becomes 2/"*")
               # (after which the modified matrix is output implicitly as result)

Uiua, 25 glyphs

⨬∘⋅@*↧⊃(≠@X|/↥=@X⬚@O↻C₂).

Try it on the Uiua Pad

I'm starting a stopwatch till someone who actually understands Uiua halves my score.

Edit: Stopwatch stopped after 1 hour and 2 minutes.

Uiua, 11 glyphs

Takes input as an array of numbers. Uses 0 for "O", 1 for "*", 2 for "X":

↥/↥-₁⬚0↻C₂.

Pad Link

JavaScript (Node.js), 82 bytes

A=>A.map((r,y)=>r.map((c,x)=>c||(g=(a,b)=>(A[y+a]||0)[x+b]?2:a>b?0:g(-b,a))(1,1)))

Try it online!