Play a perfect game of 2048 Your job is to simulate a mathematically perfect game of 2048. The idea is to find the theoretical upper limit of how far a 2048 game can go, and find how to get there.

To get an idea of what this looks like, play with this 2x2 clone and try to score 68 points. If you do, you will end up with a 2, 4, 8, and 16 tile. It's impossible to advance past that point.

Your task is made easier because you can choose where tiles spawn and what their values are, just like this clone.

You must write a program or function that accepts a 2048 board as input, and outputs the board with the spawned tile and the board after collapsing tiles. For example:

Input:
-------
0 0 0 0
0 0 0 0
0 0 0 0
0 0 8 8

Output:
-------
0 0 0 0
0 0 0 0
0 0 0 0
0 4 8 8

0 0 0 0
0 0 0 0
0 0 0 0
0 0 4 16


Your program will be repeatedly fed its own output to simulate an entire game of 2048. The first input of the program will be an empty board. You must spawn one tile on it, unlike the original game's two tiles. At the last step of the game, you will be unable to move, so your two output boards can be identical.

You must of course only output legal moves. Only a 2 or 4 can be spawned, you must move or collapse at least one tile on a move, etc.

I've purposely made the input and output requirements vague. You are free to choose the format of the input and output. You can use matrices, arrays, strings, or whatever you want. As long as you can simulate a 2048 game with them, your inputs and outputs are fine.

The winner will be the one who ends the game with the highest sum of tiles on the board, then by the lowest number of bytes in the source code. The scoring from the original game will not be taken into account. (Hint: use 4's)
Ruby, Into the Corner, Score: 3340

Here is a very simple strategy to kick this off. I do have an idea for a (near) perfect score, but I'm having trouble formalising it, so here is something simple to get things going.

def slide board, dir
    case dir
    when 'U'
        i0 = 0
        i_stride = 1
        i_dist = 4
    when 'D'
        i0 = 15
        i_stride = -1
        i_dist = -4
    when 'L'
        i0 = 0
        i_stride = 4
        i_dist = 1
    when 'R'
        i0 = 15
        i_stride = -4
        i_dist = -1
    end

    4.times do |x|
        column = []
        top_merged = false
        4.times do |y|
            tile = board[i0 + x*i_stride + y*i_dist]
            next if tile == 0
            if top_merged || tile != column.last
                column.push tile
                top_merged = false
            else
                column[-1] *= 2
                top_merged = true
            end
        end

        4.times do |y|
            board[i0 + x*i_stride + y*i_dist] = column[y] || 0
        end
    end

    board
end

def advance board
    if board.reduce(:*) &gt; 0
        return board, board
    end

    16.times do |i|
        if board[15-i] == 0
            board[15-i] = 4
            break
        end
    end

    spawned = board.clone

    # Attention, dirty dirty hand-tweaked edge cases to avoid
    # the inevitable for a bit longer. NSFS!
    if board[11] == 8 &amp;&amp; (board[12..15] == [32, 16, 4, 4] ||
                          board[12..15] == [16, 16, 4, 4] &amp;&amp; board[8..10] == [256,64,32]) || 
       board[11] == 16 &amp;&amp; (board[12..15] == [32, 8, 4, 4] || 
                           board[12..15] == [4, 32, 8, 8] || 
                           board[12..15] == [4, 32, 0, 4])

        dir = 'R'
    elsif board[11] == 16 &amp;&amp; board[12..15] == [4, 4, 32, 4] ||
          board[11] == 8 &amp;&amp; board[12..15] == [0, 4, 32, 8]
        dir = 'U'
    else
        dir = (board.reduce(:+)/4).even? ? 'U' : 'L'
    end

    board = slide(board, dir)

    if board == spawned
        dir = dir == 'U' ? 'L' : 'U'
        board = slide(board, dir)
    end
    return spawned, board
end


The advance function is the one your asking for. It takes a board as 1d array and returns the board after the tile has been spawned and after the move has been made.

You can test it with this snippet

board = [0]*16
loop do
    spawned, board = advance(board)
    board.each_slice(4) {|row| puts row*' '}
    puts
    break if board[15] &gt; 0
end

puts "Score: #{board.reduce :+}"


The strategy is very simple, and is the one I actually used to skip to the 128 when I was playing 2048 myself: just alternate between up and left. To make this work for as long as possible, new 4s are spawned in the bottom right corner.

EDIT: I've added a hard coded switch to go right a few times at specific steps just before the end, which actually lets me reach 1024. This is getting somewhat out of hand though, so I'll stop with this for now and think about a generally better approach tomorrow. (Honestly, the fact that I can increase my score by a factor of 4 by adding hand-tweaked hacks only tells me that my strategy is crap.)

This is the board you end up with

1024 512 256 128
 512 256 128  16
 256 128  64   8
   8  32   8   4