Processes are simply evaluated from top to bottom, nothing special
happening there. The state of a signal is determined by the last
assignment in a process, so if both west and south are '1', buttons_west
is "0000".
The best way to deal with unexpected behaviour is to use the simulator.
First you should write a test bench to feed your module with input
values and check the respective output values. If there is something
that doesn't work you can find out exactly what is wrong by
single-stepping through the code.
You can however not find any problem with the simulator. Only a strictly
synchronous design, i.e. a design that contains no or only clocked
storage elements (flip flops) works the same in real hardware as in the
simulator. In your code, you are describing a latch, which is an
asynchronous storage element and needs to be avoided. A latch is
generated because, if neither west, east nor south is '1', some of the
signals have to keep the value from before the process execution. The
only way to do that in hardware without a clock is a latch. If you need
that behavior, use a clocked process ("if rising_edge(clk)..."). If you
don't, give all signals default assignments at the start of the process.
Then the result of the process will only depend from its input
variables, not from the previous state of the signals, and you get a
simple logic network without latches.