the-swarm 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.yardopts +8 -0
  4. data/Gemfile +24 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +35 -0
  7. data/Rakefile +26 -0
  8. data/bin/swarm +11 -0
  9. data/docs/Swarm.html +296 -0
  10. data/docs/Swarm/Catalog.html +819 -0
  11. data/docs/Swarm/Console.html +1050 -0
  12. data/docs/Swarm/Console/Color.html +160 -0
  13. data/docs/Swarm/Console/NormalKey.html +410 -0
  14. data/docs/Swarm/Console/PopUp.html +236 -0
  15. data/docs/Swarm/Console/VIKey.html +410 -0
  16. data/docs/Swarm/Five.html +421 -0
  17. data/docs/Swarm/Four.html +389 -0
  18. data/docs/Swarm/Game.html +1197 -0
  19. data/docs/Swarm/Intro.html +419 -0
  20. data/docs/Swarm/Level.html +700 -0
  21. data/docs/Swarm/Map.html +2336 -0
  22. data/docs/Swarm/One.html +353 -0
  23. data/docs/Swarm/Three.html +383 -0
  24. data/docs/Swarm/Tile.html +3823 -0
  25. data/docs/Swarm/Two.html +355 -0
  26. data/docs/_index.html +313 -0
  27. data/docs/class_list.html +51 -0
  28. data/docs/css/common.css +1 -0
  29. data/docs/css/full_list.css +58 -0
  30. data/docs/css/style.css +492 -0
  31. data/docs/file.LICENSE.html +70 -0
  32. data/docs/file.README.html +101 -0
  33. data/docs/file_list.html +61 -0
  34. data/docs/frames.html +17 -0
  35. data/docs/index.html +101 -0
  36. data/docs/js/app.js +248 -0
  37. data/docs/js/full_list.js +216 -0
  38. data/docs/js/jquery.js +4 -0
  39. data/docs/method_list.html +1195 -0
  40. data/docs/top-level-namespace.html +110 -0
  41. data/lib/swarm.rb +60 -0
  42. data/lib/swarm/catalog.rb +71 -0
  43. data/lib/swarm/console.rb +165 -0
  44. data/lib/swarm/game.rb +173 -0
  45. data/lib/swarm/level.rb +58 -0
  46. data/lib/swarm/levels/five.rb +71 -0
  47. data/lib/swarm/levels/four.rb +55 -0
  48. data/lib/swarm/levels/intro.rb +36 -0
  49. data/lib/swarm/levels/one.rb +35 -0
  50. data/lib/swarm/levels/three.rb +51 -0
  51. data/lib/swarm/levels/two.rb +38 -0
  52. data/lib/swarm/map.rb +254 -0
  53. data/lib/swarm/tile.rb +149 -0
  54. data/lib/swarm/version.rb +5 -0
  55. data/swarm.gemspec +24 -0
  56. metadata +112 -0
@@ -0,0 +1,58 @@
1
+ module Swarm
2
+
3
+ class Level
4
+
5
+ def Level.each(&block)
6
+ [Intro, One, Two, Three, Four, Five].each { |level| block.call level.new }
7
+ end
8
+
9
+ def update
10
+ @map.update
11
+ end
12
+
13
+ def update!
14
+ @map.each(&:change!)
15
+ @map.update
16
+ end
17
+
18
+ def find_player
19
+ @player = (@player && @player.player?) ? @player : @map.find(&:player?)
20
+ end
21
+
22
+ def spawn_player
23
+ @map.center.player!
24
+ end
25
+
26
+ def move_player(direction)
27
+ move find_player, direction
28
+ end
29
+
30
+ def move(tile, direction)
31
+ case direction
32
+ when :north, :south, :west, :east
33
+ @map.move(tile, direction)
34
+ else
35
+ available = @map.available_moves(tile)
36
+ aggressive = @map.aggressive_moves(tile, direction)
37
+ aggressive &= available
38
+
39
+ if aggressive.any?
40
+ @map.move tile, aggressive.sample
41
+ elsif available.any?
42
+ @map.move tile, available.sample
43
+ end
44
+ end
45
+ end
46
+
47
+ def over?
48
+ !Catalog.select(*%i[worker soldier queen egg]).any?
49
+ end
50
+
51
+ def initialize
52
+ @map = Map.new(Console.width, Console.height, tile_width: 2)
53
+
54
+ @map.each &:empty!
55
+ @map.update
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,71 @@
1
+ module Swarm
2
+ class Five < Level
3
+
4
+ def play
5
+ promoted =
6
+ laid_egg =
7
+ hatched = false
8
+
9
+ Catalog.select(*%i[worker soldier queen egg]).each do |tile|
10
+ tile.age!
11
+
12
+ if tile.worker?
13
+ if !promoted && (tile.age % 15).zero?
14
+ tile.soldier!
15
+
16
+ promoted = true
17
+ else
18
+ move tile, @player
19
+ end
20
+ elsif tile.queen?
21
+ if !laid_egg && (tile.age % 10).zero?
22
+ move tile, @player
23
+ tile.egg!
24
+
25
+ laid_egg = true
26
+ else
27
+ move tile, @player
28
+ end
29
+ elsif tile.egg?
30
+ if !hatched && (tile.age % 5).zero?
31
+ tile.queen!
32
+
33
+ hatched = true
34
+ end
35
+ elsif tile.soldier?
36
+ move tile, @player
37
+ end
38
+ end
39
+
40
+ true
41
+ end
42
+
43
+
44
+ def show(players)
45
+ pause do
46
+ <<-POPUP.gsub(/[ ]{10}/, '') % Console.key_info
47
+
48
+ - LEVEL 5 -
49
+
50
+
51
+ Press %<pause>s to start.
52
+
53
+ (#{players} lives remaining)
54
+ POPUP
55
+ end
56
+ end
57
+
58
+ def setup
59
+ @map.each &:empty!
60
+
61
+ @map.spawn :dirt!, 30
62
+ @map.spawn :rock!, 5
63
+ @map.spawn :worker!, 0.5
64
+ @map.spawn :soldier!, 0.15
65
+ @map.spawn :queen!, 0.3
66
+ @map.spawn :egg!, 0.15
67
+
68
+ @map.center.player!
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,55 @@
1
+ module Swarm
2
+ class Four < Level
3
+
4
+ def play
5
+ promoted = false
6
+
7
+ Catalog.select(*%i[worker soldier queen]).each do |tile|
8
+ tile.age!
9
+
10
+ if tile.worker?
11
+ if !promoted && (tile.age % 15).zero?
12
+ tile.soldier!
13
+
14
+ promoted = true
15
+ else
16
+ move tile, @player
17
+ end
18
+ elsif tile.queen?
19
+ move tile, @player
20
+ elsif tile.soldier?
21
+ move tile, @player
22
+ end
23
+ end
24
+
25
+ true
26
+ end
27
+
28
+
29
+ def show(players)
30
+ pause do
31
+ <<-POPUP.gsub(/[ ]{10}/, '') % Console.key_info
32
+
33
+ - LEVEL 4 -
34
+
35
+
36
+ Press %<pause>s to start.
37
+
38
+ (#{players} lives remaining)
39
+ POPUP
40
+ end
41
+ end
42
+
43
+ def setup
44
+ @map.each &:empty!
45
+
46
+ @map.spawn :dirt!, 30
47
+ @map.spawn :rock!, 5
48
+ @map.spawn :worker!, 0.5
49
+ @map.spawn :soldier!, 0.15
50
+ @map.spawn :queen!, 0.3
51
+
52
+ @map.center.player!
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,36 @@
1
+ module Swarm
2
+ class Intro < Level
3
+ def play
4
+ @over = true
5
+ end
6
+
7
+ def show(*args)
8
+ @over = false
9
+ pause do <<-POPUP.gsub(/[ ]{10}/, '') % Console.key_info
10
+ Swarm Instructions
11
+ ----------------------------------------------
12
+ Movement:
13
+ north: %<north>s
14
+ south: %<south>s
15
+ west: %<west>s
16
+ east: %<east>s
17
+
18
+ Pause: %<pause>s
19
+ Quit: %<quit>s
20
+ ----------------------------------------------
21
+
22
+ Press %<pause>s to begin.
23
+ POPUP
24
+ end
25
+ end
26
+
27
+ def setup
28
+ @map.center.player!
29
+ end
30
+
31
+ def over?
32
+ @over
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,35 @@
1
+ module Swarm
2
+ class One < Level
3
+
4
+ def play
5
+ Catalog.select(:worker).each do |tile|
6
+ move tile, @player
7
+ end
8
+
9
+ true
10
+ end
11
+
12
+ def show(players)
13
+ pause do
14
+ <<-POPUP.gsub(/[ ]{10}/, '') % Console.key_info
15
+
16
+ - LEVEL 1 -
17
+
18
+ Press %<pause>s to start.
19
+
20
+ (#{players} lives remaining)
21
+ POPUP
22
+ end
23
+ end
24
+
25
+ def setup
26
+ @map.each &:empty!
27
+
28
+ @map.spawn :dirt!, 30
29
+ @map.spawn :rock!, 5
30
+ @map.spawn :worker!, 0.75
31
+
32
+ @map.center.player!
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,51 @@
1
+ module Swarm
2
+ class Three < Level
3
+
4
+ def play
5
+ promoted = false
6
+
7
+ Catalog.select(*%i[worker soldier]).each do |tile|
8
+ tile.age!
9
+
10
+ if tile.worker?
11
+ if !promoted && (tile.age % 15).zero?
12
+ tile.soldier!
13
+
14
+ promoted = true
15
+ else
16
+ move tile, @player
17
+ end
18
+ elsif tile.soldier?
19
+ move tile, @player
20
+ end
21
+ end
22
+
23
+ true
24
+ end
25
+
26
+ def show(players)
27
+ pause do
28
+ <<-POPUP.gsub(/[ ]{10}/, '') % Console.key_info
29
+
30
+ - LEVEL 3 -
31
+
32
+
33
+ Press %<pause>s to start.
34
+
35
+ (#{players} lives remaining)
36
+ POPUP
37
+ end
38
+ end
39
+
40
+ def setup
41
+ @map.each &:empty!
42
+
43
+ @map.spawn :dirt!, 30
44
+ @map.spawn :rock!, 5
45
+ @map.spawn :worker!, 0.5
46
+ @map.spawn :soldier!, 0.2
47
+
48
+ @map.center.player!
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ module Swarm
2
+ class Two < Level
3
+
4
+ def play
5
+ Catalog.select(*%i[worker soldier]).each do |tile|
6
+ move tile, @player
7
+ end
8
+
9
+ true
10
+ end
11
+
12
+
13
+ def show(players)
14
+ pause do
15
+ <<-POPUP.gsub(/[ ]{10}/, '') % Console.key_info
16
+
17
+ - LEVEL 2 -
18
+
19
+
20
+ Press %<pause>s to start.
21
+
22
+ (#{players} lives remaining)
23
+ POPUP
24
+ end
25
+ end
26
+
27
+ def setup
28
+ @map.each &:empty!
29
+
30
+ @map.spawn :dirt!, 30
31
+ @map.spawn :rock!, 5
32
+ @map.spawn :worker!, 0.5
33
+ @map.spawn :soldier!, 0.15
34
+
35
+ @map.center.player!
36
+ end
37
+ end
38
+ end
data/lib/swarm/map.rb ADDED
@@ -0,0 +1,254 @@
1
+ module Swarm
2
+
3
+ # @example The +Map+ Layout
4
+ # +-------------------------------------------+
5
+ # | |
6
+ # | [0,0] [1,0] [2,0] [3,0] [4,0] [5,0] [6,0] |
7
+ # | |
8
+ # | [0,1] [1,1] [2,1] [3,1] [4,1] [5,1] [6,1] |
9
+ # | |
10
+ # | [0,2] [1,2] [2,2] [3,2] [4,2] [5,2] [6,2] |
11
+ # | |
12
+ # | [0,3] [1,3] [2,3] [3,3] [4,3] [5,3] [6,3] |
13
+ # | |
14
+ # | [0,4] [1,4] [2,4] [3,4] [4,4] [5,4] [6,4] |
15
+ # | |
16
+ # | [0,5] [1,5] [2,5] [3,5] [4,5] [5,5] [6,5] |
17
+ # | |
18
+ # | [0,6] [1,6] [2,6] [3,6] [4,6] [5,6] [6,6] |
19
+ # | |
20
+ # +-------------------------------------------+
21
+ class Map
22
+
23
+ # @macro swarm.map.dimensions
24
+ # @return [Integer] the +$1+ of the +Map+.
25
+
26
+ # @macro swarm.map.dimensions
27
+ attr_reader :width
28
+
29
+ # @macro swarm.map.dimensions
30
+ attr_reader :height
31
+
32
+ # A 2 dimensional array of tiles.
33
+ def initialize(width, height, tile_width: 1, tile_height: 1)
34
+ @tiles = (0...height).step(tile_height).map.with_index do |console_y, map_y|
35
+ (0...width).step(tile_width).map.with_index do |console_x, map_x|
36
+ Tile.new map_x,
37
+ map_y,
38
+ [console_y, console_x] # reversed for Console::setpos
39
+ end
40
+ end
41
+
42
+ @height = height / tile_height
43
+ @width = width / tile_width
44
+ end
45
+
46
+ include Enumerable
47
+
48
+ def each(&block)
49
+ @tiles.each { |row| row.each &block }
50
+ end
51
+
52
+ # @return [Tile] a center tile
53
+ def center
54
+ @tiles[height / 2][width / 2]
55
+ end
56
+
57
+ # @return [Tile] a random tile
58
+ def sample
59
+ @tiles.sample.sample
60
+ end
61
+
62
+ # @param x [Integer]
63
+ # @param y [Integer]
64
+ # @return [Tile]
65
+ def [](x, y)
66
+ @tiles.fetch(y, Array.new)[x]
67
+ end
68
+
69
+ # @param x [Integer]
70
+ # @param y [Integer]
71
+ # @param tile [Tile]
72
+ # @return [Tile]
73
+ def []=(x, y, tile)
74
+ @tiles[y] ||= Array.new
75
+ @tiles[y][x] = tile
76
+ end
77
+
78
+ # @return [Tile] the last tile (highest +x+ and +y+ value)
79
+ def last
80
+ self[-1, -1]
81
+ end
82
+
83
+ def score(key)
84
+ Catalog.fetch :destroyed, key, 0
85
+ end
86
+
87
+ def spawn(flag, percent = 0.0)
88
+ total = ((percent.to_f / 100) * count)
89
+
90
+ select(&:empty?).sample(total.to_i).each &flag
91
+ end
92
+
93
+ # Move a +Tile+ on the +Map+. This movement action causes a chain
94
+ # reaction potentially moving (or killing) tiles down the map in that
95
+ # same direction.
96
+ #
97
+ # It does this by comparing each neighboring pair
98
+ # tile_1 -> tile_2
99
+ # tile_2 -> tile_3
100
+ # tile_3 -> tile_4
101
+ #
102
+ # And follows these rules
103
+ # * empty tiles don't transfer movement
104
+ # * enemies block enemies
105
+ # * players are killed when touching enemies
106
+ # * enemies get squished by players
107
+ # * players get squished by queens
108
+ # * rocks block everything
109
+ #
110
+ # @param tile [Tile]
111
+ # @param direction [Symbol] +:north+, +:south+, +:east+ or +:west+
112
+ # @return [Boolean] +true+ if the +tile+ is moved successfully, +false+ if it is not
113
+ def move(tile, direction)
114
+ tiles = send(direction, tile)
115
+ move = []
116
+
117
+ tiles.each_cons(2) do |this, that|
118
+ break if this.empty?
119
+ # break move.clear if (tile == this) && this.enemy? && that.empty?
120
+ break move.clear if this.enemy? && that.enemy?
121
+
122
+ break move.clear if this.egg? && that.enemy?
123
+ break move.clear if this.enemy? && that.egg?
124
+
125
+ break this.destroy! if this.player? && (that.enemy? || that.egg?)
126
+ break that.destroy! if (this.enemy? || this.egg?) && that.player?
127
+
128
+ if (this.worker? || this.queen? || this.egg?) && tile.player? && (that.dirt? || that.rock?)
129
+ this.destroy!
130
+ break
131
+ end
132
+
133
+ if this.soldier? && tile.player? && that.rock?
134
+ (neighbors(this) - neighbors(tile)).each &:worker!
135
+
136
+ this.destroy!
137
+ break
138
+ end
139
+
140
+ if this.player? && tile.queen? && !that.empty?
141
+ this.destroy!
142
+ break
143
+ end
144
+
145
+ break move.clear if that.rock?
146
+
147
+ move << [that, this.type, this.age]
148
+ end
149
+
150
+ return if move.empty?
151
+
152
+ tile.empty!
153
+ move.each { |t, type, age| t.send(type, age) }
154
+ end
155
+
156
+ # Find the closest route from +this+ to +that+ based only on position
157
+ # (ignoring all obstacles between +this+ and +that+).
158
+ #
159
+ # @param this [Tile] origin tile
160
+ # @param that [Tile] destination tile
161
+ # @return [Array<Symbol, ...>] +:north+, +:east+, +:west+ and/or +:south+
162
+ def aggressive_moves(this, that)
163
+ directions = []
164
+ directions << :north if (this.y > that.y) && ((this.y - that.y) < 10)
165
+ directions << :south if (that.y > this.y) && ((that.y - this.y) < 10)
166
+ directions << :west if (this.x > that.x) && ((this.x - that.x) < 10)
167
+ directions << :east if (that.x > this.x) && ((that.x - this.x) < 10)
168
+ directions
169
+ end
170
+
171
+ # Find "open" (non-blocking) moves for a +worker+ or +soldier+.
172
+ #
173
+ # @see {Map#north}
174
+ # @see {Map#south}
175
+ # @see {Map#east}
176
+ # @see {Map#west}
177
+ #
178
+ # @param tile [Tile] a +worker+ or +soldier+
179
+ # @return [Array<Symbol, ...>] +:north+, +:east+, +:west+ and/or +:south+
180
+ def available_moves(tile)
181
+ %i(north south east west).select do |direction|
182
+ _, neighbor, _ = send(direction, tile)
183
+
184
+ next if neighbor.nil?
185
+
186
+ neighbor.player? || neighbor.empty? || (tile.queen? && neighbor.dirt?)
187
+ end
188
+ end
189
+
190
+ def neighbors(tile)
191
+ [
192
+ [ 0, 1],
193
+ [ 1, 0],
194
+ [ 0,-1],
195
+ [-1, 0],
196
+ # [ 0, 0], <- self
197
+ [ 1, 1],
198
+ [-1,-1],
199
+ [ 1,-1],
200
+ [-1, 1]
201
+ ].map do |x, y|
202
+ self[tile.x + x, tile.y + y]
203
+ end
204
+ end
205
+
206
+ def update
207
+ Console.update Catalog.flush(:changed)
208
+ end
209
+
210
+ # @macro swarm.map.direction
211
+ # @see Map#column
212
+ # @see Map#row
213
+ # @param tile [Tile]
214
+ # @return [Array<Tile, ...>] +column+ or +row+ of tiles starting with +tile+ headed $0 until the end of the map
215
+
216
+ # @macro swarm.map.direction
217
+ def north(tile)
218
+ tiles = column(tile).reverse
219
+ tiles.rotate(tiles.index tile)
220
+ end
221
+
222
+ # @macro swarm.map.direction
223
+ def south(tile)
224
+ tiles = column(tile)
225
+ tiles.rotate(tiles.index tile)
226
+ end
227
+
228
+ # @macro swarm.map.direction
229
+ def east(tile)
230
+ tiles = row(tile)
231
+ tiles.rotate(tiles.index tile)
232
+ end
233
+
234
+ # @macro swarm.map.direction
235
+ def west(tile)
236
+ tiles = row(tile).reverse
237
+ tiles.rotate(tiles.index tile)
238
+ end
239
+
240
+ # @macro swarm.map.axis
241
+ # @param tile [Tile]
242
+ # @return [Array<Tile, ...>] the $0 of tiles that the +tile+ resides on
243
+
244
+ # @macro swarm.map.axis
245
+ def row(tile)
246
+ @tiles[tile.y]
247
+ end
248
+
249
+ # @macro swarm.map.axis
250
+ def column(tile)
251
+ @tiles.map { |row| row[tile.x] }
252
+ end
253
+ end
254
+ end