mine-sweeper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []