ASCII-Art Zombie Invasion Simulation
Kotlin, 283 218 bytes
Unnamed lambda (with a nested function, heh).
Golfed
{i:String,x:Int,y:Int->val m=i.lines().map{it.toCharArray()};fun v(x:Int,y:Int){try{if(m[y][x]=='#'){m[y][x]='%';for(c in-1..1)for(d in-1..1)if(!(c==0&&d==0))v(x+c,y+d)}}catch(e:Exception){}};v(x, y);m.map(::println)}
Ungolfed
fun zombies(input: String, startX: Int, startY: Int) {
val m = input.lines().map(String::toCharArray) // build game map
fun invade(x: Int, y: Int) { // nested functions, woo!
try {
if (m[y][x] == '#') { // if land
m[y][x] = '%' // mark as invaded
for (dx in -1..1) { // generate neighbour tiles
for (dy in -1..1) {
if (!(dx == 0 && dy == 0)) {
invade(x + dx, y + dy) // attempt to invade neighbours
}
}
}
}
} catch(e: Exception) {} // catches ArrayIndexOutOfBounds
}
invade(startX, startY) // start the invasion
m.map(::println) // print final state
}
Saved quite a few bytes by switching to a recursive solution.
JavaScript (ES6), 144 bytes
(s,x,y,l=s.search`\n`,g=s=>s==(s=s.replace(eval(`/(#|%)(.?[^]{${l-1}}.?)?(?!\\1)[#%]/`),`%$2%`))?s:g(s))=>g(s.slice(0,x+=y*l)+`%`+s.slice(x+1))
Where \n
represents the literal newline character. Takes 0-indexed coordinates.
Befunge, 324 323 bytes
&00p&10p20p~$v<p02+g02*!g02:+1$$$$<
#<%>\"P"/8+p>1+:::~:0`!#v_:85+`!#^_2%\2%3*1+*\2/:"P"%\"P"/8+g+\2/:"P"
:+**73"="+g00*g02g010$$$$<v
02:\-<v/"P"\%"P":/2::_|#:$<:+1+g02\+g02:\-1+g02:\+1:\-1:\+1-g
\:20g^>g:30p\2%3*1+/4%1->#^_::2%6*2+30g+\2/:"P"%\"P"/p:20g-1-
0<v2\g+8/"P"\%"P":/2::<\_@#`0:-g
2^>%3*1+/4%1g,1+:20g%#^_1+55+,\
Try it online!
Explanation
Implementing this in Befunge was a little bit complicated because we're limited to 80x25 characters of "memory" which has to be shared with the source code itself. The trick to fitting a 50x50 map into that area was to flatten the 2D map into a 1D array with two map locations per byte. This 1D array is then wrapped into a 2D array again so that it can fit in the 80 character width of the Befunge playfield.
The infection algorithm starts by converting the initial coordinates into an offset in the 1D array which it pushes onto the stack. The main loop takes a value from the stack and looks up the map state for that offset. If it's uninfected land, it gets marked as infected, and eight new offsets are pushed onto the stack (representing the land all around the current position). This process continues until the stack is empty.
To avoid having to check for out of range values, the map is stored with a one character water border around all the edges.