How to resolve the algorithm 2048 step by step in the Ruby programming language

Published on 12 May 2024 09:40 PM

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