# 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方式のカーソル移動です。