How to resolve the algorithm 2048 step by step in the Ruby programming language
How to resolve the algorithm 2048 step by step in the Ruby programming language
Table of Contents
Problem Statement
Implement a 2D sliding block puzzle game where blocks with numbers are combined to add their values.
The name comes from the popular open-source implementation of this game mechanic, 2048.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm 2048 step by step in the Ruby programming language
The following code is a Ruby implementation of the 2048 game.
The Board class is the main class of the game, it contains the board, the score, the size of the board and the winning threshold.
The constructor of the Board class initializes the board to a size of 4x4, a winning threshold of 2048 and a cell width of 6.
It also initializes the board with 0s, sets the @moved flag to true, sets the score to 0 and sets the @no_more_moves flag to false.
The draw method of the Board class draws the board to the console.
It uses the @r_vert and @r_hori flags to determine whether to draw the board vertically or horizontally.
The move method of the Board class moves the board in the given direction.
It uses the column_map and row_map methods to iterate over the columns and rows of the board and apply the logic method to each column or row.
The logic method takes a list of numbers and merges any adjacent numbers that are the same.
It also increments the score by the value of the merged numbers.
The spawn method of the Board class spawns a new number on the board.
It does this by selecting a random empty position on the board and setting that position to a random number of 2 or 4.
The print_score method of the Board class prints the score to the console.
It also prints a congratulations message if the player has won the game.
The no_more_moves? method of the Board class returns true if there are no more moves possible on the board.
The won? method of the Board class returns true if there is a number on the board that is greater than or equal to the winning threshold.
The reset! method of the Board class resets the board to its initial state.
The set method of the Board class sets the value of a cell on the board.
The format method of the Board class formats a number to be displayed on the board.
It uses the $color and $colors variables to color the number.
The column_map method of the Board class transposes the board and applies the given block to each column.
The row_map method of the Board class applies the given block to each row of the board.
The to_enum method of the Board class returns an enumerator that iterates over the cells of the board.
The $color variable is set to true if the player wants to use colors on the board.
The $colors variable contains the colors that will be used to color the numbers on the board.
The $rumble variable is set to true if the player wants to use the rumble effect on the board.
The $check_score variable is set to true if the player wants to check for the winning score after each move.
The main loop of the game asks the player for input and then moves the board in the given direction.
If the player enters the letter q or h, the game will quit or display the help message, respectively.
The input variable is set to the input that the player entered.
The if statement checks if the player entered the escape character.
If the player entered the escape character, the input variable is set to the next two characters that the player entered.
The case statement checks which character the player entered.
If the player entered the up arrow key, the w key, or the a key, the board is moved up.
If the player entered the down arrow key, the s key, or the d key, the board is moved down.
If the player entered the right arrow key, the d key, or the f key, the board is moved right.
If the player entered the left arrow key, the w key, or the s key, the board is moved left.
If the player entered the q character, the u character, or the ctrl-c character, the game is exited.
If the player entered the h character, the help message is displayed.
The puts statement prints the board to the console and the b.draw statement draws the board to the console.
The if statement checks if there are no more moves possible on the board or if the player has won the game.
If either of these conditions is true, the b.print_score method is called and the game is exited if there are no more moves possible.
If the player has won the game, the player is asked if they want to continue playing.
If the player enters the n character, the game is exited.
If the player enters the y character, the $check_score variable is set to false and the game continues.
Source code in the ruby programming language
#!/usr/bin/ruby
require 'io/console'
class Board
def initialize size=4, win_limit=2048, cell_width = 6
@size = size; @cw = cell_width; @win_limit = win_limit
@board = Array.new(size) {Array.new(size, 0)}
@moved = true; @score = 0; @no_more_moves = false
spawn
end
def draw
print "\n\n" if @r_vert
print ' ' if @r_hori
print '┌' + (['─' * @cw] * @size).join('┬') + '┐'
@board.each do |row|
print "\n"
formated = row.map {|num| num == 0 ? ' ' * @cw : format(num)}
print ' ' if @r_hori
puts '│' + formated.join('│') + '│'
print ' ' if @r_hori
print '├' + ([' ' * @cw] * @size).join('┼') + '┤'
end
print "\r"
print ' ' if @r_hori
puts '└' + (['─' * @cw] * @size).join('┴') + '┘'
end
def move direction
case direction
when :up
@board = column_map {|c| logic(c)}
@r_vert = false if $rumble
when :down
@board = column_map {|c| logic(c.reverse).reverse}
@r_vert = true if $rumble
when :left
@board = row_map {|r| logic(r)}
@r_hori = false if $rumble
when :right
@board = row_map {|r| logic(r.reverse).reverse}
@r_hori = true if $rumble
end
spawn
@moved = false
end
def print_score
puts "Your Score is #@score."
puts "Congratulations, you have won!" if to_enum.any? {|e| e >= @win_limit}
end
def no_more_moves?; @no_more_moves; end
def won?; to_enum.any? {|e| e >= @win_limit}; end
def reset!; initialize @size, @win_limit, @cw; end
private
def set x, y, val
@board[y][x] = val
end
def spawn
free_pos = to_enum.select{|elem,x,y| elem == 0}.map{|_,x,y| [x,y]}
unless free_pos.empty?
set *free_pos.sample, rand > 0.1 ? 2 : 4 if @moved
else
snap = @board
unless @stop
@stop = true
%i{up down left right}.each{|s| move(s)}
@no_more_moves = true if snap.flatten == @board.flatten
@board = snap
@stop = false
end
end
end
def logic list
jump = false
result =
list.reduce([]) do |res, val|
if res.last == val && !jump
res[-1] += val
@score += val
jump = true
elsif val != 0
res.push val
jump = false
end
res
end
result += [0] * (@size - result.length)
@moved ||= list != result
result
end
def column_map
xboard = @board.transpose
xboard.map!{|c| yield c }
xboard.transpose
end
def row_map
@board.map {|r| yield r }
end
def to_enum
@enum ||= Enumerator.new(@size * @size) do |yielder|
(@size*@size).times do |i|
yielder.yield (@board[i / @size][i % @size]), (i % @size), (i / @size )
end
end
@enum.rewind
end
def format(num)
if $color
cstart = "\e[" + $colors[Math.log(num, 2)] + "m"
cend = "\e[0m"
else
cstart = cend = ""
end
cstart + num.to_s.center(@cw) + cend
end
end
$color = true
$colors = %W{0 1;97 1;93 1;92 1;96 1;91 1;95 1;94 1;30;47 1;43 1;42
1;46 1;41 1;45 1;44 1;33;43 1;33;42 1;33;41 1;33;44}
$rumble = false
$check_score = true
unless ARGV.empty?
puts "Usage: #$0 [gridsize] [score-threshold] [padwidth] [--no-color] [--rumble]"; exit if %W[-h --help].include?(ARGV[0])
args = ARGV.map(&:to_i).reject{|n| n == 0}
b = Board.new(*args) unless args.empty?
$rumble = true if ARGV.any?{|a| a =~ /rumble/i }
$color = false if ARGV.any?{|a| a =~ /no.?color/i}
end
b ||= Board.new
puts "\e[H\e[2J"
b.draw
puts "Press h for help, q to quit"
loop do
input = STDIN.getch
if input == "\e"
2.times {input << STDIN.getch}
end
case input
when "\e[A", "w" then b.move(:up)
when "\e[B", "s" then b.move(:down)
when "\e[C", "d" then b.move(:right)
when "\e[D", "a" then b.move(:left)
when "q","\u0003","\u0004" then b.print_score; exit
when "h"
puts <<-EOM.gsub(/^\s*/, '')
┌─ ─┐
│Use the arrow-keys or WASD on your keyboard to push board in the given direction.
│Tiles with the same number merge into one.
│Get a tile with a value of #{ARGV[1] || 2048} to win.
│In case you cannot move or merge any tiles anymore, you loose.
│You can start this game with different settings by providing commandline argument:
│For instance:
│ %> #$0 6 8192 --rumble
└─ ─┘
PRESS q TO QUIT (or Ctrl-C or Ctrl-D)
EOM
input = STDIN.getch
end
puts "\e[H\e[2J"
b.draw
if b.no_more_moves? or $check_score && b.won?
b.print_score
if b.no_more_moves?
puts "No more moves possible"
puts "Again? (y/n)"
exit if STDIN.gets.chomp.downcase == "n"
$check_score = true
b.reset!
puts "\e[H\e[2J"
b.draw
else
puts "Continue? (y/n)"
exit if STDIN.gets.chomp.downcase == "n"
$check_score = false
puts "\e[H\e[2J"
b.draw
end
end
end
You may also check:How to resolve the algorithm Ascending primes step by step in the Ruby programming language
You may also check:How to resolve the algorithm Bulls and cows step by step in the Ruby programming language
You may also check:How to resolve the algorithm Roots of unity step by step in the Ruby programming language
You may also check:How to resolve the algorithm Dinesman's multiple-dwelling problem step by step in the Ruby programming language
You may also check:How to resolve the algorithm Population count step by step in the Ruby programming language