Let's write β

プログラミング中にできたことか、思ったこととか

2048ゲームをRubyで

2048というゲームがなかなかプレイするのにも、AIを作るのにも面白そうだったので、
自分でいじれるように書いて見ました。

require "curses"

def make_board()
  board = Array.new(4).map!{ Array.new( 4, nil ) }
end

def available_pos(board)
  pos = []
  board.each_with_index do |row, y|
    board.each_with_index do |cell, x|
      if board[y][x] == nil
        pos << [x, y]
      end
    end
  end
  return pos
end

def init_board(board)
  pos = available_pos(board)
  init_pos = pos.sample(2)
  init_pos.each do |p|
    x, y = p
    board[y][x] = 2
  end
  return board
end

def insert_random(board)
  pos = available_pos(board)
  insert_pos = pos.sample
  x, y = insert_pos
  board[y][x] = (rand(2) + 1) * 2
  return board
end

def show_board(board)
  board.each do |row|
    puts "+------+------+------+------+"
    print "|"
    row.each do |cell| c = cell.nil? ? " " : cell
      print " #{c.to_s.center(4)} |"
    end
    puts ""
  end
  puts "+------+------+------+------+"
end

def sum_list(row)
  if (row.size == 0) 
    return [[],0]
  elsif(row.size < 2)
    return [row,0]
  else
    if not (row[0].nil? or row[1].nil?) and (row[0] == row[1])
      rest_row, rest_score = sum_list(row[2..-1])
      return [[row[0] * 2] + rest_row + [nil], row[0] + rest_score]
    else
      rest_row, rest_score = sum_list(row[1..-1])
      return [[row[0]] + rest_row, rest_score]
    end
  end
end

def shift_row(row, dir)
  #Collect values
  values = row.select {|i| not i.nil? }
  if (dir == :left) 
    sumed_values, score = sum_list(values)
    return [(sumed_values + [nil] * (4 - values.size)), score]
  else
    tmp_values, score = sum_list(values.reverse)
    return [([nil] * (4 - values.size) + tmp_values.reverse), score]
  end
end

def shift_board(board, dir)
  new_board = Array.new(4).map!{Array.new(4, nil)}
  score = 0
  board.each_with_index do |row, y|
    new_board_val, s = shift_row(row, dir)
    new_board[y] = new_board_val
    score += s
  end
  return [new_board, score]
end

def turn_left(board)
  new_board = Array.new(4).map!{Array.new(4, nil)}
  board.each_with_index do |row, y|
    row.each_with_index do |cell, x|
      new_board[3 - x][y] = cell
    end
  end
  return new_board
end

def turn_right(board)
  new_board = Array.new(4).map!{Array.new(4, nil)}
  board.each_with_index do |row, y|
    row.each_with_index do |cell, x|
      new_board[x][3 - y] = cell
    end
  end
  return new_board
end

def update_board(board, dir)
  case dir
  when :left
    updated_board,score = shift_board(board, dir)
  when :right
    updated_board,score =  shift_board(board, dir)
  when :up
    tmp_board,score = shift_board(turn_left(board), :left)
    updated_board =  turn_right(tmp_board)
  when :down
    tmp_board, score = shift_board(turn_left(board), :right)
    updated_board =  turn_right(tmp_board)
  end
  return [insert_random(updated_board),score]
end

class Game
  attr_accessor :board, :score
  def initialize()
    @board = init_board(make_board())
    @score = 0
  end

  def update(dir)
    @board, s = update_board(@board, dir)
    @score += s
  end

  def show_board_curses()
    Curses.setpos(0, 0)
    Curses.addstr("Score: #{@score}")
    @board.each_with_index do |row, y|
      Curses.setpos(y * 2 + 1, 0)
      Curses.addstr("+------+------+------+------+")
      line_str = "|"
      row.each_with_index do |cell| c = cell.nil? ? " " : cell
      line_str += " #{c.to_s.center(4)} |"
      end
      Curses.setpos(y * 2 + 2, 0)
      Curses.addstr(line_str)
    end
    Curses.setpos(9, 0)
    Curses.addstr("+------+------+------+------+")
  end

end


def main()
  #Initialize screen
  Curses.init_screen
  #CreateBoard
  game = Game.new()
  #Running 2048
  begin
    while true
      Curses.clear
      game.show_board_curses()
      Curses.refresh
      case Curses.getch
      when ?h
        game.update(:left)
      when ?l
        game.update(:right)
      when ?k
        game.update(:up)
      when ?j
        game.update(:down)
      else
        break
      end
    end
  ensure
    Curses.close_screen
  end
end

if __FILE__ == $0
  main()
end

そうさはvim方式のカーソル移動です。