Stop walking into walls!

16

3

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.

MildlyMilquetoast

Posted 2017-05-03T00:59:12.127

Reputation: 2 907

Not using yuhjklbn? RIP. – Rɪᴋᴇʀ – 2017-05-03T01:16:18.310

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ɪᴋᴇʀ – 2017-05-03T01:18:44.187

3 line string falls under "reasonable alternative", and is acceptable. Also fixed the returning from a function bit. – MildlyMilquetoast – 2017-05-03T01:21:31.203

1This part is confusing: "If no directions are free, look at the next closest 2 directions." – Leaky Nun – 2017-05-03T02:38:07.717

1Can 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 – 2017-05-03T03:09:27.310

2Can I use the directions 123;456;789 instead of 789;456;123? – Leaky Nun – 2017-05-03T03:14:57.893

@LeakyNun Sure. – MildlyMilquetoast – 2017-05-03T04:40:42.497

If the random generation does not have to be uniform, can I just always pick the one counterclockwise to it? – HyperNeutrino – 2017-05-03T05:50:45.920

@HyperNeutrino No, there must be a >0 chance of each option – MildlyMilquetoast – 2017-05-03T05:51:56.503

Okay. Thanks for clarifying. – HyperNeutrino – 2017-05-03T06:03:29.653

Answers

2

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)

Maria

Posted 2017-05-03T00:59:12.127

Reputation: 644

3

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]))
}

Luke

Posted 2017-05-03T00:59:12.127

Reputation: 4 675

1

Python 3, 120 104 153 176 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.

enter image description here

Example:

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

Кирилл Малышев

Posted 2017-05-03T00:59:12.127

Reputation: 439

>

  • 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 – 2017-05-03T14:24:34.653

    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 – 2017-05-03T14:46:08.813

    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 – Кирилл Малышев – 2017-05-03T14:57:00.840

    @JonathanAllan, at first I did not quite understand the rules. I hope I could correct the code correctly. – Кирилл Малышев – 2017-05-03T15:40:14.097

    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 – 2017-05-03T15:52:21.573

    It seems to be working properly now... – Luke – 2017-05-04T16:46:11.527