2048-ruby 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/2048 +8 -0
- data/lib/game_2048/game.rb +66 -0
- data/lib/game_2048/render.rb +177 -0
- data/lib/game_2048/terminal.rb +169 -0
- data/lib/game_2048/tiles.rb +179 -0
- data/lib/game_2048.rb +6 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 363aba227984e0848f9b6a40a1eb04b5540c1c5572f061c6d2a61d0ba7a3829e
|
4
|
+
data.tar.gz: ca798754075c148bc7ff45cde0f6287c336a8a2dd7400399b7f323a0b9b9751a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8cd4332ea3a8ccdbc4636906c9648aee7419f3b1aa20a099355c601f7e98bc5765dbabc9898c93abe6d4a0931fa985397a3999609b45c3b50dca1c8f6dc8f783
|
7
|
+
data.tar.gz: 6fcfe4af73f38cade1f2831324a5dff732c43f9f945794cefafe1d1425dbbde3549cd1d1449f00d3735613411fa6d70a27c50cec584a3f60ee6bfe86abc58831
|
data/bin/2048
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Game2048
|
4
|
+
##
|
5
|
+
# Game
|
6
|
+
class Game
|
7
|
+
def initialize
|
8
|
+
@terminal = Terminal.new
|
9
|
+
@tiles = Tiles.new
|
10
|
+
@render = Render.new(@terminal, @tiles)
|
11
|
+
@running = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def stop
|
15
|
+
@running = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
@running = true
|
20
|
+
@terminal.raw_mode
|
21
|
+
@terminal.hide_cursor
|
22
|
+
@render.refresh
|
23
|
+
|
24
|
+
Kernel.trap('SIGWINCH') do
|
25
|
+
@render.refresh
|
26
|
+
end
|
27
|
+
|
28
|
+
while @running
|
29
|
+
@render.draw
|
30
|
+
key = @terminal.read
|
31
|
+
case key
|
32
|
+
when 'q'
|
33
|
+
stop
|
34
|
+
when 'r'
|
35
|
+
@tiles.reset
|
36
|
+
when '+'
|
37
|
+
@render.size += 1
|
38
|
+
@render.refresh
|
39
|
+
when '-'
|
40
|
+
@render.size -= 1
|
41
|
+
@render.refresh
|
42
|
+
end
|
43
|
+
|
44
|
+
next if @tiles.win? || @tiles.game_over?
|
45
|
+
|
46
|
+
case key
|
47
|
+
when :up
|
48
|
+
@tiles.move_up
|
49
|
+
when :down
|
50
|
+
@tiles.move_down
|
51
|
+
when :right
|
52
|
+
@tiles.move_right
|
53
|
+
when :left
|
54
|
+
@tiles.move_left
|
55
|
+
when 'u'
|
56
|
+
@tiles.undo
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@terminal.cooked_mode
|
61
|
+
@terminal.erase_display
|
62
|
+
@terminal.show_cursor
|
63
|
+
@terminal.move_home
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Game2048
|
4
|
+
##
|
5
|
+
# Render
|
6
|
+
class Render
|
7
|
+
SIZE = 3
|
8
|
+
HORIZONTAL = "\u2500"
|
9
|
+
VERTICAL = "\u2502"
|
10
|
+
LEFT_VERTICAL = "\u251c"
|
11
|
+
RIGHT_VERTICAL = "\u2524"
|
12
|
+
TOP_HORIZONTAL = "\u252c"
|
13
|
+
BOTTOM_HORIZONTAL = "\u2534"
|
14
|
+
TOP_LEFT = "\u250c"
|
15
|
+
TOP_RIGHT = "\u2510"
|
16
|
+
BOTTOM_LEFT = "\u2514"
|
17
|
+
BOTTOM_RIGHT = "\u2518"
|
18
|
+
HORIZONTAL_VERTICAL = "\u253c"
|
19
|
+
BORDER_COLOR = :yellow
|
20
|
+
TILE_COLOR = :cyan
|
21
|
+
STATUSBAR_COLOR = :blue
|
22
|
+
STATUSBAR_TEXT_COLOR = :white
|
23
|
+
GAMEOVER_COLOR = :red
|
24
|
+
WIN_COLOR = :green
|
25
|
+
BLANK = ' '
|
26
|
+
|
27
|
+
attr_reader :size
|
28
|
+
|
29
|
+
def initialize(terminal, tiles, **options)
|
30
|
+
@tiles = tiles
|
31
|
+
@terminal = terminal
|
32
|
+
@x = @y = 1
|
33
|
+
@rows = @cols = 0
|
34
|
+
@width = @height = 0
|
35
|
+
@size = options.fetch(:size, SIZE)
|
36
|
+
end
|
37
|
+
|
38
|
+
def size=(value)
|
39
|
+
n = Math.sqrt(Tiles::SIZE).to_i
|
40
|
+
width = value * 2 * n + n + 1
|
41
|
+
height = value * n + n + 1
|
42
|
+
return if value <= 1 || width > @cols || height > @rows
|
43
|
+
|
44
|
+
@size = value
|
45
|
+
end
|
46
|
+
|
47
|
+
def refresh
|
48
|
+
@terminal.erase_display
|
49
|
+
align
|
50
|
+
draw
|
51
|
+
end
|
52
|
+
|
53
|
+
def draw
|
54
|
+
draw_tiles
|
55
|
+
draw_statusbar
|
56
|
+
draw_gameover
|
57
|
+
end
|
58
|
+
|
59
|
+
def draw_statusbar
|
60
|
+
line = String.new
|
61
|
+
line << "#{@tiles.score} #{VERTICAL} "
|
62
|
+
line << "\u2190\u2193\u2191\u2192 Move #{VERTICAL} "
|
63
|
+
line << "u Undo #{VERTICAL} "
|
64
|
+
line << "r Reset #{VERTICAL} "
|
65
|
+
line << "+- Scale #{VERTICAL} "
|
66
|
+
line << 'q Quit'
|
67
|
+
x = @cols / 2 - line.length / 2 + 1
|
68
|
+
return if @cols < line.length
|
69
|
+
|
70
|
+
@terminal.move_to(1, @rows)
|
71
|
+
@terminal.bg_color(STATUSBAR_COLOR)
|
72
|
+
@terminal.write(BLANK * @cols)
|
73
|
+
@terminal.move_to(x, @rows)
|
74
|
+
@terminal.fg_color(STATUSBAR_TEXT_COLOR)
|
75
|
+
@terminal.write(line[0..(@cols - 1)])
|
76
|
+
@terminal.reset
|
77
|
+
end
|
78
|
+
|
79
|
+
def draw_gameover
|
80
|
+
y = @y + @height + 1
|
81
|
+
return if @rows <= y
|
82
|
+
|
83
|
+
@terminal.move_to(1, y)
|
84
|
+
@terminal.write(BLANK * @cols)
|
85
|
+
@terminal.reset
|
86
|
+
|
87
|
+
line = String.new
|
88
|
+
if @tiles.win?
|
89
|
+
line << 'Win!'
|
90
|
+
elsif @tiles.game_over?
|
91
|
+
line << 'Game over!'
|
92
|
+
end
|
93
|
+
x = @cols / 2 - line.length / 2 + 1
|
94
|
+
@terminal.move_to(x, y)
|
95
|
+
@tiles.win? ? @terminal.fg_color(WIN_COLOR) : @terminal.fg_color(GAMEOVER_COLOR)
|
96
|
+
@terminal.bold
|
97
|
+
@terminal.write(line[0..(@cols - 1)])
|
98
|
+
@terminal.reset
|
99
|
+
end
|
100
|
+
|
101
|
+
def draw_tiles
|
102
|
+
n = Math.sqrt(Tiles::SIZE).to_i
|
103
|
+
return if @cols <= @width || @rows <= @height
|
104
|
+
|
105
|
+
@terminal.move_to(@x, @y)
|
106
|
+
@terminal.fg_color(BORDER_COLOR)
|
107
|
+
@terminal.write(TOP_LEFT)
|
108
|
+
n.times do |i|
|
109
|
+
@terminal.write(HORIZONTAL * (@size * 2))
|
110
|
+
next if i == n - 1
|
111
|
+
|
112
|
+
@terminal.write(TOP_HORIZONTAL)
|
113
|
+
end
|
114
|
+
@terminal.write(TOP_RIGHT)
|
115
|
+
|
116
|
+
n.times do |i|
|
117
|
+
@size.times do |j|
|
118
|
+
@terminal.move_to(@x, @y + i * @size + i + j + 1)
|
119
|
+
@terminal.write(VERTICAL)
|
120
|
+
n.times do |k|
|
121
|
+
@terminal.reset
|
122
|
+
@terminal.write(BLANK * (@size * 2))
|
123
|
+
@terminal.reset
|
124
|
+
next if k == n - 1
|
125
|
+
|
126
|
+
@terminal.fg_color(BORDER_COLOR)
|
127
|
+
@terminal.write(VERTICAL)
|
128
|
+
end
|
129
|
+
@terminal.fg_color(BORDER_COLOR)
|
130
|
+
@terminal.write(VERTICAL)
|
131
|
+
end
|
132
|
+
next if i == n - 1
|
133
|
+
|
134
|
+
@terminal.move_to(@x, @y + i * (@size + 1) + @size + 1)
|
135
|
+
@terminal.write(LEFT_VERTICAL)
|
136
|
+
n.times do |j|
|
137
|
+
@terminal.write(HORIZONTAL * (@size * 2))
|
138
|
+
next if j == n - 1
|
139
|
+
|
140
|
+
@terminal.write(HORIZONTAL_VERTICAL)
|
141
|
+
end
|
142
|
+
@terminal.write(RIGHT_VERTICAL)
|
143
|
+
end
|
144
|
+
|
145
|
+
@terminal.move_to(@x, @y + @size * n + n)
|
146
|
+
@terminal.write(BOTTOM_LEFT)
|
147
|
+
n.times do |i|
|
148
|
+
@terminal.write(HORIZONTAL * (@size * 2))
|
149
|
+
next if i == n - 1
|
150
|
+
|
151
|
+
@terminal.write(BOTTOM_HORIZONTAL)
|
152
|
+
end
|
153
|
+
@terminal.write(BOTTOM_RIGHT)
|
154
|
+
@terminal.reset
|
155
|
+
|
156
|
+
@terminal.fg_color(TILE_COLOR)
|
157
|
+
@terminal.bold
|
158
|
+
@tiles.items.each.with_index do |tile, i|
|
159
|
+
t = tile.zero? ? '' : tile.to_s
|
160
|
+
x = i % n * (@size * 2 + 1) + @size - t.length / 2 + 1
|
161
|
+
y = i / n * (@size + 1) + @size / 2 + 1
|
162
|
+
@terminal.move_to(@x + x, @y + y)
|
163
|
+
@terminal.write(t)
|
164
|
+
end
|
165
|
+
@terminal.reset
|
166
|
+
end
|
167
|
+
|
168
|
+
def align
|
169
|
+
@rows, @cols = @terminal.display_size
|
170
|
+
n = Math.sqrt(Tiles::SIZE).to_i
|
171
|
+
@width = @size * 2 * n + n + 1
|
172
|
+
@height = @size * n + n + 1
|
173
|
+
@x = @cols / 2 - @width / 2
|
174
|
+
@y = @rows / 2 - @height / 2
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
module Game2048
|
6
|
+
##
|
7
|
+
# VT100 terminal
|
8
|
+
class Terminal
|
9
|
+
CSI = "\e["
|
10
|
+
CUU = 'A'
|
11
|
+
CUD = 'B'
|
12
|
+
CUF = 'C'
|
13
|
+
CUB = 'D'
|
14
|
+
CUP = 'H'
|
15
|
+
ED = 'J'
|
16
|
+
SGR = 'm'
|
17
|
+
CPR = '6n'
|
18
|
+
CPA = 'R'
|
19
|
+
|
20
|
+
HIDE_CURSOR = '?25l'
|
21
|
+
SHOW_CURSOR = '?25h'
|
22
|
+
|
23
|
+
RESET = 0
|
24
|
+
BOLD = 1
|
25
|
+
DIM = 2
|
26
|
+
BLINK = 5
|
27
|
+
REVERSE = 7
|
28
|
+
UNDERSCORE = 4
|
29
|
+
COLOR_FG = 30
|
30
|
+
COLOR_BG = 40
|
31
|
+
|
32
|
+
COLORS = {
|
33
|
+
black: 0,
|
34
|
+
red: 1,
|
35
|
+
green: 2,
|
36
|
+
yellow: 3,
|
37
|
+
blue: 4,
|
38
|
+
magenta: 5,
|
39
|
+
cyan: 6,
|
40
|
+
white: 7
|
41
|
+
}.freeze
|
42
|
+
|
43
|
+
KEY_MAP = {
|
44
|
+
CUU => :up,
|
45
|
+
CUD => :down,
|
46
|
+
CUF => :right,
|
47
|
+
CUB => :left
|
48
|
+
}.freeze
|
49
|
+
|
50
|
+
def initialize(input: $stdin, output: $stdout)
|
51
|
+
@input = input
|
52
|
+
@output = output
|
53
|
+
@sgr = []
|
54
|
+
end
|
55
|
+
|
56
|
+
def move_to(x, y)
|
57
|
+
@output.write("#{CSI}#{y};#{x}#{CUP}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def move_home
|
61
|
+
move_to(1, 1)
|
62
|
+
end
|
63
|
+
|
64
|
+
def erase_display
|
65
|
+
@output.write("#{CSI}2#{ED}")
|
66
|
+
end
|
67
|
+
|
68
|
+
def fg_color(color)
|
69
|
+
color_exist?(color)
|
70
|
+
@sgr << COLORS[color] + COLOR_FG
|
71
|
+
end
|
72
|
+
|
73
|
+
def bg_color(color)
|
74
|
+
color_exist?(color)
|
75
|
+
@sgr << COLORS[color] + COLOR_BG
|
76
|
+
end
|
77
|
+
|
78
|
+
def bold
|
79
|
+
@sgr << BOLD
|
80
|
+
end
|
81
|
+
|
82
|
+
def underscore
|
83
|
+
@sgr << UNDERSCORE
|
84
|
+
end
|
85
|
+
|
86
|
+
def dim
|
87
|
+
@sgr << DIM
|
88
|
+
end
|
89
|
+
|
90
|
+
def blink
|
91
|
+
@sgr << BLINK
|
92
|
+
end
|
93
|
+
|
94
|
+
def reverse
|
95
|
+
@sgr << REVERSE
|
96
|
+
end
|
97
|
+
|
98
|
+
def reset
|
99
|
+
@sgr.clear
|
100
|
+
@output.write("#{CSI}#{RESET}#{SGR}")
|
101
|
+
end
|
102
|
+
|
103
|
+
def hide_cursor
|
104
|
+
@output.write("#{CSI}#{HIDE_CURSOR}")
|
105
|
+
end
|
106
|
+
|
107
|
+
def show_cursor
|
108
|
+
@output.write("#{CSI}#{SHOW_CURSOR}")
|
109
|
+
end
|
110
|
+
|
111
|
+
def display_size
|
112
|
+
move_to(999, 999)
|
113
|
+
@output.write("#{CSI}#{CPR}")
|
114
|
+
data = String.new
|
115
|
+
data << @input.readchar
|
116
|
+
data << @input.readchar
|
117
|
+
return if data != CSI
|
118
|
+
|
119
|
+
data.clear
|
120
|
+
while (char = @input.readchar) != CPA
|
121
|
+
data << char
|
122
|
+
end
|
123
|
+
data.split(';').map(&:to_i)
|
124
|
+
end
|
125
|
+
|
126
|
+
def write(text = '')
|
127
|
+
text = "#{CSI}#{@sgr.join(';')}#{SGR}#{text}" unless @sgr.empty?
|
128
|
+
@sgr.clear
|
129
|
+
@output.write(text)
|
130
|
+
end
|
131
|
+
|
132
|
+
def read
|
133
|
+
data = @input.readchar
|
134
|
+
if data == "\e"
|
135
|
+
data << @input.readchar
|
136
|
+
if data == CSI
|
137
|
+
data.clear
|
138
|
+
loop do
|
139
|
+
char = @input.readchar
|
140
|
+
data << char
|
141
|
+
break unless char.ord.between?(0x30, 0x39) || char == ';'
|
142
|
+
end
|
143
|
+
|
144
|
+
return KEY_MAP.fetch(data, :unknown)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
data
|
149
|
+
end
|
150
|
+
|
151
|
+
def raw_mode
|
152
|
+
@input&.raw!
|
153
|
+
end
|
154
|
+
|
155
|
+
def cooked_mode
|
156
|
+
@input&.cooked!
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def color_exist?(color)
|
162
|
+
raise TerminalError, "Unknown color #{color}" unless COLORS.key?(color)
|
163
|
+
|
164
|
+
true
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class TerminalError < StandardError; end
|
169
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Game2048
|
6
|
+
##
|
7
|
+
# Game
|
8
|
+
class Tiles
|
9
|
+
SIZE = 4**2
|
10
|
+
NO_TILE = 0
|
11
|
+
NEW_TILE_CHANCE = 90
|
12
|
+
NEW_TILE_2 = 2
|
13
|
+
NEW_TILE_4 = 4
|
14
|
+
WIN_SUM = 2048
|
15
|
+
|
16
|
+
attr_reader :items
|
17
|
+
|
18
|
+
def initialize(items = nil, auto_new_tile: true)
|
19
|
+
raise TilesError, "Tile map size must be #{SIZE}" if !items.nil? && items.length != SIZE
|
20
|
+
|
21
|
+
if items.nil?
|
22
|
+
reset
|
23
|
+
else
|
24
|
+
@items = items
|
25
|
+
end
|
26
|
+
@auto_new_tile = auto_new_tile
|
27
|
+
@items_prev = []
|
28
|
+
end
|
29
|
+
|
30
|
+
def score
|
31
|
+
@items.sum
|
32
|
+
end
|
33
|
+
|
34
|
+
def reset
|
35
|
+
@items = Array.new(SIZE, NO_TILE)
|
36
|
+
new_tile
|
37
|
+
end
|
38
|
+
|
39
|
+
def new_tile
|
40
|
+
items = []
|
41
|
+
@items.each.with_index { |tile, i| items << i if tile == NO_TILE }
|
42
|
+
return if items.empty?
|
43
|
+
|
44
|
+
@items[items.sample] = SecureRandom.rand(1..100) <= NEW_TILE_CHANCE ? NEW_TILE_2 : NEW_TILE_4
|
45
|
+
end
|
46
|
+
|
47
|
+
def game_over?
|
48
|
+
return false if @items.index(NO_TILE)
|
49
|
+
|
50
|
+
n = Math.sqrt(SIZE).to_i
|
51
|
+
@items.each.with_index do |item, i|
|
52
|
+
a = i + 1
|
53
|
+
return false if a < @items.length && !(a % n).zero? && item == @items[a]
|
54
|
+
|
55
|
+
b = i + n
|
56
|
+
return false if b < @items.length && item == @items[b]
|
57
|
+
end
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
def win?
|
62
|
+
items.count { |item| item >= WIN_SUM } >= 1
|
63
|
+
end
|
64
|
+
|
65
|
+
def undo
|
66
|
+
return if @items_prev.empty?
|
67
|
+
|
68
|
+
@items = @items_prev.dup
|
69
|
+
@items_prev.clear
|
70
|
+
end
|
71
|
+
|
72
|
+
def move_up
|
73
|
+
items = @items.dup
|
74
|
+
n = Math.sqrt(SIZE).to_i
|
75
|
+
n.times do |row|
|
76
|
+
move_zeroes(-n)
|
77
|
+
n.times do |column|
|
78
|
+
tile = row * n + column
|
79
|
+
tile_prev = tile - n
|
80
|
+
if !tile_prev.negative? && @items[tile_prev] == @items[tile]
|
81
|
+
@items[tile_prev] += @items[tile]
|
82
|
+
@items[tile] = NO_TILE
|
83
|
+
end
|
84
|
+
end
|
85
|
+
move_zeroes(-n)
|
86
|
+
end
|
87
|
+
return if items == @items
|
88
|
+
|
89
|
+
@items_prev = items
|
90
|
+
new_tile if @auto_new_tile
|
91
|
+
end
|
92
|
+
|
93
|
+
def move_down
|
94
|
+
items = @items.dup
|
95
|
+
n = Math.sqrt(SIZE).to_i
|
96
|
+
(n - 1).downto(0) do |row|
|
97
|
+
move_zeroes(n)
|
98
|
+
n.times do |column|
|
99
|
+
tile = row * n + column
|
100
|
+
tile_next = tile + n
|
101
|
+
if tile_next < @items.length && @items[tile_next] == @items[tile]
|
102
|
+
@items[tile_next] += @items[tile]
|
103
|
+
@items[tile] = NO_TILE
|
104
|
+
end
|
105
|
+
end
|
106
|
+
move_zeroes(n)
|
107
|
+
end
|
108
|
+
return if items == @items
|
109
|
+
|
110
|
+
@items_prev = items
|
111
|
+
new_tile if @auto_new_tile
|
112
|
+
end
|
113
|
+
|
114
|
+
def move_right
|
115
|
+
items = @items.dup
|
116
|
+
n = Math.sqrt(SIZE).to_i
|
117
|
+
n.times do |row|
|
118
|
+
move_zeroes(1)
|
119
|
+
(n - 1).downto(0) do |column|
|
120
|
+
tile = row * n + column
|
121
|
+
tile_next = tile + 1
|
122
|
+
if tile_next < row * n + n && @items[tile_next] == @items[tile]
|
123
|
+
@items[tile_next] += @items[tile]
|
124
|
+
@items[tile] = NO_TILE
|
125
|
+
end
|
126
|
+
end
|
127
|
+
move_zeroes(1)
|
128
|
+
end
|
129
|
+
return if items == @items
|
130
|
+
|
131
|
+
@items_prev = items
|
132
|
+
new_tile if @auto_new_tile
|
133
|
+
end
|
134
|
+
|
135
|
+
def move_left
|
136
|
+
items = @items.dup
|
137
|
+
n = Math.sqrt(SIZE).to_i
|
138
|
+
n.times do |row|
|
139
|
+
move_zeroes(-1)
|
140
|
+
n.times do |column|
|
141
|
+
tile = row * n + column
|
142
|
+
tile_prev = tile - 1
|
143
|
+
if tile_prev >= row * n && @items[tile_prev] == @items[tile]
|
144
|
+
@items[tile_prev] += @items[tile]
|
145
|
+
@items[tile] = NO_TILE
|
146
|
+
end
|
147
|
+
end
|
148
|
+
move_zeroes(-1)
|
149
|
+
end
|
150
|
+
return if items == @items
|
151
|
+
|
152
|
+
@items_prev = items
|
153
|
+
new_tile if @auto_new_tile
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def move_zeroes(dir)
|
159
|
+
n = Math.sqrt(SIZE).to_i
|
160
|
+
n.times do |row|
|
161
|
+
n.times do |column|
|
162
|
+
tile = row * n + column
|
163
|
+
loop do
|
164
|
+
tile_other = tile + dir
|
165
|
+
if tile_other.negative? || tile_other >= @items.length || (dir == 1 && tile_other >= row * n + n) || (dir == -1 && tile_other < row * n) || @items[tile_other] != NO_TILE
|
166
|
+
break
|
167
|
+
end
|
168
|
+
|
169
|
+
@items[tile_other] = @items[tile]
|
170
|
+
@items[tile] = NO_TILE
|
171
|
+
tile = tile_other
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class TilesError < StandardError; end
|
179
|
+
end
|
data/lib/game_2048.rb
ADDED
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: 2048-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- anim
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-19 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: me@telpart.ru
|
15
|
+
executables:
|
16
|
+
- '2048'
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/2048
|
21
|
+
- lib/game_2048.rb
|
22
|
+
- lib/game_2048/game.rb
|
23
|
+
- lib/game_2048/render.rb
|
24
|
+
- lib/game_2048/terminal.rb
|
25
|
+
- lib/game_2048/tiles.rb
|
26
|
+
homepage: https://github.com/animotto/2048-ruby
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '2.7'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubygems_version: 3.1.2
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: Classic game 2048 in your terminal
|
49
|
+
test_files: []
|