/
ms_board.rb
175 lines (143 loc) · 3.39 KB
/
ms_board.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
class Board
attr_reader :bombs, :size, :start_time
def initialize( size = 9, bombs = 10)
@size = size
@bombs = bombs
@board = generate_board(size)
place_bombs(@board)
@start_time = Time.now
end
def process_command(command)
if command[0] == :f
toggle_flag(command[1])
elsif command[0] == :r
reveal_tile(command[1])
else
flag_click(command[1])
end
end
def tiles
@board.flatten
end
def won? #can flatten this
tiles.all? do |tile|
tile.is_bomb ? tile.state == :flagged : tile.state == :revealed
end
end
def lost? #can flatten this with method tiles (flattens)
tiles.any? do |tile|
tile.is_bomb && tile.state == :revealed
end
end
def reveal_tile(pos)
return if self[pos].state == :flagged || self[pos].state == :revealed
self[pos].reveal
if self[pos].bomb_count == 0
neighbors(pos).each { |coord| reveal_tile(coord)}
end
nil
end
def flag_click(pos)
return if [:flagged,:hidden].include?(self[pos].state)
adj_flags = neighbors(pos).select { |neighbor| self[neighbor].state == :flagged}.size
if adj_flags == self[pos].bomb_count
neighbors(pos).select { |neighbor| self[neighbor].state == :hidden}.each do |neighbor|
self.reveal_tile(neighbor)
end
end
end
def toggle_flag(pos)
self[pos].flag
nil
end
def render
print "# |"
@board.size.times {|i| print "#{i+1}|"}
puts
print "--+"
@board.size.times { print '-+'}
puts
@board.each_with_index do |row, index|
print "#{index+1} |"
row.each do |tile|
char = char_to_render(tile)
print char
print '|'
end
puts
print "--+"
@board.size.times { print '-+'}
puts
end
nil
end
def char_to_render(tile)
case tile.state
when :hidden
' '.on_black
when :flagged
'⚑'.colorize(:red)
when :revealed
if tile.is_bomb
'⚛'
elsif tile.bomb_count == 0
' '
else
"#{color_case(tile.bomb_count)}"
end
end
end
def color_case(number)
case number
when 1
number.to_s.colorize(:light_blue)
when 2
number.to_s.colorize(:green)
when 3
number.to_s.colorize(:light_red)
when 4
number.to_s.colorize(:blue)
when 5
number.to_s.colorize(:light_black)
when 6
number.to_s.colorize(:light_cyan)
when 7
number.to_s.colorize(:magenta)
when 8
number.to_s.colorize(:cyan)
end
end
protected
private
def generate_board(size)
board = Array.new(size) { Array.new(size) { Tile.new(self)} }
end
def place_bombs(board)
bombs_left = @bombs
until bombs_left == 0
row = rand(board.count)
col = rand(board.count)
pos = [row,col]
unless self[pos].is_bomb
self[pos].make_bomb
set_neighbor_bomb_counts(pos)
bombs_left -= 1
end
end
board
end
def set_neighbor_bomb_counts (pos)
neighbors(pos).each {|neighbor| self[neighbor].increase_bomb_count }
nil
end
def [] (pos)
@board[pos[0]][pos[1]]
end
def neighbors(pos)
deltas = [-1,0,1].product([-1,0,1]) - [[0,0]]
pot_neighbors = deltas.map {|delta| [pos[0] + delta[0], pos[1] + delta[1]]}
neighbors = pot_neighbors.reject do |neighbor|
neighbor.any? {|coord| !coord.between?(0, @board.size-1)}
end
end
end