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

The Final Few Methods

By

See More About

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.

All the important methods were already talked about in the previous articles. We really need to talk about spawning new cells and seeing if we've met the game over condition though.

Spawning New Tiles

 

Every time you make a move (and twice at the beginning of the game) new tiles are spawned. These new tiles have a value of either 2 or 4 and can appear anywhere there is a blank tile. So first, we write a method that gets a list of empty tiles.


def empty_cells
	@board.map.with_index do|row, rowidx|
		row.map.with_index do|num, colidx|
			[rowidx, colidx, num]
		end
	end.flatten(1).find_all{|cell| cell[2] == 0}
end

 

The two map.with_index loops are used to create a list of all tiles on the board paired with their row and column indices. Since we don't care about the structure of the board here, we just want a list of tiles we can iterate over but still remember their row and column indices.

Then we flatten that list, but we only flatten one level. Since we iterated over the rows, then over the columns of each row, we'll have a few nested lists. We just want one list of lists (the sub-lists being the row, column and value list for each cell) we use flatten(1). What if we had just called flatten without the argument? We would have gotten just a list of values, and that would be too difficult to work with.

Then we use the find_all method from enumerable. We want to see if the cell value is 0, and if so, keep it in the list. The cell value in this ad hoc list is at index 2.

Now let's spawn some new tiles.


def spawn_new
	empty = empty_cells

	cell = empty.sample(1).flatten
	@board[cell[0]][cell[1]] = [2,4].sample(1).first
end

Get a list of empty cells. Sample 1 from that list of empty cells (sample returns a random sample from an array, as in a statistical sample). Assign to the resulting empty cell a random 2 or 4. Simple as that.

Game Over?

 

The game_over? method is also rather straightforward.


def game_over?
	if @board.flatten.include?(0)
		return false
	end

	game_over = true
	4.times do
		@board.each do|row|
			row.each_cons(2) do|nums|
				game_over = false if nums.first == nums.last
			end
		end

		rotate!
	end

	return game_over
end

The top part tests if the board includes a zero. If there's a zero on the board (an empty tile), then the game is not over, just return false. The second part ends up being a little tricky since the board rotate code is destructive. The board must be rotated four times to remain intact, so we can't just return false as soon as we find two tiles of the same value adjacent to each other.

So we start with game_over being equal to true. Each time two adjacent tiles with the same value are found, it's set to false (in a typical board, it will be set to false multiple times). Each iteration, it just iterates over the rows, then iterates over each row using each_cons(2) (each consecutive two values). This is a useful method that, for the array [1,2,3,4] will give the values [1,2], [2,3], [3,4] to the block passed to it. So if any of these little sub-arrays has the same value in both places, then set the game_over flag to false. Then rotate the board. This is guaranteed to happen 4 times to keep the board intact.

That's it!  This is the end of this series of articles.  You can either return to the first article or go back to the listing of all articles.

  1. About.com
  2. Technology
  3. Ruby
  4. Tutorials
  5. Writing a 2048 Clone in Ruby
  6. The Final Few Methods

©2014 About.com. All rights reserved.