# Virus vs Antidotes code golf

## Python 3, 131 bytes

j=''.join
g=lambda s:eval('j(s)'+4*'.replace("%s%s","%s%s")'%(*'vbbbbvbbavacvaca',))
f=lambda x:j(map(g,zip(*map(g,x)))).count('v')


Try it online!

-2 bytes thanks to Kevin Cruijssen

## Explanation

Replaces all 'v' to 'b' if found next to 'b'. Next, replaces all 'v' to 'c' if found next to 'a'. A second iteration with the transposed version of the array clears all vertical and diagonal viruses. Finally, it will return the remaining number of 'v's.

Without eval:

## Python 3, 134 bytes

j=''.join
g=lambda s,m='vbbbbvbbavacvaca':m and g(j(s).replace(m[:2],m[2:4]),m[4:])or s
f=lambda x:j(map(g,zip(*map(g,x)))).count('v')


Try it online!

## JavaScript (ES7), 108 bytes

Takes input as a matrix of characters.

f=m=>(g=(y,X,V)=>m.map(r=>r.map((v,x)=>V?v>f&V>'a'>(x-X)**2+y*y-2?r[x]=n--:0:v<f?g(-y,x,v):n++)|y++))(n=0)|n


Try it online!

Similar to my original answer, but doing V>'a'>(x-X)**2+y*y-2 is actually 1 byte shorter than using the hexa trick described below. ¯\_(ツ)_/¯

## JavaScript (ES7), 109 bytes

Takes input as a matrix of characters.

f=m=>(g=(y,X,V)=>m.map(r=>r.map((v,x)=>V?v>f&(x-X)**2+y*y<V-8?r[x]=n--:0:v<f?g(-y,x,'0x'+v):n++)|y++))(n=0)|n


Try it online!

### How?

The quadrance of two points $$\A_1=(x_1,y_1)\$$ and $$\A_2=(x_2,y_2)\$$ is defined as:

$$Q(A_1,A_2)=(x_2−x_1)^2+(y_2−y_1)^2$$

Considering integer coordinates, it looks as follows:

$$\begin{matrix} &8&5&4&5&8\\ &5&2&1&2&5\\ &4&1&\bullet&1&4\\ &5&2&1&2&5\\ &8&5&4&5&8 \end{matrix}$$

Therefore:

• a type A antidote located at $$\A_1\$$ is able to kill a virus located at $$\A_2\$$ if $$\Q(A_1,A_2)<2\$$
• a type B antidote located at $$\A_1\$$ is able to kill a virus located at $$\A_2\$$ if $$\Q(A_1,A_2)<3\$$

Conveniently, this exclusive upper bound ($$\2\$$ or $$\3\$$) can be obtained by converting the antidote character from hexadecimal to decimal and subtracting $$\8\$$:

• $$\\text{A}_{16} - 8_{10} = 2_{10}\$$
• $$\\text{B}_{16} - 8_{10} = 3_{10}\$$

### Commented

f =                      // named function, because we use it to test if a character
// is below or above 'm'
m => (                   // m[] = input matrix
g = (                  // g is a recursive function taking:
y,                   //   y = offset between the reference row and the current row
X,                   //   X = reference column
V                    //   V = reference value, prefixed with '0x'
) =>                   //
m.map(r =>           // for each row r[] in m[]:
r.map((v, x) =>    //   for each value v at position x in r[]:
V ?              //     if V is defined:
v > f &        //       if v is equal to 'v'
(x - X) ** 2 + //       and the quadrance between the reference point and
y * y          //       the current point
< V - 8 ?      //       is less than the reference value read as hexa minus 8:
r[x] = n--   //         decrement n and invalidate the current cell
:              //       else:
0            //         do nothing
:                //     else:
v < f ?        //       if v is either 'a' or 'b':
g(           //         do a recursive call:
-y,        //           pass the opposite of y
x,         //           pass x unchanged
'0x' + v   //           pass v prefixed with '0x'
)            //         end of recursive call
:              //       else:
n++          //         increment n
) | y++            //   end of inner map(); increment y
)                    // end of outer map()
)(n = 0)               // initial call to g with y = n = 0
| n                    // return n


## 05AB1E, 3330 29 bytes

2F.•s¯}˜?•2ô€Â2ä.:S¶¡øJ»}'v¢


Try it online or verify a few more test cases.

Port of @Jitse's Python 3 answer, so make sure to upvote him!
-1 byte thanks to @Jitse.

Explanation:

The legacy version has the advantage of being able to zip/transpose a string-list, where the new version would need an explicit S and J, since it only works with character-lists. But, the new version is still 3 bytes shorter by using €Â in combination with a shorter compressed string. In the legacy version, € would only keep the last value on the stack inside the map, but in the new version, it will keep all values on the stack inside the map.

2F                  # Loop 2 times:
.•s¯}˜?•          #  Push compressed string "vbvabbca"
2ô               #  Split it into parts of size 2: ["vb","va","bb","ca"]
€Â             #  Bifurcate (short for duplicate & reverse copy) each:
#   ["vb","bv","va","av","bb","bb","ca","ac"]
2ä           #  Split it into two parts:
#   [["vb","bv","va","av"],["bb","bb","ca","ac"]]
#  Push both those lists separated to the stack
.:        #  Replace all strings once one by one in the (implicit) input-string
S       #  Then split the entire modified input to a list of characters
¶¡     #  Split that list by newlines into sublists of characters
ø    #  Zip/transpose; swapping rows/columns
J   #  Join each inner character-list back together to a string again
»  #  And join it back together by newlines
}'v¢               '# After the loop: count how many "v" remain


See this 05AB1E tip of mine (section How to compress strings not part of the dictionary?) to understand why .•s¯}˜?• is "vbvabbca".