CodeFights: Game 2048 Description


  You are most likely familiar with the game 2048.
  
  2048 is played on a gray 4 × 4 grid with numbered tiles that slide smoothly when a player moves them using one of the four arrow keys -  Up ,  Down ,  Left  or  Right . On every turn, a new tile with a value of either 2 or 4 randomly appears on an empty spot of the board. After one of the keys is pressed, the tiles slide as far as possible in the chosen direction until they are stopped either by another tile or by the edge of the grid. If two tiles with the same number collide while moving, they merge into a tile with this number doubled. You can't merge an already merged tile in the same turn. If there are more than 2 tiles in the same row (column) that can merge, the farthest (closest to an edge) pair will be merged first (see the second example).
  
  In this problem you are not going to solve the 2048 puzzle, but you are going to find the final state of the board from the given one after a defined set of n arrow key presses, assuming that no new random tiles will appear on the empty spots.
  
  The following example shows the next state of the board after pressing Right.
  
  
  
  You are given a matrix 4 × 4 which corresponds to the 2048 game grid. grid[0][0] corresponds to the upper left tile of the grid. Each element of the grid is equal to some power of 2 if there is a tile with that value in the corresponding position, and 0 if it corresponds to the empty spot.
  You are also given a sequence of key presses as a string path. Each character of the string equals L, R, U, or D corresponding to  Left ,  Right ,  Up  or  Down  respectively.
  
  Please note that in some cases after pressing an arrow key nothing will be changed.


Example

For

grid = [[0, 0, 0, 0],
        [0, 0, 2, 2],
        [0, 0, 2, 4],
        [2, 2, 4, 8]]


and path = "RR"

the output should be

game2048(grid, path) = [[0, 0, 0, 0],
                        [0, 0, 0, 4],
                        [0, 0, 2, 4],
                        [0, 0, 8, 8]]


Code

def merge(row, left_or_down):
    i, new_row = 0, []
    transposed = [e for e in row if e]

    if not left_or_down: 
        transposed = transposed[::-1]

    while i &lt; len(transposed):
        if i == len(transposed) - 1:
            new_row += [transposed[i]]
            break
        if transposed[i] == transposed[i + 1]:
            new_row += [2 * transposed[i]]
            i += 2
        else:
            new_row += [transposed[i]]
            i += 1

    if not left_or_down: 
        new_row = [0]*(4 - len(new_row)) + new_row[::-1]

    return  new_row + [0]*(4 - len(new_row))

def game2048(grid, path):    
    for direc in path:

        if direc in 'LR':
            grid = [merge(row, direc == 'L') for row in grid]

        if direc in 'UD':
            grid = [merge(row[::-1], direc == 'D') for row in zip(*grid)]
            grid = [row for row in zip(*grid)][::-1]

    return grid
The game2048 function is short, which is good. It could be more clear to the casual reader.
The 2nd grid assignment uses zip(*grid), which is fine, but it would benefit by being broken out as a named helper function, which the third grid assignment could also use. Helper names often do a better documentation job than comments do.

The merge function uses the identifier transposed, where perhaps row would have sufficed, without confusingly calling to mind the 90-degree aspect of matrix transpose.

The magic number 4 is distracting - please assign a board size constant and use that.

The code lacks comments, which is not necessarily a problem, especially since it tries hard to use meaningful identifiers. Its clarity would benefit from unit tests (or docstring tests) showing before / after effects on a grid or row. The "output should be" narrative is helpful; I'm suggesting writing it as a TestCase. In the course of doing that, you may find it useful for coverage or didactic purposes to break out the occasional helper function.