1. Technology
You can opt-out at any time. Please refer to our privacy policy for contact information.

The Core Algorithm

By

The following article is part of a series.  For more articles in this series, see Cloning the Game 2048 in Ruby.  For the complete and final code, see the gist.

 

The Rotate Method

Since the rotate method is destructive, I've opted to call it rotate!. It's the same thing, almost verbatim, as discussed in the previous article.


def rotate!(n=1)
@board = (0..@size-1).map {|i| @board.map{|row| row[i] }.reverse }
end

 

The Slide Method

The slide method is the core of the whole 2048 game. It slides all the rows to the left, collapsing any empty tiles and combining any adjacent tiles with the same value. Remember that this method only slides the puzzles to the left. In order to slide the puzzle to the right, you need to rotate the puzzle twice (so it's essentially upside down, and left is right), slide the tiles and rotate the puzzle twice again. Similarly, to move up and down you rotate, slide and rotate again (always completing four rotations total).

The slide method is almost 30 lines long, but in the end it's not that difficult. First, the slide method keeps track of how many moves were made and returns this value. If a slide is done that makes no moves, no new pieces will be spawned (spawning is not done in the slide method).


def slide
	moves = 0

	# Collapse zeroes
	# …

	# Collapse like cells
	# …

	return moves
end

 

The actual meat of the method is separated into two loops. The first loop collapses any row's empty tiles. For example, if a row reads [4,0,4,2] before this loop, the row will read [4,4,2,0] after, having moved the tiles to the right of the zero to the left and made a new zero on the right.


# Collapse zeroes
(0...@size).each do|row|
	(0...@size-1).each do|col|
		while @board[row][col] == 0 && @board[row][col+1..@size-1].max > 0
			@board[row][col...@size-1] = @board[row][col+1..@size-1] + [0]
			moves += 1
		end
	end
end

 

Now, that can look pretty gnarly. So let's take it piece by piece. The outer move loop loops over the row indices of the 2D array representing the 2048 board. All we're looking for here is the row indices. Note that the range uses the triple dot exclusive range. For example, 1..3 represents the numbers [1,2,3], while 1...3 represents [1,2]. In the second loop, we're iterating over the column indices, but with one tiny twist. We don't want the right-most column index. If the right-most column is a 0, we just leave it, so for that we use @size-1 in the range. Let's strip those loops away and just remember that row and col are the row and column indices.


while @board[row][col] == 0 && @board[row][col+1..@size-1].max > 0
	@board[row][col...@size-1] = @board[row][col+1..@size-1] + [0]
	moves += 1
end

 

What we're looking at isn't so scary now. The while loop just tests if the cell we're currently looking at is zero and there are tiles to the right that aren't zero (if the max of the tiles to the right of the current tile is greater than zero). We then use the array index range assignment trick. For example, you can say a = [1,2,3,4]; a[0..1] = [8,8] and it will only assign to the elements 0 and 1 of the array. Here we're assigning the zero and everything to the right of it with everything to the right of it plus a zero on the end. Then we're just incrementing moves. That's it for collapsing zero, the only difficult line is the one that actually does the work.

And finally the part that collapses like cells, the meat of the game. Again, this has two outer loops that we're going to strip away before we look at it. The two outer loops do the same as the outer loops used to collapse the zeroes, they iterate over row and column indices.


# Collapse like cells
(0...@size).each do|row|
	(0...@size-1).each do|col|
		if @board[row][col] != 0 && @board[row][col] == @board[row][col+1]
			@board[row][col..@size-1] = @board[row][col+1..@size-1] + [0]
			@board[row][col] *= 2
			@score += @board[row][col]
			moves += 1
		end
	end
end

And with that noise removed we get:


if @board[row][col] != 0 && @board[row][col] == @board[row][col+1]
	@board[row][col..@size-1] = @board[row][col+1..@size-1] + [0]
	@board[row][col] *= 2
	@score += @board[row][col]
	moves += 1
end

 

The if statement is just checking for something rather simple. If the cell is not zero and the cell to the right is the same value, then do its thing. If they are equal we do the same collapse method as before, adding a zero on the end just like before. Then we double the current cell as if to "combine" the cells with the same value. The we increment score and moves.

That's it. It looks pretty dense, but in the end the code is really doing just a few simple things.

There's more!  To keep reading, see the next article in this series: The Final Few Methods.

  1. About.com
  2. Technology
  3. Ruby
  4. Tutorials
  5. Writing a 2048 Clone in Ruby
  6. The Core Algorithm

©2014 About.com. All rights reserved.