minehunter 0.1.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 +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE.txt +555 -0
- data/README.md +101 -0
- data/exe/minehunt +3 -0
- data/exe/minehunter +5 -0
- data/lib/minehunter/cli.rb +126 -0
- data/lib/minehunter/field.rb +127 -0
- data/lib/minehunter/game.rb +272 -0
- data/lib/minehunter/grid.rb +384 -0
- data/lib/minehunter/intro.rb +52 -0
- data/lib/minehunter/version.rb +5 -0
- data/lib/minehunter.rb +23 -0
- metadata +178 -0
@@ -0,0 +1,384 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "field"
|
4
|
+
|
5
|
+
module Minehunter
|
6
|
+
# A grid with fields representation
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class Grid
|
10
|
+
# Track the number of flags remaining
|
11
|
+
#
|
12
|
+
# @return [Integer]
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
attr_reader :flags_remaining
|
16
|
+
|
17
|
+
# Track the number of unmined fields remaining
|
18
|
+
#
|
19
|
+
# @return [Integer]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
attr_reader :unmined_fields_remaining
|
23
|
+
|
24
|
+
# Create a Grid instance
|
25
|
+
#
|
26
|
+
# @param [Integer] width
|
27
|
+
# the number of columns
|
28
|
+
# @param [Integer] height
|
29
|
+
# the number of rows
|
30
|
+
# @param [Integer] mines_limit
|
31
|
+
# the total number of mines
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def initialize(width: nil, height: nil, mines_limit: nil)
|
35
|
+
if mines_limit >= width * height
|
36
|
+
raise Error, "cannot have more mines than available fields"
|
37
|
+
end
|
38
|
+
|
39
|
+
@width = width
|
40
|
+
@height = height
|
41
|
+
@mines_limit = mines_limit
|
42
|
+
@fields = []
|
43
|
+
|
44
|
+
reset
|
45
|
+
end
|
46
|
+
|
47
|
+
# Reset all fields to defaults
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
def reset
|
51
|
+
(@width * @height).times do |i|
|
52
|
+
@fields[i] = Field.new
|
53
|
+
end
|
54
|
+
@unmined_fields_remaining = @width * @height - @mines_limit
|
55
|
+
@flags_remaining = @mines_limit
|
56
|
+
end
|
57
|
+
|
58
|
+
# Check whether or not the grid is cleared
|
59
|
+
#
|
60
|
+
# @return [Boolean]
|
61
|
+
#
|
62
|
+
# @api public
|
63
|
+
def cleared?
|
64
|
+
@unmined_fields_remaining.zero?
|
65
|
+
end
|
66
|
+
|
67
|
+
# All fields with mines
|
68
|
+
#
|
69
|
+
# @return [Array<Field>]
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
def mines
|
73
|
+
@fields.select(&:mine?)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Move up on the grid
|
77
|
+
#
|
78
|
+
# @return [Integer]
|
79
|
+
#
|
80
|
+
# @api public
|
81
|
+
def move_up(y)
|
82
|
+
y.zero? ? @height - 1 : y - 1
|
83
|
+
end
|
84
|
+
|
85
|
+
# Move down on the grid
|
86
|
+
#
|
87
|
+
# @return [Integer]
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
def move_down(y)
|
91
|
+
y == @height - 1 ? 0 : y + 1
|
92
|
+
end
|
93
|
+
|
94
|
+
# Move left on the grid
|
95
|
+
#
|
96
|
+
# @return [Integer]
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
def move_left(x)
|
100
|
+
x.zero? ? @width - 1 : x - 1
|
101
|
+
end
|
102
|
+
|
103
|
+
# Move right on the grid
|
104
|
+
#
|
105
|
+
# @return [Integer]
|
106
|
+
#
|
107
|
+
# @api public
|
108
|
+
def move_right(x)
|
109
|
+
x == @width - 1 ? 0 : x + 1
|
110
|
+
end
|
111
|
+
|
112
|
+
# Find field index at a given position
|
113
|
+
#
|
114
|
+
# @param [Integer] x
|
115
|
+
# the x coordinate
|
116
|
+
# @param [Integer] y
|
117
|
+
# the y coordinate
|
118
|
+
#
|
119
|
+
# @return [Integer]
|
120
|
+
#
|
121
|
+
# @api public
|
122
|
+
def at(x, y)
|
123
|
+
y * @width + x
|
124
|
+
end
|
125
|
+
|
126
|
+
# Find a field at a given position
|
127
|
+
#
|
128
|
+
# @param [Integer] x
|
129
|
+
# the x coordinate
|
130
|
+
# @param [Integer] y
|
131
|
+
# the y coordinate
|
132
|
+
#
|
133
|
+
# @return [Field]
|
134
|
+
#
|
135
|
+
# @api public
|
136
|
+
def field_at(x, y)
|
137
|
+
@fields[at(x, y)]
|
138
|
+
end
|
139
|
+
|
140
|
+
# Set a mine at a given position
|
141
|
+
#
|
142
|
+
# @param [Integer] x
|
143
|
+
# the x coordinate
|
144
|
+
# @param [Integer] y
|
145
|
+
# the y coordinate
|
146
|
+
#
|
147
|
+
# @api public
|
148
|
+
def mine(x, y)
|
149
|
+
field_at(x, y).mine!
|
150
|
+
end
|
151
|
+
|
152
|
+
# Add or remove a flag at a given position
|
153
|
+
#
|
154
|
+
# @param [Integer] x
|
155
|
+
# the x coordinate
|
156
|
+
# @param [Integer] y
|
157
|
+
# the y coordinate
|
158
|
+
#
|
159
|
+
# @api public
|
160
|
+
def flag(x, y)
|
161
|
+
field = field_at(x, y)
|
162
|
+
return unless field.cover?
|
163
|
+
|
164
|
+
@flags_remaining += field.flag? ? 1 : -1
|
165
|
+
field.flag
|
166
|
+
end
|
167
|
+
|
168
|
+
# Check whether or not there is a flag at a given position
|
169
|
+
#
|
170
|
+
# @param [Integer] x
|
171
|
+
# the x coordinate
|
172
|
+
# @param [Integer] y
|
173
|
+
# the y coordinate
|
174
|
+
#
|
175
|
+
# @return [Boolean]
|
176
|
+
#
|
177
|
+
# @api public
|
178
|
+
def flag?(x, y)
|
179
|
+
field_at(x, y).flag?
|
180
|
+
end
|
181
|
+
|
182
|
+
# Fill grid with mines skipping the current position and nearby fields
|
183
|
+
#
|
184
|
+
# @param [Integer] x
|
185
|
+
# the x coordinate
|
186
|
+
# @param [Integer] y
|
187
|
+
# the y coordinate
|
188
|
+
# @param [Proc] randomiser
|
189
|
+
# the mine position randomiser
|
190
|
+
#
|
191
|
+
# @api public
|
192
|
+
def fill_with_mines(x, y, randomiser: DEFAULT_RANDOMISER)
|
193
|
+
limit = @mines_limit
|
194
|
+
while limit > 0
|
195
|
+
mine_x = randomiser[@width]
|
196
|
+
mine_y = randomiser[@height]
|
197
|
+
next if mine_x == x && mine_y == y
|
198
|
+
next if fields_next_to(x, y).include?([mine_x, mine_y])
|
199
|
+
|
200
|
+
field = field_at(mine_x, mine_y)
|
201
|
+
next if field.mine?
|
202
|
+
|
203
|
+
field.mine!
|
204
|
+
limit -= 1
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Enumerate fields next to a given position
|
209
|
+
#
|
210
|
+
# @param [Integer] x
|
211
|
+
# the x coordinate
|
212
|
+
# @param [Integer] y
|
213
|
+
# the y coordinate
|
214
|
+
#
|
215
|
+
# @return [Enumerator]
|
216
|
+
# the coordinates for nearby fields
|
217
|
+
#
|
218
|
+
# @api public
|
219
|
+
def fields_next_to(x, y)
|
220
|
+
return to_enum(:fields_next_to, x, y) unless block_given?
|
221
|
+
|
222
|
+
-1.upto(1) do |offset_x|
|
223
|
+
-1.upto(1) do |offset_y|
|
224
|
+
close_x = x + offset_x
|
225
|
+
close_y = y + offset_y
|
226
|
+
|
227
|
+
next if close_x == x && close_y == y
|
228
|
+
next unless within?(close_x, close_y)
|
229
|
+
|
230
|
+
yield(close_x, close_y)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Check whether coordinates are within the grid
|
236
|
+
#
|
237
|
+
# return [Boolean]
|
238
|
+
#
|
239
|
+
# @api public
|
240
|
+
def within?(x, y)
|
241
|
+
x >= 0 && x < @width && y >= 0 && y < @height
|
242
|
+
end
|
243
|
+
|
244
|
+
# Total number of mines next to a given position
|
245
|
+
#
|
246
|
+
# @param [Integer] x
|
247
|
+
# the x coordinate
|
248
|
+
# @param [Integer] y
|
249
|
+
# the y coordinate
|
250
|
+
#
|
251
|
+
# @return [Integer]
|
252
|
+
#
|
253
|
+
# @api public
|
254
|
+
def count_mines_next_to(x, y)
|
255
|
+
fields_next_to(x, y).reduce(0) do |acc, cords|
|
256
|
+
acc + (field_at(*cords).mine? ? 1 : 0)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Total number of flags next to a given position
|
261
|
+
#
|
262
|
+
# @param [Integer] x
|
263
|
+
# the x coordinate
|
264
|
+
# @param [Integer] y
|
265
|
+
# the y coordinate
|
266
|
+
#
|
267
|
+
# @return [Integer]
|
268
|
+
#
|
269
|
+
# @api public
|
270
|
+
def count_flags_next_to(x, y)
|
271
|
+
fields_next_to(x, y).reduce(0) do |acc, cords|
|
272
|
+
acc + (field_at(*cords).flag? ? 1 : 0)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Uncover fields surrounding the position
|
277
|
+
#
|
278
|
+
# @param [Integer] x
|
279
|
+
# the x coordinate
|
280
|
+
# @param [Integer] y
|
281
|
+
# the y coordinate
|
282
|
+
#
|
283
|
+
# @return [Boolean]
|
284
|
+
# whether or not uncovered a mine
|
285
|
+
#
|
286
|
+
# @api public
|
287
|
+
def uncover(x, y)
|
288
|
+
field = field_at(x, y)
|
289
|
+
|
290
|
+
if field.mine?
|
291
|
+
field.uncover
|
292
|
+
uncover_mines
|
293
|
+
return true
|
294
|
+
end
|
295
|
+
|
296
|
+
return uncover_around(x, y) unless field.cover?
|
297
|
+
|
298
|
+
mine_count = count_mines_next_to(x, y)
|
299
|
+
field.mine_count = mine_count
|
300
|
+
flag(x, y) if field.flag?
|
301
|
+
field.uncover
|
302
|
+
@unmined_fields_remaining -= 1
|
303
|
+
|
304
|
+
if mine_count.zero?
|
305
|
+
fields_next_to(x, y) do |close_x, close_y|
|
306
|
+
close_field = field_at(close_x, close_y)
|
307
|
+
next if !close_field.cover? || close_field.mine?
|
308
|
+
|
309
|
+
uncover(close_x, close_y)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
false
|
313
|
+
end
|
314
|
+
|
315
|
+
# Uncover fields around numbered field matching flags count
|
316
|
+
#
|
317
|
+
# @param [Integer] x
|
318
|
+
# the x coordinate
|
319
|
+
# @param [Integer] y
|
320
|
+
# the y coordinate
|
321
|
+
#
|
322
|
+
# @return [Boolean]
|
323
|
+
# whether or not uncovered a mine
|
324
|
+
#
|
325
|
+
# @api public
|
326
|
+
def uncover_around(x, y)
|
327
|
+
field = field_at(x, y)
|
328
|
+
uncovered_mine = false
|
329
|
+
|
330
|
+
if count_flags_next_to(x, y) != field.mine_count
|
331
|
+
return uncovered_mine
|
332
|
+
end
|
333
|
+
|
334
|
+
fields_next_to(x, y) do |close_x, close_y|
|
335
|
+
close_field = field_at(close_x, close_y)
|
336
|
+
next if !close_field.cover? || close_field.flag?
|
337
|
+
|
338
|
+
uncover(close_x, close_y)
|
339
|
+
|
340
|
+
uncovered_mine = true if close_field.mine?
|
341
|
+
end
|
342
|
+
|
343
|
+
uncovered_mine
|
344
|
+
end
|
345
|
+
|
346
|
+
# Uncover all mines without a flag
|
347
|
+
#
|
348
|
+
# @api public
|
349
|
+
def uncover_mines
|
350
|
+
@fields.each do |field|
|
351
|
+
if field.mine? && !field.flag? || field.flag? && !field.mine?
|
352
|
+
field.wrong if field.flag?
|
353
|
+
field.uncover
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
# Render grid
|
359
|
+
#
|
360
|
+
# @return [String]
|
361
|
+
#
|
362
|
+
# @api public
|
363
|
+
def render(x, y, decorator: DEFAULT_DECORATOR)
|
364
|
+
out = []
|
365
|
+
|
366
|
+
@height.times do |field_y|
|
367
|
+
@width.times do |field_x|
|
368
|
+
field = field_at(field_x, field_y)
|
369
|
+
rendered_field = field.render(decorator: decorator)
|
370
|
+
|
371
|
+
if field_x == x && field_y == y && decorator
|
372
|
+
bg_color = field.mine? && !field.cover? ? :on_red : :on_green
|
373
|
+
rendered_field = decorator[rendered_field, bg_color]
|
374
|
+
end
|
375
|
+
|
376
|
+
out << rendered_field
|
377
|
+
end
|
378
|
+
out << "\n"
|
379
|
+
end
|
380
|
+
|
381
|
+
out.join
|
382
|
+
end
|
383
|
+
end # Grid
|
384
|
+
end # Minehunter
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Minehunter
|
4
|
+
# An intro screen content
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class Intro
|
8
|
+
INTRO = [
|
9
|
+
" ,-*",
|
10
|
+
" (_) Minehunter",
|
11
|
+
"",
|
12
|
+
"Movement",
|
13
|
+
" [↑] [w]",
|
14
|
+
" [←][↓][→] [a][s][d]",
|
15
|
+
"",
|
16
|
+
"Actions",
|
17
|
+
" Toggle Flag f",
|
18
|
+
" Uncover space",
|
19
|
+
" Restart r",
|
20
|
+
" Quit q",
|
21
|
+
"",
|
22
|
+
"Press any key to start!"
|
23
|
+
].freeze
|
24
|
+
|
25
|
+
# The maximum intro screen content width
|
26
|
+
#
|
27
|
+
# @return [Integer]
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def self.width
|
31
|
+
@width ||= INTRO.max_by(&:length).size
|
32
|
+
end
|
33
|
+
|
34
|
+
# The intro screen content height
|
35
|
+
#
|
36
|
+
# @return [Integer]
|
37
|
+
#
|
38
|
+
# @api public
|
39
|
+
def self.height
|
40
|
+
@height ||= INTRO.size
|
41
|
+
end
|
42
|
+
|
43
|
+
# Render intro screen content
|
44
|
+
#
|
45
|
+
# @return [String]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def self.render
|
49
|
+
INTRO.join("\n")
|
50
|
+
end
|
51
|
+
end # SplashScreen
|
52
|
+
end # Minehunter
|
data/lib/minehunter.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "minehunter/cli"
|
4
|
+
|
5
|
+
module Minehunter
|
6
|
+
class Error < StandardError; end
|
7
|
+
|
8
|
+
# Apply no styling
|
9
|
+
DEFAULT_DECORATOR = ->(str, *_colors) { str }
|
10
|
+
|
11
|
+
# Random number generator
|
12
|
+
GENERATOR = Random.new
|
13
|
+
|
14
|
+
# Generate random number less than max
|
15
|
+
DEFAULT_RANDOMISER = ->(max) { GENERATOR.rand(max) }
|
16
|
+
|
17
|
+
# Start the game
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def self.run
|
21
|
+
CLI.new.run
|
22
|
+
end
|
23
|
+
end # Minehunter
|
metadata
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: minehunter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Piotr Murach
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pastel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tty-box
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.7.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.7.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: tty-cursor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.7.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.7.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: tty-option
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.2.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.2.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: tty-reader
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.9.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.9.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: tty-screen
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.8.1
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.8.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.0'
|
125
|
+
description: Terminal mine hunting game.
|
126
|
+
email:
|
127
|
+
- piotr@piotrmurach.com
|
128
|
+
executables:
|
129
|
+
- minehunter
|
130
|
+
- minehunt
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files:
|
133
|
+
- README.md
|
134
|
+
- CHANGELOG.md
|
135
|
+
- LICENSE.txt
|
136
|
+
files:
|
137
|
+
- CHANGELOG.md
|
138
|
+
- LICENSE.txt
|
139
|
+
- README.md
|
140
|
+
- exe/minehunt
|
141
|
+
- exe/minehunter
|
142
|
+
- lib/minehunter.rb
|
143
|
+
- lib/minehunter/cli.rb
|
144
|
+
- lib/minehunter/field.rb
|
145
|
+
- lib/minehunter/game.rb
|
146
|
+
- lib/minehunter/grid.rb
|
147
|
+
- lib/minehunter/intro.rb
|
148
|
+
- lib/minehunter/version.rb
|
149
|
+
homepage: https://github.com/piotrmurach/minehunter
|
150
|
+
licenses:
|
151
|
+
- AGPL-3.0
|
152
|
+
metadata:
|
153
|
+
allowed_push_host: https://rubygems.org
|
154
|
+
bug_tracker_uri: https://github.com/piotrmurach/minehunter/issues
|
155
|
+
changelog_uri: https://github.com/piotrmurach/minehunter/blob/master/CHANGELOG.md
|
156
|
+
documentation_uri: https://www.rubydoc.info/gems/minehunter
|
157
|
+
homepage_uri: https://github.com/piotrmurach/minehunter
|
158
|
+
source_code_uri: https://github.com/piotrmurach/minehunter
|
159
|
+
post_install_message:
|
160
|
+
rdoc_options: []
|
161
|
+
require_paths:
|
162
|
+
- lib
|
163
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: 2.0.0
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0'
|
173
|
+
requirements: []
|
174
|
+
rubygems_version: 3.1.2
|
175
|
+
signing_key:
|
176
|
+
specification_version: 4
|
177
|
+
summary: Terminal mine hunting game.
|
178
|
+
test_files: []
|