rb2048 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bfd2e7bd56a12fbf552193b69014f17be9d094cadc5c2b31932693697d0a5bc9
4
+ data.tar.gz: d2558a8b1c94f5e98c66ccca187e0172deca3db52e72e514afdc6ae79deae21c
5
+ SHA512:
6
+ metadata.gz: 3054b8b701af938df0200407eac694447aa8526b828d93aa3bfee2f908a5e34d8657b48003eeed74cadad0b144e5b1a1a99ffa9b45c738d50150e10eea08fe00
7
+ data.tar.gz: 43215747406b1805eada7ccff358ce3495ff3343011f16990b10c1e5e4e30c61fcab834e0fc51643dbad26553253069612fa85adae83c5a406884b358495c209
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in rb2048.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+ gem "minitest", "~> 5.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,26 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rb2048 (1.0.0)
5
+ curses (~> 1.4.4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ curses (1.4.4)
11
+ minitest (5.16.2)
12
+ rake (13.0.6)
13
+
14
+ PLATFORMS
15
+ arm64-darwin-21
16
+ ruby
17
+ x86_64-darwin
18
+ x86_64-linux
19
+
20
+ DEPENDENCIES
21
+ minitest (~> 5.0)
22
+ rake (~> 13.0)
23
+ rb2048!
24
+
25
+ BUNDLED WITH
26
+ 2.3.4
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Mark24
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # Rb2048
2
+
3
+ Ruby 2048 Game
4
+
5
+
6
+ ## Install
7
+
8
+ `gem install rb2048`
9
+
10
+ ## Help
11
+
12
+ ```
13
+ Usage: rb2048 [options]
14
+ --version verison
15
+ --size SIZE Size of board: 4-10
16
+ --level LEVEL Hard Level 2-5
17
+ ```
18
+ ## Game Screen
19
+
20
+ ```
21
+
22
+ -- Ruby 2048 --
23
+
24
+ -------------------------------------
25
+ | 16 | 16 | 2 | 16 |
26
+ -------------------------------------
27
+ | 0 | 0 | 0 | 0 |
28
+ -------------------------------------
29
+ | 0 | 0 | 0 | 2 |
30
+ -------------------------------------
31
+ | 0 | 0 | 0 | 0 |
32
+ -------------------------------------
33
+
34
+ Score: 16 You:UP
35
+
36
+
37
+
38
+ Control: W(↑) A(←) S(↓) D(→) Q(quit) R(Restart)
39
+ ```
40
+
41
+ ## Game Model Lightspot
42
+
43
+ Not just a main thread to create TUI Game.
44
+
45
+ This is a experiment for just testing Ruby3 Thread & Queue.
46
+
47
+ I build this game that UserI/O, Data Computing, TUI Render use 3 different threads & message channel to work together.
48
+
49
+ ## Contributing
50
+
51
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Mark24Code/rb2048.
52
+
53
+ ## License
54
+
55
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ task default: %i[test]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "rb2048"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/rb2048 ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require_relative "../lib/rb2048"
4
+
5
+ options = {}
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: rb2048 [options]"
8
+
9
+ opts.on("--version", "verison") do |v|
10
+ # optins[:verison] = v
11
+ puts "Ruby2048: v#{::Rb2048::VERSION}"
12
+ puts "author: Mark24"
13
+ puts "mail: mark.zhangyoung@qq.com"
14
+ puts "project: https://github.com/Mark24Code/rb2048"
15
+ return
16
+ end
17
+
18
+ opts.on("--size SIZE", Integer, "Size of board: 4-10") do |size|
19
+ if size < 4
20
+ puts "--size too small,it should in [4,10].Then will use size=4 as default"
21
+ sleep 1
22
+ elsif size > 10
23
+ puts "--size too small,it should in [4,10].It will use size=10."
24
+ sleep 1
25
+ end
26
+ options[:size] = size
27
+ end
28
+
29
+ opts.on("--level LEVEL", Integer, "Hard Level 2-5") do |level|
30
+ if level < 2
31
+ puts "--level too small,it should in [2,5].Then will use level=2 as default"
32
+ sleep 1
33
+ elsif level > 5
34
+ puts "--level too small,it should in [2,5].It will use size=5."
35
+ sleep 1
36
+ end
37
+ options[:level] = level
38
+ end
39
+
40
+ end.parse!
41
+
42
+ init_size = options[:size] || 4
43
+ init_level = options[:level] || 2
44
+
45
+ ::Rb2048::Game.new(nil,init_size,init_level).run
@@ -0,0 +1,212 @@
1
+ require "curses"
2
+ require_relative './game_board'
3
+ require "thread"
4
+ require_relative './logger'
5
+
6
+
7
+ trap("INT") { ::Rb2048::Game.quit_game; exit 0 }
8
+
9
+ module Rb2048
10
+ class Game
11
+
12
+ def self.quit_game
13
+ Curses.close_screen
14
+ printf "Goodbye :P \n"
15
+ end
16
+
17
+ def initialize(init_elements = nil, size=4, level=2)
18
+ @logger = LoggerMan
19
+
20
+ @level = level
21
+ @size = size
22
+
23
+ @render = ::Rb2048::GameRender.new
24
+ @window = @render.window
25
+
26
+ @frame_data_channel = Queue.new
27
+ @refresh_rate = 60
28
+ @frame_delta = 1.0 / @refresh_rate
29
+
30
+ @io_data_channel = Queue.new
31
+
32
+ init_game(init_elements)
33
+ end
34
+
35
+ def run
36
+ init_game
37
+ main_loop
38
+ end
39
+
40
+ def init_game(init_elements=nil)
41
+ @backend = ::Rb2048::GameBoard.new(@size,@level)
42
+ @backend.create_elements(init_elements)
43
+
44
+ @logger.log("INIT", "start")
45
+ @frame_data_channel << @backend.tun_result
46
+ end
47
+
48
+ def main_loop
49
+ key_listener_thr = Thread.new { self.key_listener }
50
+ action_thr = Thread.new { self.action }
51
+ render_thr = Thread.new { self.render }
52
+
53
+ [key_listener_thr,action_thr, render_thr].map(&:join)
54
+ end
55
+
56
+ def action
57
+ while (command = @io_data_channel.shift)
58
+ if command == :QUIT
59
+ self.class.quit_game
60
+ exit 0
61
+ elsif command == :RESTART
62
+ self.init_game
63
+ else
64
+ @backend.__send__("#{command.to_s.downcase}_merge")
65
+ frame_data = @backend.tun_result
66
+ @frame_data_channel << frame_data
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ def key_listener()
73
+ while true
74
+ command = nil
75
+
76
+ k = @window.getch
77
+ if k == Curses::KEY_UP || k == "w"
78
+ command = :UP
79
+ elsif k == Curses::KEY_DOWN || k == "s"
80
+ command = :DOWN
81
+ elsif k == Curses::KEY_LEFT || k == "a"
82
+ command = :LEFT
83
+ elsif k == Curses::KEY_RIGHT || k == "d"
84
+ command = :RIGHT
85
+ elsif k == "q"
86
+ command = :QUIT
87
+ elsif k == "r"
88
+ command = :RESTART
89
+ else
90
+ end
91
+
92
+ @io_data_channel << command if command
93
+ sleep 0.08
94
+ end
95
+ end
96
+
97
+ def render
98
+ while (frame_data = @frame_data_channel.shift)
99
+ break if frame_data == :done
100
+ @render.draw(frame_data) if frame_data
101
+ sleep 0.16
102
+ end
103
+ end
104
+ end
105
+
106
+ class GameRender
107
+ attr :window
108
+ def initialize
109
+ @window = nil
110
+ @window_height = nil
111
+ @window_width = nil
112
+
113
+ init_screen
114
+ end
115
+
116
+ def init_screen
117
+ Curses.init_screen
118
+ Curses.cbreak
119
+ Curses.noecho
120
+ Curses.curs_set(0) # Invisible cursor
121
+ Curses.stdscr.keypad = true
122
+
123
+ @window_height = Curses.lines
124
+ @window_width = Curses.cols
125
+
126
+ @window = Curses::Window.new(@window_height, @window_width, 0, 0)
127
+ @window.refresh
128
+ end
129
+
130
+ def draw(payload)
131
+ data = payload[:data]
132
+ size = payload[:size]
133
+
134
+ level = payload[:level]
135
+ score = payload[:score]
136
+ last_action = payload[:last_action]
137
+ status = payload[:status]
138
+
139
+ start_timestamp = payload[:start_timestamp]
140
+ end_timestamp = payload[:end_timestamp]
141
+
142
+ tun_new_value = payload[:tun_new_value]
143
+
144
+ @window.clear
145
+
146
+ # display board content buffer
147
+ buffer = []
148
+ number_width = score.to_s.length * 2
149
+ for row_index in (0..size-1)
150
+ row_text = "|"
151
+ for col_index in (0..size-1)
152
+
153
+ n = data[row_index][col_index]
154
+ n_text = " "*(number_width - n.to_s.length) + n.to_s
155
+ row_text +=" "*2+"#{n_text}"+" "*2+"|"
156
+ end
157
+ buffer.push(row_text)
158
+ end
159
+
160
+ # x,y offset counter
161
+ row,col = [0,1]
162
+
163
+ # title
164
+ content_width = buffer.first.length
165
+ title = "-- Ruby 2048 --"
166
+ left_offset = (content_width - title.length) / 2
167
+ row = row + 1
168
+ @window.setpos(row, left_offset)
169
+ @window.addstr("-- Ruby 2048 --")
170
+
171
+ # board
172
+ row = row + 2
173
+ start_row, start_col = [row,1]
174
+ @window.setpos(start_row, start_col)
175
+ @window.addstr("-"*buffer.first.length)
176
+ buffer.each_with_index do |line,index|
177
+ @window.setpos(start_row+index*2 + 2, start_col)
178
+ @window.addstr("-" * line.length)
179
+ @window.setpos(start_row+index*2 + 1, start_col)
180
+ @window.addstr(line)
181
+ end
182
+
183
+ # score
184
+ row = row + buffer.length * 2 + 2
185
+ @window.setpos(row, col)
186
+ @window.addstr("Score:\t#{score}\t\t#{"You:"+last_action.to_s if last_action}")
187
+
188
+ # status
189
+
190
+ row = row + 2
191
+ @window.setpos(row, col)
192
+ result_text = nil
193
+ if status == 1
194
+ result_text = "Congratulation! You have got 2048!!! :D"
195
+ elsif status == -1
196
+ result_text = "YOU LOST :("
197
+ end
198
+
199
+ if result_text
200
+ left_offset = (buffer.first.length - result_text.length )/2
201
+ @window.setpos(row, left_offset)
202
+ @window.addstr(result_text)
203
+ end
204
+
205
+ # control
206
+ row = row + 2
207
+ @window.setpos(row, col)
208
+ @window.addstr("Control: W(↑) A(←) S(↓) D(→) Q(quit) R(Restart)")
209
+ @window.refresh
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,291 @@
1
+ require_relative './logger'
2
+
3
+ module Rb2048
4
+ class GameBoard
5
+ attr :elements, :status
6
+ def initialize(size=4, level = 2,win_standard=2048)
7
+ @size = size
8
+ @elements = []
9
+
10
+ @level = level
11
+ @score = 0
12
+ @start_timestamp = nil
13
+ @end_timestamp = nil
14
+ @status = 0 # -1 lost, 0 init/running, 1 win
15
+ @win_standard = win_standard
16
+
17
+ @last_action = nil
18
+ @logger = LoggerMan
19
+ end
20
+
21
+ def create_init_value
22
+ total_count = @size ** 2
23
+ init_value_count = total_count * @level / 10
24
+ zero_value_count = total_count - init_value_count
25
+
26
+ values = []
27
+ for i in (0..init_value_count-1)
28
+ values.push(rand_init)
29
+ end
30
+
31
+ for i in (0..zero_value_count-1)
32
+ values.push(0)
33
+ end
34
+
35
+ values.shuffle
36
+ end
37
+
38
+ def create_elements(init_elements = nil)
39
+ pos_values = init_elements || create_init_value
40
+
41
+ max_nums = @size - 1
42
+ for i in (0..max_nums)
43
+ row = []
44
+ for j in (0..max_nums)
45
+ row.push(pos_values[i*@size+j])
46
+ end
47
+ @elements.push(row)
48
+ end
49
+ @elements
50
+ end
51
+
52
+ def bigger_then_zero(arr)
53
+ arr.filter { |e| e > 0}
54
+ end
55
+
56
+ def left_merge
57
+ @logger.log("Left", @elements)
58
+ for row_index in (0..@size-1)
59
+ row = @elements[row_index]
60
+ update_row = update_line(row)
61
+ @elements[row_index] = update_row
62
+ end
63
+ @last_action = :LEFT
64
+ @elements
65
+ end
66
+
67
+ def right_merge
68
+ @logger.log("Right", @elements)
69
+ for row_index in (0..@size-1)
70
+ row = @elements[row_index]
71
+ update_row = update_line(row.reverse)
72
+ @elements[row_index] = update_row.reverse
73
+ end
74
+ @last_action = :RIGHT
75
+ @elements
76
+ end
77
+
78
+
79
+ def up_merge
80
+ @logger.log("UP", @elements)
81
+ for col_index in (0..@size-1)
82
+ col = []
83
+ for row_index in (0..@size-1)
84
+ col.push(@elements[row_index][col_index])
85
+ end
86
+ update_col = update_line(col)
87
+
88
+ for row_index in (0..@size-1)
89
+ @elements[row_index][col_index] = update_col[row_index]
90
+ end
91
+ end
92
+ @last_action = :UP
93
+ @elements
94
+ end
95
+
96
+ def down_merge
97
+ @logger.log("Down", @elements)
98
+ for col_index in (0..@size-1)
99
+ col = []
100
+ for row_index in (0..@size-1)
101
+ col.push(@elements[row_index][col_index])
102
+ end
103
+
104
+ update_col = update_line(col.reverse).reverse
105
+ # update
106
+ for row_index in (0..@size-1)
107
+ @elements[row_index][col_index] = update_col[row_index]
108
+ end
109
+ end
110
+ @last_action = :DOWN
111
+ @elements
112
+ end
113
+
114
+ def merge_once(arr)
115
+
116
+ if arr.length <= 1
117
+ return arr
118
+ end
119
+
120
+ data = arr.clone
121
+ result = []
122
+ first = nil
123
+ next_ = nil
124
+
125
+ while !data.empty?
126
+ if first == nil
127
+ first = data.shift
128
+ end
129
+
130
+ if next_ == nil
131
+ next_ = data.shift
132
+ end
133
+
134
+ if first != next_
135
+ result.push(first)
136
+ if next_
137
+ data.unshift(next_)
138
+ end
139
+ first = nil
140
+ next_ = nil
141
+ else
142
+ result.push(first + next_)
143
+
144
+ first = nil
145
+ next_ = nil
146
+ end
147
+ end
148
+
149
+ return result
150
+ end
151
+
152
+ def collect_max_score(result)
153
+ max_ = result.max
154
+ if max_
155
+ @score = @score > result.max ? @score : result.max
156
+ end
157
+ end
158
+
159
+ def merge(arr)
160
+ if arr.length <= 0
161
+ return arr
162
+ end
163
+ result = arr.clone
164
+
165
+ while true
166
+ before = result.clone
167
+ result = merge_once(result)
168
+ break if before == result
169
+ end
170
+
171
+ return result
172
+ end
173
+
174
+ def create_zero_array(len)
175
+ Array.new(len,0)
176
+ end
177
+
178
+ def update_line(arr)
179
+ size = arr.length
180
+ compact = merge(bigger_then_zero(arr))
181
+ collect_max_score(compact)
182
+ compact.concat(create_zero_array(size - compact.length))
183
+ end
184
+
185
+ def start_timer
186
+ @start_timestamp = Time.now.to_i
187
+ end
188
+
189
+ def close_timer
190
+ @end_timestamp = Time.now.to_i
191
+ end
192
+
193
+ def check_dead_road
194
+ dead_road = @size * 2
195
+
196
+ for row_index in (0..@size-1)
197
+ row = @elements[row_index]
198
+ before = bigger_then_zero(row).clone
199
+ after = merge(before)
200
+ if before == after
201
+ dead_road -= 1
202
+ end
203
+ end
204
+
205
+ for col_index in (0..@size-1)
206
+ col = []
207
+ for row_index in (0..@size-1)
208
+ col.push(@elements[row_index][col_index])
209
+ end
210
+
211
+ before = bigger_then_zero(col).clone
212
+ after = merge(before)
213
+ if before == after
214
+ dead_road -= 1
215
+ end
216
+ end
217
+
218
+ dead_road
219
+ end
220
+
221
+ def check_game_status
222
+ if @score >= @win_standard
223
+ return @status = 1
224
+ else
225
+ if all_zero_pos.length == 0
226
+ dead_road = check_dead_road
227
+ if dead_road <= 0
228
+ @status = -1
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ def rand_init
235
+ 2 ** rand(1..4)
236
+ end
237
+
238
+ def create_new_number
239
+ zeros = all_zero_pos
240
+ if zeros.length > 0
241
+ x,y = zeros.sample
242
+ new_number = rand_init
243
+ @elements[x][y] = new_number
244
+ {pos:[x,y], value: new_number}
245
+ end
246
+
247
+ return nil
248
+ end
249
+
250
+ def all_zero_pos
251
+ zeros = []
252
+ for i in (0..@size-1)
253
+ for j in (0..@size-1)
254
+ e = @elements[i][j]
255
+ if e == 0
256
+ zeros.push([i,j])
257
+ end
258
+ end
259
+ end
260
+
261
+ zeros
262
+ end
263
+
264
+ def tun_result
265
+ tun_new_value = create_new_number
266
+
267
+ check_game_status
268
+
269
+ data = []
270
+ for i in (0..@elements.length-1)
271
+ row = []
272
+ for j in (0..@elements.length-1)
273
+ row.push(@elements[i][j])
274
+ end
275
+ data.push(row)
276
+ end
277
+
278
+ return {
279
+ data: data,
280
+ size: @size,
281
+ level: @level,
282
+ score: @score,
283
+ last_action: @last_action,
284
+ start_timestamp: @start_timestamp,
285
+ end_timestamp: @end_timestamp,
286
+ status: @status,
287
+ tun_new_value: tun_new_value
288
+ }
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,21 @@
1
+ class Logger
2
+ def initialize(switch)
3
+ @switch = switch
4
+ if @switch == :on
5
+ @output = File.absolute_path("#{__dir__}/../../log")
6
+ @f = File.open("#{@output}/#{Time.now.to_s}.log","w+")
7
+ end
8
+ end
9
+
10
+ def log(component, info)
11
+ @f << "[#{component}]: #{Time.now.to_s}: #{info}\n" if @switch == :on
12
+ end
13
+
14
+ def close
15
+ @f.close if @switch == :on
16
+ end
17
+ end
18
+
19
+ LoggerMan = Logger.new(:off)
20
+
21
+ at_exit { LoggerMan.close }
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rb2048
4
+ VERSION = "1.0.0"
5
+ end
data/lib/rb2048.rb ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./rb2048/version"
4
+
5
+ module Rb2048
6
+ class Error < StandardError; end
7
+
8
+ class InvalidValue < StandardError;end
9
+ # Your code goes here...
10
+ end
11
+
12
+ require_relative "./rb2048/game"
data/sig/rb2048.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Rb2048
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rb2048
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Mark24
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-07-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: curses
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.4
27
+ description: 2048 command line game build by ruby.
28
+ email:
29
+ - mark.zhangyoung@qq.com
30
+ executables:
31
+ - rb2048
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - Gemfile
36
+ - Gemfile.lock
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - bin/console
41
+ - bin/setup
42
+ - exe/rb2048
43
+ - lib/rb2048.rb
44
+ - lib/rb2048/game.rb
45
+ - lib/rb2048/game_board.rb
46
+ - lib/rb2048/logger.rb
47
+ - lib/rb2048/version.rb
48
+ - sig/rb2048.rbs
49
+ homepage: https://github.com/Mark24Code/rb2048
50
+ licenses:
51
+ - MIT
52
+ metadata:
53
+ homepage_uri: https://github.com/Mark24Code/rb2048
54
+ source_code_uri: https://github.com/Mark24Code/rb2048
55
+ changelog_uri: https://github.com/Mark24Code/rb2048
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.6.0
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubygems_version: 3.3.3
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: Ruby Command line 2048 game
75
+ test_files: []