mine-sweeper 0.0.1

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
+ SHA1:
3
+ metadata.gz: 3cfd499cad7b89c5d6494534c4db66233db79595
4
+ data.tar.gz: a9a393f72e5bf30c3c3b1ecf7be27738134f2498
5
+ SHA512:
6
+ metadata.gz: 0bb0db5ea31b6ee7df11abe9b2fef4a701b6dee305d0409d934997694814999e2ac555b5263fb03e55ba0b58828ae973363bd6f9e0d1c85813b64c9fb15bd178
7
+ data.tar.gz: 56fff36c0331077938b32900a1daf7e32d13b539a18f50228e3393f1a3ffc0a38e3f40d940fc35c27763c920cba80e2c5e737af72a6ac62e13e1635151749dcf
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mine-sweeper.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Dan Berger
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # MineSweeper
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'mine-sweeper'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install mine-sweeper
20
+
21
+ ## Usage
22
+
23
+ #### Launch Game
24
+
25
+ Play MineSweeper in pry or irb with only two commands:
26
+
27
+ $ require 'mine-sweeper'
28
+ $ MineSweeper::Game.new
29
+
30
+ #### Commands
31
+
32
+ Every turn you will be guided to select a row, a column and a command.
33
+
34
+ **'C'** to clear a space. This will not work if the coordinates are flagged.
35
+
36
+ **'A'** to auto-clear around the coordinates. This only works if
37
+ (1) the coordinates are already cleared and
38
+ (2) the number of adjacent flags matches the number of adjacent mines.
39
+
40
+ **'F'** to flag a space as a mine.
41
+
42
+ **'U'** to un-flag, or remove a flag.
43
+
44
+ The number next to the '⚑' symbol shows you how many flags are left to place."
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it ( https://github.com/[my-github-username]/mine-sweeper/fork )
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,19 @@
1
+ require "mine-sweeper/version"
2
+ require "mine-sweeper/cell"
3
+ require "mine-sweeper/minefield"
4
+ require "mine-sweeper/turn"
5
+ require "mine-sweeper/game_helper"
6
+
7
+ module MineSweeper
8
+ class Game
9
+ def initialize
10
+ @mines = Minefield.new
11
+ intro
12
+ loop do
13
+ print_game_board
14
+ take_a_turn
15
+ check_for_game_end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,66 @@
1
+ module MineSweeper
2
+
3
+ class Cell
4
+
5
+ attr_reader :mine, :cleared, :flagged, :adjacent_mines
6
+
7
+ def initialize
8
+ @mine = false
9
+ @cleared = false
10
+ @flagged = false
11
+ @adjacent_mines = 0
12
+ end
13
+
14
+ # This ends the game!
15
+ def exploded?
16
+ mine && cleared
17
+ end
18
+
19
+ def place_mine
20
+ @mine = true
21
+ end
22
+
23
+ def count_adjacent_mine
24
+ @adjacent_mines += 1
25
+ end
26
+
27
+ def clear
28
+ @cleared = true unless flagged
29
+ end
30
+
31
+ def flag
32
+ @flagged = true unless cleared
33
+ end
34
+
35
+ def unflag
36
+ @flagged = false
37
+ end
38
+
39
+ def to_s
40
+ if cleared
41
+ cleared_string_states
42
+ else
43
+ uncleared_string_states
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def cleared_string_states
50
+ return "✷" if exploded?
51
+ if adjacent_mines == 0
52
+ " "
53
+ else
54
+ adjacent_mines.to_s
55
+ end
56
+ end
57
+
58
+ def uncleared_string_states
59
+ if flagged
60
+ "⚑"
61
+ else
62
+ "█"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,48 @@
1
+ require_relative 'minefield'
2
+ require_relative 'turn'
3
+
4
+ module MineSweeper
5
+ class Game
6
+
7
+ private
8
+
9
+ def intro
10
+ print "welcome to\nMINESWEEPER\n\nCOMMANDS:\n'C' to clear\n'A' to auto-clear\n'F' to flag\n'U' to un-flag\nThe ⚑ shows you how many flags are left"
11
+ end
12
+
13
+ def print_game_board
14
+ puts "⚑ #{@mines.unflagged_mines}"
15
+ @mines.render
16
+ end
17
+
18
+ def take_a_turn
19
+ turn = Turn.new
20
+ @mines.take_turn(turn.message)
21
+ end
22
+
23
+ def check_for_game_end
24
+ game_over_sequence if @mines.game_over?
25
+ end
26
+
27
+ def game_over_sequence
28
+ if @mines.won?
29
+ winning_sequence
30
+ else
31
+ losing_sequence
32
+ end
33
+ exit
34
+ end
35
+
36
+ def winning_sequence
37
+ @mines.flag_remaining_mines
38
+ print_game_board
39
+ puts "CONGRATULATIONS!"
40
+ end
41
+
42
+ def losing_sequence
43
+ @mines.blow_up_board
44
+ print_game_board
45
+ puts "BOOM! YOU LOST."
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,208 @@
1
+ require_relative 'cell'
2
+
3
+ module MineSweeper
4
+
5
+ class Minefield
6
+
7
+ attr_reader :size, :field
8
+
9
+ def initialize(size = 10)
10
+ set_field_size(size)
11
+ create_field_with_attributes
12
+ end
13
+
14
+ def render
15
+ field.each do |row|
16
+ row.each { |cell| print cell }
17
+ print "\n"
18
+ end
19
+ nil
20
+ end
21
+
22
+ def take_turn(turn)
23
+ row = turn[:row]
24
+ column = turn[:column]
25
+ action = turn[:action]
26
+ take_action(row, column, action)
27
+ end
28
+
29
+ # GAME ENDING SCENARIOS
30
+
31
+ def lost?
32
+ field.flatten.any? { |cell| cell.exploded? }
33
+ end
34
+
35
+ def blow_up_board
36
+ field.flatten.each do |cell|
37
+ cell.clear if cell.mine
38
+ end
39
+ end
40
+
41
+ def won?
42
+ uncleared_cells == number_of_mines
43
+ end
44
+
45
+ def flag_remaining_mines
46
+ field.flatten.each do |cell|
47
+ cell.flag if cell.mine
48
+ end
49
+ end
50
+
51
+ def game_over?
52
+ lost? || won?
53
+ end
54
+
55
+ # PUBLIC BOARD INFORMATION
56
+
57
+ def number_of_mines
58
+ field.flatten.count { |cell| cell.mine }
59
+ end
60
+
61
+ def number_of_flags
62
+ field.flatten.count { |cell| cell.flagged }
63
+ end
64
+
65
+ def unflagged_mines
66
+ number_of_mines - number_of_flags
67
+ end
68
+
69
+ def uncleared_cells
70
+ field.flatten.count { |cell| !(cell.cleared) }
71
+ end
72
+
73
+ private
74
+
75
+ # INITIALIZE HELPERS
76
+
77
+ def set_field_size(size)
78
+ if size.class == Fixnum
79
+ @size = size
80
+ else
81
+ raise 'Please enter an integer.'
82
+ end
83
+ end
84
+
85
+ def create_field_with_attributes
86
+ @field = Array.new(size){ Array.new(size){ Cell.new } }
87
+ generate_mines
88
+ generate_adjacent_mine_counts
89
+ end
90
+
91
+ def mine_calculator
92
+ if size < 20
93
+ 9
94
+ else
95
+ 100
96
+ end
97
+ end
98
+
99
+ def generate_mines
100
+ mines_left = mine_calculator
101
+ until mines_left == 0
102
+ row = rand(size)
103
+ col = rand(size)
104
+ unless field[row][col].mine
105
+ field[row][col].place_mine
106
+ mines_left -= 1
107
+ end
108
+ end
109
+ end
110
+
111
+ def generate_adjacent_mine_counts
112
+ field.each_with_index do |row, row_num|
113
+ row.each_with_index do |cell, col_num|
114
+ if cell.mine
115
+ neighbor_cells(row_num,col_num).each do |neighbor|
116
+ neighbor.count_adjacent_mine
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ # TAKE TURN HELPERS
124
+
125
+ def take_action(row, column, action)
126
+ cell = field[row][column]
127
+ case action
128
+ when "A"
129
+ auto_clear(row,column)
130
+ when "C"
131
+ clear_with_zero_cascade(row,column)
132
+ when "F"
133
+ cell.flag if unflagged_mines > 0
134
+ when "U"
135
+ cell.unflag
136
+ end
137
+ end
138
+
139
+ # SMART CLEARING
140
+
141
+ def auto_clear(row,col)
142
+ if already_cleared?(row,col) && flags_match_mines?(row,col)
143
+ valid_neighbor_coordinates(row,col).each do |cell|
144
+ row,col = cell[0],cell[1]
145
+ clear_with_zero_cascade(row,col)
146
+ end
147
+ end
148
+ end
149
+
150
+ def clear_with_zero_cascade(target_row,target_column)
151
+ target_cell = field[target_row][target_column]
152
+ target_cell.clear
153
+ if target_cell.adjacent_mines == 0
154
+ valid_neighbor_coordinates(target_row,target_column).each do |cell|
155
+ row,col = cell[0],cell[1]
156
+ unless field[row][col].cleared
157
+ clear_with_zero_cascade(cell[0],cell[1])
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ def already_cleared?(row,col)
164
+ field[row][col].cleared
165
+ end
166
+
167
+ def flags_match_mines?(row,col)
168
+ count_nearby_flags(row,col) == field[row][col].adjacent_mines
169
+ end
170
+
171
+ def count_nearby_flags(row,col)
172
+ neighbor_cells(row,col).count { |cell| cell.flagged }
173
+ end
174
+
175
+ # FINDING NEIGHBOR CELLS HELPERS
176
+
177
+ def valid_rows(row)
178
+ rows = [row]
179
+ rows << (row - 1) unless row == 0
180
+ rows << (row + 1) unless row == (size - 1)
181
+ rows
182
+ end
183
+
184
+ def valid_cols(col)
185
+ cols = [col]
186
+ cols << (col - 1) unless col == 0
187
+ cols << (col + 1) unless col == (size - 1)
188
+ cols
189
+ end
190
+
191
+ def valid_neighbor_coordinates(row,col)
192
+ coords = valid_rows(row).each_with_object([]) do |row, nearby|
193
+ valid_cols(col).each do |col|
194
+ nearby << [row,col]
195
+ end
196
+ end
197
+ coords - [[row,col]]
198
+ end
199
+
200
+ def neighbor_cells(row,col)
201
+ valid_neighbor_coordinates(row,col).each_with_object([]) do |cell,neighbors|
202
+ neighbor_row = cell[0]
203
+ neighbor_col = cell[1]
204
+ neighbors << field[neighbor_row][neighbor_col]
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,36 @@
1
+ module MineSweeper
2
+
3
+ class Turn
4
+
5
+ attr_reader :action
6
+
7
+ def initialize
8
+ print "which row?\n > "
9
+ @row = gets.chomp.to_i
10
+ print "which column?\n > "
11
+ @column = gets.chomp.to_i
12
+ @action = "x"
13
+ until action_is_valid?
14
+ print "which action?\n > "
15
+ @action = gets.chomp.upcase
16
+ print "that is not a valid action" unless action_is_valid?
17
+ end
18
+ end
19
+
20
+ def message
21
+ { row: @row,
22
+ column: @column,
23
+ action: @action }
24
+ end
25
+
26
+ private
27
+
28
+ def action_is_valid?
29
+ valid_actions.include? action
30
+ end
31
+
32
+ def valid_actions
33
+ ["A","C","F","U"]
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module MineSweeper
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mine-sweeper/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mine-sweeper"
8
+ spec.version = MineSweeper::VERSION
9
+ spec.authors = ["Dan Berger"]
10
+ spec.email = ["dsberger@gmail.com"]
11
+ spec.summary = %q{A CLI Minesweeper clone}
12
+ spec.description = %q{Remember Minesweeper? It's like that only clunkier.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mine-sweeper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Dan Berger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Remember Minesweeper? It's like that only clunkier.
42
+ email:
43
+ - dsberger@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - lib/mine-sweeper.rb
54
+ - lib/mine-sweeper/cell.rb
55
+ - lib/mine-sweeper/game_helper.rb
56
+ - lib/mine-sweeper/minefield.rb
57
+ - lib/mine-sweeper/turn.rb
58
+ - lib/mine-sweeper/version.rb
59
+ - mine-sweeper.gemspec
60
+ homepage: ''
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.2.2
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: A CLI Minesweeper clone
84
+ test_files: []