# 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)

• @undergroundmonorail This is different from that question. This question allows spawning your own tiles, and is about going as far as mathematically possible, not just to 2048. – Kendall Frey May 13 '14 at 13:44
• @TheDoctor 68 is a sum of powers of 2, and is what your score would be if you get 2, 4, 8, 16. – ace May 13 '14 at 15:39
• Is this really a duplicate? What more would it take to make it different? – Kendall Frey May 13 '14 at 16:07
• @Quincunx That would really generate a suboptimal game though. – Kendall Frey May 13 '14 at 20:26
• I found the heart of this challenge, "Find an optimal solution", to be unique, but admittedly it was a poor choice to enclose it in a duplicate "shell". This one screams, "Oh look, another 2048 Code Golf challenge." With close votes being so subjective, you really have to sell your challenge to the crowd. Sometimes that means generating your own terrible ripoff of 2048. – Rainbolt May 14 '14 at 13:36

## 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

if board.reduce(:*) > 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 && (board[12..15] == [32, 16, 4, 4] ||
board[12..15] == [16, 16, 4, 4] && board[8..10] == [256,64,32]) ||
board[11] == 16 && (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 && board[12..15] == [4, 4, 32, 4] ||
board[11] == 8 && 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
board.each_slice(4) {|row| puts row*' '}
puts
break if board[15] > 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

• Just to nitpick, spawning 4's doesn't give you an optimal score because you're not gaining the 4 points each time one is created as opposed to being generated by 2 2's. – BrunoJ Jul 29 '14 at 20:55
• @BrunoJ The score for this challenge is simply computed as the total of all tiles at the end, not the score you'd have in the actual game. But if that was the case, you're right of course. ;) ... Although I think with my strategy it wouldn't make a difference, because I'd only get to 128 instead of 256 then. – Martin Ender Jul 29 '14 at 21:03
• Oh, didn't catch that the scoring isn't the same, my apologies – BrunoJ Jul 29 '14 at 22:07