# Stop walking into walls!

Some text-based rogue-likes don't let you walk into walls, and give you your move back if you try to. Why do that when you can make the player move in the closest valid direction?

## The challenge

Write a program of function that, given a direction and a 3 by 3 grid of characters, outputs the same grid after the player has taken a step.

For example,

9
#..
.@#
#.#


becomes

#.@
..#
#.#


## Input

• The direction is given by a single digit from 1 to 9, each corresponding to 8 cardinal directions and standing still. This is derived from the relative locations of the numbers on a keypad:

NW   N   NE
..\  |  /
...7 8 9
W- 4 5 6 -E
...1 2 3
../  |  \
SW   S   SE

However, you may also use the numbers 123, 456, 789 instead of 789, 456, 123. In other words, you may swap the top and bottom 2 rows or numbers if you so choose. These are the only 2 acceptable index combinations.

• The 3 by 3 grid will consist of 3 distinct, printable ASCII characters representing walkable floor, walls, and the player. (In the test cases, . is used for floor, #s are walls, and @ is the player)

• You may choose what characters your program uses, but you must state them in your answer and they must be consistent over multiple trials.
• The character representing the character will always be in the middle of the 3 by 3 grid, and the direction will always be between 1 and 9 (incl.)
• You may take input in any order
• The 3 by 3 grid can be input as a char array, a string array, a 9 length string, or another reasonable alternative.

## Output

• Return from a function, or output to StdOut or closest alternative
• Trailing spaces and newlines are allowed
• You must use the same character representations as the input
• The same formats allowed for inputs are allowed for outputs

## How the player moves

If the specified direction is blocked by a wall (e.g. if the above example had direction 6), then look at the 2 closest directions:

• If one (and only one) direction is free, move the player in that direction.
• If no directions are free, look at the next closest 2 directions (excluding direction 5). If you have wrapped all the way around and found no open direction (player surrounded by walls), do not move the player
• If both of the directions are open, pick one to move to at random (although not necessarily uniformly).

If the given direction is a 5, do not move the player

## Test cases

(# = wall, . = floor, @ = player)

Input:

9
#..
.@#
#.#

Output:

#.@
..#
#.#

Input:

3
#..
.@#
#.#

Output:

#..
..#
#@#

Input:

7
##.
#@#
..#

Output:

##@      ##.
#.#  or  #.#
..#      @.#

Input:

5
...
.@.
...

Output:

...
.@.
...

Input:

2
###
#@#
###

Output:

###
#@#
###


## Scoring

This is , so the shortest answer in bytes wins.

• Not using yuhjklbn? RIP. – Rɪᴋᴇʀ May 3 '17 at 1:16
• Also, I don't really like the "pick an alternative direction" part as much. It's kinda different from the other part of the challenge, which is "move the player in a specified direction and output.". That's just personal preference though. – Rɪᴋᴇʀ May 3 '17 at 1:18
• This part is confusing: "If no directions are free, look at the next closest 2 directions." – Leaky Nun May 3 '17 at 2:38
• Can we assign a number instead of an ASCII character for each element, then take input as a 2-dimensional list? Or do they have to be strings? – Scott Milner May 3 '17 at 3:09
• Can I use the directions 123;456;789 instead of 789;456;123? – Leaky Nun May 3 '17 at 3:14

# Pyth - 73 70 bytes

Kmsd"78963214"DPGJXXYt@K+xKQG\@4\.R?q/J\#/Y\#Jk=YwV5=GflTmPd(N_N)IGOGB


Try It

Input consists of two lines:

1st line: direction of move

2nd line: The board (positions 123456789, with 123 being the top row)

# JavaScript (ES6), 192 163 bytes

a=>b=>a[(a[4]=0)+--b]?(A=(c,d)=>c==b|(e=a[c])-(f=a[d])|!e?(a[c-b?(e?e<f:new Date&1)?c:d:4]=2,a):A(+g[c],+h[d]))(+(g="3016X2785")[b],+(h="1250X8367")[b]):(a[b]=2,a)


# Notes

This function uses a special input format. The first input is an array of integers (0 for floor, 1 for wall and 2 for player) representing the map. The second input is the (flipped) direction: 1 is north-west, 2 is north, 3 is north-east, 4 is west etc. The inputs should be given through currying syntax (Z(a)(b)).

# Test cases

The maps and directions have been modified to suit my input format.

Z=
a=>b=>a[(a[4]=0)+--b]?(A=(c,d)=>c==b|(e=a[c])-(f=a[d])|!e?(a[c-b?(e?e<f:new Date&1)?c:d:4]=2,a):A(+g[c],+h[d]))(+(g="3016X2785")[b],+(h="1250X8367")[b]):(a[b]=2,a)

testcases = [
[[1,0,0,0,2,1,1,0,1], 3],
[[1,0,0,0,2,1,1,0,1], 9],
[[1,1,0,1,2,1,0,0,1], 1],
[[0,0,0,0,2,0,0,0,0], 5],
[[1,1,1,1,2,1,1,1,1], 2]
]
for (test of testcases) {
console.log(Z(test[0])(test[1]))
}

# Python 3, 120104153176 175 bytes

def f(n,l):
n-=1
if n!=4and'.'in l:l[sorted(enumerate(l),key=lambda x:abs(x[0]%3-n%3+(x[0]//3-n//3)*1j)-ord(x[1])-__import__('random').random()/9)[1][0]],l[4]='@.'
return l


Try it online!

This method get direction and list of '.', '#' and '@'. Indexes start with 1 to 9 (with 0 to 8 in list really). So it has the form

123
456
789


Method return new list with new positions.

This line

sorted(enumerate(l),key=lambda x:abs(x[0]%3-n%3+(x[0]//3-n//3)*1j)-ord(x[1])-__import__('random').random()/9)


returns a list of this type:

>>>n=7
>>> l=['#','#','#','.','@','#','.','#','.']
>>> sorted(enumerate(l),key=lambda x:abs(x[0]%3-n%3+(x[0]//3-n//3)*1j)-ord(x[1])-__import__('random').random()/9)
[(4, '@'), (8, '.'), (6, '.'), (3, '.'), (7, '#'), (5, '#'), (1, '#'), (0, '#'), (2, '#')]


We calculate distances to free points and add randomness. Because ord('#') <= ord('.') - 8 and ord('.') + 8 <= ord('@') we can say that the nearest '.' for n=7(index in list) has an index of 8.

Example:

>>> f(9, ['#','.','.','.','@','#','#','.','#'])
['#', '.', '.', '.', '.', '#', '#', '@', '#']
>>> f(3, ['#','.','.','.','@','#','#','.','#'])
['#', '.', '@', '.', '.', '#', '#', '.', '#']
>>> f(5, ['.','.','#','.','@','.','#','.','#'])
['.', '.', '#', '.', '@', '.', '#', '.', '#']
>>> f(7, ['#','#','#','#','@','#','#','#','#'])
['#', '#', '#', '#', '@', '#', '#', '#', '#']
>>> f(7, ['#','.','.','.','@','#','#','.','#'])
['#', '.', '.', '@', '.', '#', '#', '.', '#'] or ['#', '.', '.', '.', '.', '#', '#', '@', '#']

• 1) This doesn't work for test cases where the direction the player wants to move in is occupied and the correct position is not the first ',' (which is the case for test cases 2, 3 and 5). 2) Your I/O format does not seem to be the same as in the question. Please mention your I/O format. – Luke May 3 '17 at 14:24
• Your example f(9, ... does not work - it places the character at 2 but the closest positions to 9 are 6 and 8, so one of those should be picked at random (there is no randomness in your code either). Furthermore it should then go around the perimeter finding the next closest, so f(9,list("####@#.##")) should place the character into the only possible spot (7 here). – Jonathan Allan May 3 '17 at 14:46
• Thanks for pointing out mistakes For me, the news is that code l[4],l[l.index('.')]='.@' and l[l.index('.')],l[4]='@.' are different – Кирилл Малышев May 3 '17 at 14:57
• @JonathanAllan, at first I did not quite understand the rules. I hope I could correct the code correctly. – Кирилл Малышев May 3 '17 at 15:40
• Still not correct, no. 1. the example f(9, ... should return list("#....##@#") (because 8 is free and next to 9). 2. Something like f(9,list("####@.#.#")) should have some chance of returning list("####..#@#") (not always list("####.@#.#")) as specified "If both of the directions are open, pick one to move to at random". – Jonathan Allan May 3 '17 at 15:52