Skip to content

Instantly share code, notes, and snippets.

@wkjagt
Last active August 29, 2015 14:19
Show Gist options
  • Save wkjagt/36faf0be354a64ccee03 to your computer and use it in GitHub Desktop.
Save wkjagt/36faf0be354a64ccee03 to your computer and use it in GitHub Desktop.

Revisions

  1. wkjagt revised this gist Apr 22, 2015. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion 2048.rb
    Original file line number Diff line number Diff line change
    @@ -98,7 +98,9 @@ def cols
    end

    Game = Struct.new(:board) do
    KEYS = { "w" => [0,-1], "a" => [-1,0], "s" => [0,+1], "d" => [+1,0], "\u0003" => :exit }
    KEYS = { "w" => [0,-1], "a" => [-1,0], "s" => [0,+1], "d" => [+1,0],
    "A" => [0,-1], "D" => [-1,0], "B" => [0,+1], "C" => [+1,0],
    "\u0003" => :exit }

    def run
    board.draw
  2. wkjagt revised this gist Apr 22, 2015. 1 changed file with 8 additions and 6 deletions.
    14 changes: 8 additions & 6 deletions 2048.rb
    Original file line number Diff line number Diff line change
    @@ -22,11 +22,7 @@ def draw
    system "clear" or system "cls"

    puts HOR_LINE
    rows.each do |r|
    print EMPTY_COL + "\n|"
    r.each { |(s, v)| print format_nr_square(v) + "|" }
    puts "\n" + EMPTY_COL + "\n" + HOR_LINE
    end
    rows.each { |row| draw_row(row) }
    puts "\nMoves: #{@history.size - 1}\nScore: #{@score}"
    end

    @@ -43,11 +39,17 @@ def move(dir)

    private

    def draw_row(row)
    print "#{EMPTY_COL}\n|"
    print row.map { |(_, val)| format_square(val) + "|" }.join
    print "\n#{EMPTY_COL}\n#{HOR_LINE}\n"
    end

    def add_new
    @squares[random_empty] = [2, 4].sample(1).first
    end

    def format_nr_square(value)
    def format_square(value)
    return " " * 6 unless value

    log = Math.log(value, 2).to_i
  3. wkjagt revised this gist Apr 22, 2015. 1 changed file with 98 additions and 98 deletions.
    196 changes: 98 additions & 98 deletions 2048.rb
    Original file line number Diff line number Diff line change
    @@ -1,119 +1,119 @@
    require 'io/console'

    class Board
    X, Y = 0, 1
    COLORS = [:red, :green, :brown, :magenta, :cyan]
    HOR_LINE = "-" * 29
    EMPTY_COL = "| " * 4 + "|"
    X, Y = 0, 1
    COLORS = [:red, :green, :brown, :magenta, :cyan]
    HOR_LINE = "-" * 29
    EMPTY_COL = "| " * 4 + "|"

    def initialize
    def initialize
    @score = 0
    @squares = (0..15).map { |n| [[n % 4, n / 4], nil] }.to_h
    @move_order = {
    [0,-1] => -> { rows }, [0,+1] => -> { rows.reverse },
    [-1,0] => -> { cols }, [+1,0] => -> { cols.reverse },
    }

    2.times { add_new }
    @history = [@squares.dup]
    end

    def draw
    system "clear" or system "cls"

    puts HOR_LINE
    rows.each do |r|
    print EMPTY_COL + "\n|"
    r.each { |(s, v)| print format_nr_square(v) + "|" }
    puts "\n" + EMPTY_COL + "\n" + HOR_LINE
    end
    puts "\nMoves: #{@history.size - 1}\nScore: #{@score}"
    end

    def move(dir)
    @doubled = []
    move_lines(@move_order[dir].call, dir)

    unless @history.last == @squares
    @squares = (0..15).map { |n| [[n % 4, n / 4], nil] }.to_h
    @move_order = {
    [0,-1] => -> { rows }, [0,+1] => -> { rows.reverse },
    [-1,0] => -> { cols }, [+1,0] => -> { cols.reverse },
    }

    2.times { add_new }
    @history = [@squares.dup]
    end

    def draw
    system "clear" or system "cls"

    puts HOR_LINE
    rows.each do |r|
    print EMPTY_COL + "\n|"
    r.each { |(s, v)| print format_nr_square(v) + "|" }
    puts "\n" + EMPTY_COL + "\n" + HOR_LINE
    end
    puts "\nMoves: #{@history.size - 1}\nScore: #{@score}"
    end

    def move(dir)
    @doubled = []
    move_lines(@move_order[dir].call, dir)

    unless @history.last == @squares
    add_new
    @history << @squares.dup
    end
    self
    end

    private

    def add_new
    @squares[random_empty] = [2, 4].sample(1).first
    end

    def format_nr_square(value)
    return " " * 6 unless value

    log = Math.log(value, 2).to_i
    f = value.to_s.center(6).send(COLORS[-1 + log % COLORS.length])
    log <= COLORS.length ? f : f.bold
    end

    def move_lines(lines, dir)
    lines.each { |line| move_line(line, dir) }
    end

    def move_line(line, dir)
    line.select { |k, v| v }.each_key { |location| slide_square(location, dir) }
    end

    def slide_square(loc, dir)
    value = @squares[loc]
    neighbour = [loc[X] + dir[X],loc[Y] + dir[Y]]
    return unless @squares.key? neighbour

    if @squares[neighbour].nil?
    @squares[loc], @squares[neighbour] = nil, value
    slide_square(neighbour, dir)
    elsif @squares[neighbour] == value && !@doubled.include?(neighbour)
    @squares[loc], @squares[neighbour] = nil, value * 2
    @doubled << neighbour
    end
    self
    end

    private

    def add_new
    @squares[random_empty] = [2, 4].sample(1).first
    end

    def format_nr_square(value)
    return " " * 6 unless value

    log = Math.log(value, 2).to_i
    f = value.to_s.center(6).send(COLORS[-1 + log % COLORS.length])
    log <= COLORS.length ? f : f.bold
    end

    def move_lines(lines, dir)
    lines.each { |line| move_line(line, dir) }
    end

    def move_line(line, dir)
    line.select { |k, v| v }.each_key { |location| slide_square(location, dir) }
    end

    def slide_square(loc, dir)
    value = @squares[loc]
    neighbour = [loc[X] + dir[X],loc[Y] + dir[Y]]
    return unless @squares.key? neighbour

    if @squares[neighbour].nil?
    @squares[loc], @squares[neighbour] = nil, value
    slide_square(neighbour, dir)
    elsif @squares[neighbour] == value && !@doubled.include?(neighbour)
    @squares[loc], @squares[neighbour] = nil, value * 2
    @doubled << neighbour
    @score += value * 2
    end
    end
    end
    end

    def random_empty
    @squares.select{ |_, v| v.nil? }.to_a.sample(1).to_h.keys.first
    end
    def random_empty
    @squares.select{ |_, v| v.nil? }.to_a.sample(1).to_h.keys.first
    end

    def lines(dir)
    (0..3).map{ |i| @squares.select { |s| s[dir] == i } }
    end
    def lines(dir)
    (0..3).map{ |i| @squares.select { |s| s[dir] == i } }
    end

    def rows
    lines(Y)
    end
    def rows
    lines(Y)
    end

    def cols
    lines(X)
    end
    def cols
    lines(X)
    end
    end

    Game = Struct.new(:board) do
    KEYS = { "w" => [0,-1], "a" => [-1,0], "s" => [0,+1], "d" => [+1,0], "\u0003" => :exit }

    def run
    board.draw
    while key = STDIN.getch
    next unless KEYS.key?(key)
    KEYS[key] == :exit ? exit : board.move(KEYS[key]).draw
    end
    end
    KEYS = { "w" => [0,-1], "a" => [-1,0], "s" => [0,+1], "d" => [+1,0], "\u0003" => :exit }

    def run
    board.draw
    while key = STDIN.getch
    next unless KEYS.key?(key)
    KEYS[key] == :exit ? exit : board.move(KEYS[key]).draw
    end
    end
    end

    class String
    def red; "\033[31m#{self}\033[0m" end
    def green; "\033[32m#{self}\033[0m" end
    def brown; "\033[33m#{self}\033[0m" end
    def magenta; "\033[35m#{self}\033[0m" end
    def cyan; "\033[36m#{self}\033[0m" end
    def bold; "\033[1m#{self}\033[22m" end
    def red; "\033[31m#{self}\033[0m" end
    def green; "\033[32m#{self}\033[0m" end
    def brown; "\033[33m#{self}\033[0m" end
    def magenta; "\033[35m#{self}\033[0m" end
    def cyan; "\033[36m#{self}\033[0m" end
    def bold; "\033[1m#{self}\033[22m" end
    end

    Game.new(Board.new).run
  4. wkjagt revised this gist Apr 22, 2015. 1 changed file with 107 additions and 122 deletions.
    229 changes: 107 additions & 122 deletions 2048.rb
    Original file line number Diff line number Diff line change
    @@ -1,134 +1,119 @@
    #!/usr/bin/ruby

    require "awesome_print"
    require 'io/console'
    require "pry"

    class String
    def red; "\033[31m#{self}\033[0m" end
    def green; "\033[32m#{self}\033[0m" end
    def brown; "\033[33m#{self}\033[0m" end
    def magenta; "\033[35m#{self}\033[0m" end
    def cyan; "\033[36m#{self}\033[0m" end
    def bold; "\033[1m#{self}\033[22m" end
    end

    class Board
    COLORS = [:red, :green, :brown, :magenta, :cyan]
    HOR_LINE = "-" * 29
    EMPTY_COL = "| " * 4 + "|"
    X, Y = 0, 1
    COLORS = [:red, :green, :brown, :magenta, :cyan]
    HOR_LINE = "-" * 29
    EMPTY_COL = "| " * 4 + "|"

    def initialize
    def initialize
    @score = 0
    @doubled = []
    @squares = (0..15).map { |n| [{x: n % 4, y: n / 4}, nil] }.to_h
    @move_order = {
    {y:-1} => -> { rows }, {y:+1} => -> { rows.reverse },
    {x:-1} => -> { cols }, {x:+1} => -> { cols.reverse },
    }

    2.times { add_new }
    @history = [@squares.dup]
    end

    def add_new
    @squares[random_empty] = [2, 4].sample(1).first
    end

    def draw
    system "clear" or system "cls"

    puts HOR_LINE
    rows.each do |r|
    print EMPTY_COL + "\n|"
    r.each { |(s, v)| print format_nr_square(v) + "|" }
    puts "\n" + EMPTY_COL + "\n" + HOR_LINE
    end
    puts "\nMoves: #{@history.size - 1}\nScore: #{@score}"
    end

    def move(dir)
    @doubled = []
    move_lines(@move_order[dir].call, dir)

    unless @history.last == @squares
    add_new
    @squares = (0..15).map { |n| [[n % 4, n / 4], nil] }.to_h
    @move_order = {
    [0,-1] => -> { rows }, [0,+1] => -> { rows.reverse },
    [-1,0] => -> { cols }, [+1,0] => -> { cols.reverse },
    }

    2.times { add_new }
    @history = [@squares.dup]
    end

    def draw
    system "clear" or system "cls"

    puts HOR_LINE
    rows.each do |r|
    print EMPTY_COL + "\n|"
    r.each { |(s, v)| print format_nr_square(v) + "|" }
    puts "\n" + EMPTY_COL + "\n" + HOR_LINE
    end
    puts "\nMoves: #{@history.size - 1}\nScore: #{@score}"
    end

    def move(dir)
    @doubled = []
    move_lines(@move_order[dir].call, dir)

    unless @history.last == @squares
    add_new
    @history << @squares.dup
    end
    end

    private

    def format_nr_square(value)
    return " " * 6 unless value
    index = -1 + Math.log(value, 2).to_i % COLORS.length
    value.to_s.center(6).send(COLORS[index]).bold
    end

    def move_lines(lines, dir)
    lines.each { |line| move_line(line, dir) }
    end

    def move_line(line, dir)
    line.select { |k, v| v }.each_key { |location| slide_square(location, dir) }
    end

    def slide_square(location, dir)
    value = @squares[location]
    neighbour = {
    x: location[:x] + dir.fetch(:x, 0),
    y: location[:y] + dir.fetch(:y, 0)
    }

    return unless @squares.key? neighbour

    if @squares[neighbour].nil?
    @squares[location], @squares[neighbour] = nil, value
    slide_square(neighbour, dir)
    elsif @squares[neighbour] == value && !@doubled.include?(neighbour)
    @squares[location], @squares[neighbour] = nil, value * 2
    @doubled << neighbour
    end
    self
    end

    private

    def add_new
    @squares[random_empty] = [2, 4].sample(1).first
    end

    def format_nr_square(value)
    return " " * 6 unless value

    log = Math.log(value, 2).to_i
    f = value.to_s.center(6).send(COLORS[-1 + log % COLORS.length])
    log <= COLORS.length ? f : f.bold
    end

    def move_lines(lines, dir)
    lines.each { |line| move_line(line, dir) }
    end

    def move_line(line, dir)
    line.select { |k, v| v }.each_key { |location| slide_square(location, dir) }
    end

    def slide_square(loc, dir)
    value = @squares[loc]
    neighbour = [loc[X] + dir[X],loc[Y] + dir[Y]]
    return unless @squares.key? neighbour

    if @squares[neighbour].nil?
    @squares[loc], @squares[neighbour] = nil, value
    slide_square(neighbour, dir)
    elsif @squares[neighbour] == value && !@doubled.include?(neighbour)
    @squares[loc], @squares[neighbour] = nil, value * 2
    @doubled << neighbour
    @score += value * 2
    end
    end
    def random_empty
    @squares.select { |_, v| v.nil? }.to_a.sample(1).to_h.keys.first
    end
    def line(index, dir)
    @squares.select { |a| a[dir] == index }
    end
    def rows
    (0..3).map { |i| line(i, :y) }
    end
    def cols
    (0..3).map { |i| line(i, :x) }
    end
    end
    end

    def random_empty
    @squares.select{ |_, v| v.nil? }.to_a.sample(1).to_h.keys.first
    end

    def lines(dir)
    (0..3).map{ |i| @squares.select { |s| s[dir] == i } }
    end

    def rows
    lines(Y)
    end

    def cols
    lines(X)
    end
    end

    class Game
    KEYS = { "w" => {y:-1}, "a" => {x:-1}, "s" => {y:+1}, "d" => {x:+1}, "\u0003" => :exit }

    def initialize
    @board = Board.new
    @board.draw
    end

    def run
    while key = STDIN.getch
    next unless KEYS.key?(key)
    handle_key(KEYS[key])
    @board.draw
    end
    end

    def handle_key(action)
    action == :exit ? exit : @board.move(action)
    end
    Game = Struct.new(:board) do
    KEYS = { "w" => [0,-1], "a" => [-1,0], "s" => [0,+1], "d" => [+1,0], "\u0003" => :exit }

    def run
    board.draw
    while key = STDIN.getch
    next unless KEYS.key?(key)
    KEYS[key] == :exit ? exit : board.move(KEYS[key]).draw
    end
    end
    end

    Game.new.run
    class String
    def red; "\033[31m#{self}\033[0m" end
    def green; "\033[32m#{self}\033[0m" end
    def brown; "\033[33m#{self}\033[0m" end
    def magenta; "\033[35m#{self}\033[0m" end
    def cyan; "\033[36m#{self}\033[0m" end
    def bold; "\033[1m#{self}\033[22m" end
    end

    Game.new(Board.new).run
  5. wkjagt revised this gist Apr 21, 2015. 1 changed file with 3 additions and 5 deletions.
    8 changes: 3 additions & 5 deletions 2048.rb
    Original file line number Diff line number Diff line change
    @@ -37,7 +37,7 @@ def add_new

    def draw
    system "clear" or system "cls"

    puts HOR_LINE
    rows.each do |r|
    print EMPTY_COL + "\n|"
    @@ -50,10 +50,10 @@ def draw
    def move(dir)
    @doubled = []
    move_lines(@move_order[dir].call, dir)

    unless @history.last == @squares
    @history << @squares.dup
    add_new
    @history << @squares.dup
    end
    end

    @@ -71,8 +71,6 @@ def move_lines(lines, dir)

    def move_line(line, dir)
    line.select { |k, v| v }.each_key { |location| slide_square(location, dir) }
    sleep 0.1
    draw
    end

    def slide_square(location, dir)
  6. wkjagt revised this gist Apr 21, 2015. 1 changed file with 109 additions and 109 deletions.
    218 changes: 109 additions & 109 deletions 2048.rb
    Original file line number Diff line number Diff line change
    @@ -5,131 +5,131 @@
    require "pry"

    class String
    def red; "\033[31m#{self}\033[0m" end
    def green; "\033[32m#{self}\033[0m" end
    def brown; "\033[33m#{self}\033[0m" end
    def magenta; "\033[35m#{self}\033[0m" end
    def cyan; "\033[36m#{self}\033[0m" end
    def bold; "\033[1m#{self}\033[22m" end
    def red; "\033[31m#{self}\033[0m" end
    def green; "\033[32m#{self}\033[0m" end
    def brown; "\033[33m#{self}\033[0m" end
    def magenta; "\033[35m#{self}\033[0m" end
    def cyan; "\033[36m#{self}\033[0m" end
    def bold; "\033[1m#{self}\033[22m" end
    end

    class Board
    COLORS = [:red, :green, :brown, :magenta, :cyan]
    HOR_LINE = "-" * 29
    EMPTY_COL = "| " * 4 + "|"
    COLORS = [:red, :green, :brown, :magenta, :cyan]
    HOR_LINE = "-" * 29
    EMPTY_COL = "| " * 4 + "|"

    def initialize
    def initialize
    @score = 0
    @doubled = []
    @squares = (0..15).map { |n| [{x: n % 4, y: n / 4}, nil] }.to_h
    @move_order = {
    {y:-1} => -> { rows }, {y:+1} => -> { rows.reverse },
    {x:-1} => -> { cols }, {x:+1} => -> { cols.reverse },
    }
    @doubled = []
    @squares = (0..15).map { |n| [{x: n % 4, y: n / 4}, nil] }.to_h
    @move_order = {
    {y:-1} => -> { rows }, {y:+1} => -> { rows.reverse },
    {x:-1} => -> { cols }, {x:+1} => -> { cols.reverse },
    }

    2.times { add_new }
    @history = [@squares.dup]
    end
    def add_new
    @squares[random_empty] = [2, 4].sample(1).first
    end
    def draw
    system "clear" or system "cls"
    2.times { add_new }
    @history = [@squares.dup]
    end
    def add_new
    @squares[random_empty] = [2, 4].sample(1).first
    end
    def draw
    system "clear" or system "cls"

    puts HOR_LINE
    rows.each do |r|
    print EMPTY_COL + "\n|"
    r.each { |(s, v)| print format_nr_square(v) + "|" }
    puts "\n" + EMPTY_COL + "\n" + HOR_LINE
    end
    puts "\nMoves: #{@history.size - 1}\nScore: #{@score}"
    end
    def move(dir)
    @doubled = []
    move_lines(@move_order[dir].call, dir)
    unless @history.last == @squares
    @history << @squares.dup
    add_new
    end
    end
    private
    def format_nr_square(value)
    return " " * 6 unless value
    index = -1 + Math.log(value, 2).to_i % COLORS.length
    value.to_s.center(6).send(COLORS[index]).bold
    end
    def move_lines(lines, dir)
    lines.each { |line| move_line(line, dir) }
    end
    def move_line(line, dir)
    line.select { |k, v| v }.each_key { |location| slide_square(location, dir) }
    puts HOR_LINE
    rows.each do |r|
    print EMPTY_COL + "\n|"
    r.each { |(s, v)| print format_nr_square(v) + "|" }
    puts "\n" + EMPTY_COL + "\n" + HOR_LINE
    end
    puts "\nMoves: #{@history.size - 1}\nScore: #{@score}"
    end
    def move(dir)
    @doubled = []
    move_lines(@move_order[dir].call, dir)
    unless @history.last == @squares
    @history << @squares.dup
    add_new
    end
    end
    private
    def format_nr_square(value)
    return " " * 6 unless value
    index = -1 + Math.log(value, 2).to_i % COLORS.length
    value.to_s.center(6).send(COLORS[index]).bold
    end
    def move_lines(lines, dir)
    lines.each { |line| move_line(line, dir) }
    end
    def move_line(line, dir)
    line.select { |k, v| v }.each_key { |location| slide_square(location, dir) }
    sleep 0.1
    draw
    end
    def slide_square(location, dir)
    value = @squares[location]
    neighbour = {
    x: location[:x] + dir.fetch(:x, 0),
    y: location[:y] + dir.fetch(:y, 0)
    }
    end
    def slide_square(location, dir)
    value = @squares[location]
    neighbour = {
    x: location[:x] + dir.fetch(:x, 0),
    y: location[:y] + dir.fetch(:y, 0)
    }

    return unless @squares.key? neighbour
    return unless @squares.key? neighbour

    if @squares[neighbour].nil?
    @squares[location], @squares[neighbour] = nil, value
    slide_square(neighbour, dir)
    elsif @squares[neighbour] == value && !@doubled.include?(neighbour)
    @squares[location], @squares[neighbour] = nil, value * 2
    @doubled << neighbour
    if @squares[neighbour].nil?
    @squares[location], @squares[neighbour] = nil, value
    slide_square(neighbour, dir)
    elsif @squares[neighbour] == value && !@doubled.include?(neighbour)
    @squares[location], @squares[neighbour] = nil, value * 2
    @doubled << neighbour
    @score += value * 2
    end
    end
    def random_empty
    @squares.select { |_, v| v.nil? }.to_a.sample(1).to_h.keys.first
    end
    def line(index, dir)
    @squares.select { |a| a[dir] == index }
    end
    def rows
    (0..3).map { |i| line(i, :y) }
    end
    def cols
    (0..3).map { |i| line(i, :x) }
    end
    end
    end
    def random_empty
    @squares.select { |_, v| v.nil? }.to_a.sample(1).to_h.keys.first
    end
    def line(index, dir)
    @squares.select { |a| a[dir] == index }
    end
    def rows
    (0..3).map { |i| line(i, :y) }
    end
    def cols
    (0..3).map { |i| line(i, :x) }
    end
    end

    class Game
    KEYS = { "w" => {y:-1}, "a" => {x:-1}, "s" => {y:+1}, "d" => {x:+1}, "\u0003" => :exit }
    KEYS = { "w" => {y:-1}, "a" => {x:-1}, "s" => {y:+1}, "d" => {x:+1}, "\u0003" => :exit }

    def initialize
    @board = Board.new
    @board.draw
    end
    def run
    while key = STDIN.getch
    next unless KEYS.key?(key)
    handle_key(KEYS[key])
    @board.draw
    end
    end
    def handle_key(action)
    action == :exit ? exit : @board.move(action)
    end
    def initialize
    @board = Board.new
    @board.draw
    end
    def run
    while key = STDIN.getch
    next unless KEYS.key?(key)
    handle_key(KEYS[key])
    @board.draw
    end
    end
    def handle_key(action)
    action == :exit ? exit : @board.move(action)
    end
    end

    Game.new.run
  7. wkjagt created this gist Apr 21, 2015.
    136 changes: 136 additions & 0 deletions 2048.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,136 @@
    #!/usr/bin/ruby

    require "awesome_print"
    require 'io/console'
    require "pry"

    class String
    def red; "\033[31m#{self}\033[0m" end
    def green; "\033[32m#{self}\033[0m" end
    def brown; "\033[33m#{self}\033[0m" end
    def magenta; "\033[35m#{self}\033[0m" end
    def cyan; "\033[36m#{self}\033[0m" end
    def bold; "\033[1m#{self}\033[22m" end
    end

    class Board
    COLORS = [:red, :green, :brown, :magenta, :cyan]
    HOR_LINE = "-" * 29
    EMPTY_COL = "| " * 4 + "|"

    def initialize
    @score = 0
    @doubled = []
    @squares = (0..15).map { |n| [{x: n % 4, y: n / 4}, nil] }.to_h
    @move_order = {
    {y:-1} => -> { rows }, {y:+1} => -> { rows.reverse },
    {x:-1} => -> { cols }, {x:+1} => -> { cols.reverse },
    }

    2.times { add_new }
    @history = [@squares.dup]
    end

    def add_new
    @squares[random_empty] = [2, 4].sample(1).first
    end

    def draw
    system "clear" or system "cls"

    puts HOR_LINE
    rows.each do |r|
    print EMPTY_COL + "\n|"
    r.each { |(s, v)| print format_nr_square(v) + "|" }
    puts "\n" + EMPTY_COL + "\n" + HOR_LINE
    end
    puts "\nMoves: #{@history.size - 1}\nScore: #{@score}"
    end

    def move(dir)
    @doubled = []
    move_lines(@move_order[dir].call, dir)

    unless @history.last == @squares
    @history << @squares.dup
    add_new
    end
    end

    private

    def format_nr_square(value)
    return " " * 6 unless value
    index = -1 + Math.log(value, 2).to_i % COLORS.length
    value.to_s.center(6).send(COLORS[index]).bold
    end

    def move_lines(lines, dir)
    lines.each { |line| move_line(line, dir) }
    end

    def move_line(line, dir)
    line.select { |k, v| v }.each_key { |location| slide_square(location, dir) }
    sleep 0.1
    draw
    end

    def slide_square(location, dir)
    value = @squares[location]
    neighbour = {
    x: location[:x] + dir.fetch(:x, 0),
    y: location[:y] + dir.fetch(:y, 0)
    }

    return unless @squares.key? neighbour

    if @squares[neighbour].nil?
    @squares[location], @squares[neighbour] = nil, value
    slide_square(neighbour, dir)
    elsif @squares[neighbour] == value && !@doubled.include?(neighbour)
    @squares[location], @squares[neighbour] = nil, value * 2
    @doubled << neighbour
    @score += value * 2
    end
    end

    def random_empty
    @squares.select { |_, v| v.nil? }.to_a.sample(1).to_h.keys.first
    end

    def line(index, dir)
    @squares.select { |a| a[dir] == index }
    end

    def rows
    (0..3).map { |i| line(i, :y) }
    end

    def cols
    (0..3).map { |i| line(i, :x) }
    end
    end

    class Game
    KEYS = { "w" => {y:-1}, "a" => {x:-1}, "s" => {y:+1}, "d" => {x:+1}, "\u0003" => :exit }

    def initialize
    @board = Board.new
    @board.draw
    end

    def run
    while key = STDIN.getch
    next unless KEYS.key?(key)
    handle_key(KEYS[key])
    @board.draw
    end
    end

    def handle_key(action)
    action == :exit ? exit : @board.move(action)
    end
    end

    Game.new.run