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,110 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; &quot;Swarm&quot; Source Code Documentation
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ pathId = "";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+
41
+
42
+ <span class="title">Top Level Namespace</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Top Level Namespace
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ </div>
80
+
81
+ <h2>Defined Under Namespace</h2>
82
+ <p class="children">
83
+
84
+
85
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="Swarm.html" title="Swarm (module)">Swarm</a></span>
86
+
87
+
88
+
89
+
90
+ </p>
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ </div>
101
+
102
+ <div id="footer">
103
+ Generated on Wed Aug 30 07:17:58 2017 by
104
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
+ 0.9.9 (ruby-2.4.1).
106
+ </div>
107
+
108
+ </div>
109
+ </body>
110
+ </html>
data/lib/swarm.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'curses'
2
+ require 'io/console'
3
+
4
+ $LOAD_PATH.unshift __dir__
5
+
6
+ require 'swarm/console'
7
+ require 'swarm/game'
8
+ require 'swarm/map'
9
+ require 'swarm/catalog'
10
+
11
+ require 'swarm/tile'
12
+
13
+ require 'swarm/level'
14
+ require 'swarm/levels/intro'
15
+ require 'swarm/levels/one'
16
+ require 'swarm/levels/two'
17
+ require 'swarm/levels/three'
18
+ require 'swarm/levels/four'
19
+ require 'swarm/levels/five'
20
+
21
+ require 'swarm/version'
22
+
23
+ # Namespace for classes and modules of the +Swarm+ application.
24
+ module Swarm
25
+
26
+ # Initialize the console, launch the game, play the game, print the
27
+ # scoreboard and exit to the shell.
28
+ #
29
+ # @return [Kernel#exit] exit to shell with success/failure status code
30
+ def self.start
31
+ Signal.trap('INT') { game.stop } # Ctrl-C stops the game gracefully
32
+
33
+ game = Game.new
34
+
35
+ Console.open do
36
+
37
+ game.start
38
+
39
+ Level.each do |level|
40
+ next if game.over?
41
+
42
+ game.setup! level
43
+ game.show!
44
+ game.play! until game.over? || level.over?
45
+ end
46
+
47
+ game.stop
48
+ end
49
+
50
+ puts game
51
+
52
+ exit true # success!
53
+ rescue => error
54
+ Console.close
55
+
56
+ warn 'Swarm encountered an unhandled error'
57
+ warn error.message, *error.backtrace
58
+ exit false # failure!
59
+ end
60
+ end
@@ -0,0 +1,71 @@
1
+ module Swarm
2
+ class Catalog
3
+
4
+ class << self
5
+
6
+ def count(key)
7
+ instance.synchronize { |categories| categories[key].count }
8
+ end
9
+
10
+ def fetch(key, value, default = nil)
11
+ instance.synchronize { |categories| categories[key].fetch value, default }
12
+ end
13
+
14
+ def increment(key, value)
15
+ instance.synchronize do |categories|
16
+ categories[key][value] ||= 0
17
+ categories[key][value] += 1
18
+ end
19
+ end
20
+
21
+ def store(key, value)
22
+ instance.synchronize { |categories| categories[key].store value, true }
23
+ end
24
+
25
+ def delete(key, value)
26
+ instance.synchronize { |categories| categories[key].delete value }
27
+ end
28
+
29
+ def flush(key, fetch: :keys)
30
+ instance.synchronize do |categories|
31
+ result = categories.delete(key)
32
+ result ||= Hash.new
33
+ result.send fetch
34
+ end
35
+ end
36
+
37
+ def select(*keys, fetch: :keys)
38
+ instance.synchronize do |categories|
39
+ keys.flat_map { |key| categories[key].send fetch }
40
+ end
41
+ end
42
+
43
+ def clear(key)
44
+ instance.synchronize { |categories| categories[key].clear }
45
+ end
46
+
47
+ def instance
48
+ @instance ||= new
49
+ end
50
+
51
+ private :new,
52
+ :instance
53
+ end
54
+
55
+ def synchronize
56
+ yield categories
57
+ # transaction.synchronize { yield categories }
58
+ end
59
+
60
+ private
61
+
62
+ def transaction
63
+ @transaction ||= Mutex.new
64
+ end
65
+
66
+ def categories
67
+ @categories ||= Hash.new { |h, k| h[k] = Hash.new }
68
+ end
69
+ end
70
+ end
71
+
@@ -0,0 +1,165 @@
1
+ module Swarm
2
+
3
+ module Console
4
+
5
+ module Color
6
+ BLACK = 234
7
+ SILVER = 240
8
+ WHITE = 248
9
+ GREY = 237
10
+ RED = 167
11
+ PURPLE = 104
12
+ GREEN = 120
13
+ BLUE = 39
14
+ YELLOW = 186
15
+ end
16
+
17
+ module NormalKey
18
+ def up; @up ||= Curses::Key::UP end
19
+ def down; @down ||= Curses::Key::DOWN end
20
+ def left; @left ||= Curses::Key::LEFT end
21
+ def right; @right ||= Curses::Key::RIGHT end
22
+
23
+ def pause; @pause ||= ?\s.freeze end
24
+ def quit; @quit ||= ?q.freeze end
25
+ end
26
+
27
+ module VIKey
28
+ def up; @up ||= ?k.freeze end
29
+ def down; @down ||= ?j.freeze end
30
+ def left; @left ||= ?h.freeze end
31
+ def right; @right ||= ?l.freeze end
32
+
33
+ def pause; @pause ||= ?\s.freeze end
34
+ def quit; @quit ||= ?q.freeze end
35
+ end
36
+
37
+ include Curses
38
+ include Color
39
+
40
+ class PopUp < Window
41
+
42
+ def initialize(message, indent: 2)
43
+ lines = message.split(?\n)
44
+ height = 2 + lines.count
45
+ max = lines.map(&:length).max
46
+ width = max + (indent*2)
47
+
48
+ super height,
49
+ width,
50
+ (Console.height/2) - (height/2),
51
+ (Console.width/2) - (max/2) - indent
52
+
53
+ box ?|, ?-, ?+
54
+
55
+ lines.each.with_index do |line, index|
56
+ setpos (index + 1), 2
57
+ addstr line.center(max)
58
+ end
59
+ end
60
+ end
61
+
62
+ attr_reader :key_info
63
+
64
+ extend self
65
+
66
+ def init_keys
67
+ nonl
68
+ noecho # don't echo keypresses
69
+ stdscr.nodelay = -1 # don't wait for input, just listen for it
70
+ stdscr.keypad true
71
+
72
+ @key_info = {pause: '<SPACEBAR>', quit: ?q}
73
+
74
+ if ENV.fetch('SWARM', 'VIM_MODE') == 'easy'
75
+ extend NormalKey
76
+
77
+ @key_info.merge! north: '<UP>', south: '<DOWN>', west: '<LEFT>', east: '<RIGHT>'
78
+ else
79
+ extend VIKey
80
+
81
+ @key_info.merge! north: ?k, south: ?j, west: ?h, east: ?l
82
+ end
83
+ end
84
+
85
+ def init_colors
86
+ start_color
87
+ use_default_colors
88
+
89
+ #id #fg #bg
90
+ init_pair BLACK, BLACK, BLACK
91
+ init_pair SILVER, SILVER, SILVER
92
+ init_pair WHITE, WHITE, WHITE
93
+ init_pair GREY, GREY, GREY
94
+ init_pair RED, RED, BLACK
95
+ init_pair PURPLE, PURPLE, BLACK
96
+ init_pair GREEN, GREEN, BLACK
97
+ init_pair BLUE, BLUE, BLACK
98
+ init_pair YELLOW, YELLOW, BLACK
99
+ end
100
+
101
+ # @yieldparam command [String] yields +command+ on keypress
102
+ def keypress
103
+ command = getch and yield(command)
104
+ end
105
+
106
+ # @macro swarm.console.dimensions
107
+ # @return [Integer] +$0+ of console
108
+
109
+ # @macro swarm.console.dimensions
110
+ def width
111
+ @width ||= cols
112
+ end
113
+
114
+ # @macro swarm.console.dimensions
115
+ def height
116
+ @height ||= lines
117
+ end
118
+
119
+ # Redraw all +tiles+ given and refresh the console
120
+ # @param tiles [Array<Tile, ...>]
121
+ def update(tiles)
122
+ tiles.each &method(:draw)
123
+ refresh
124
+ end
125
+
126
+ # @param color [Integer] 0 to 8
127
+ # @param x [Integer]
128
+ # @param y [Integer]
129
+ def draw(tile)
130
+ setpos *tile.location
131
+
132
+ attron(color_pair tile.color) { addstr tile.icon }
133
+ end
134
+
135
+ def wipe
136
+ clear
137
+ refresh
138
+ end
139
+
140
+ # Turn line buffering on and close the +Curses+ session.
141
+ def close
142
+ return if closed?
143
+
144
+ nocbreak
145
+ close_screen
146
+ end
147
+
148
+ # Initialize the +Curses+ environment, configure it and then clean up
149
+ # when the caller is done. 9 different bg/fg pairs are initialized for
150
+ # the colored +Tile+ squares (0-8): (black, red, green, yellow, blue,
151
+ # magenta, cyan, white, grey)
152
+ def open
153
+ init_screen
154
+ init_colors
155
+ init_keys
156
+
157
+ curs_set 0 # hide the cursor
158
+ cbreak # turn off line buffering
159
+
160
+ yield
161
+
162
+ close
163
+ end
164
+ end
165
+ end
data/lib/swarm/game.rb ADDED
@@ -0,0 +1,173 @@
1
+ module Swarm
2
+
3
+ class Game
4
+
5
+ REFRESH_RATE = 0.05 # This seems to keep the laptop under 750°.
6
+
7
+ def play!
8
+ turn = Time.now.to_f
9
+
10
+ @player = @level.find_player
11
+ @player ||= spawn_player
12
+
13
+ listen_for_keypress
14
+
15
+ unless @popup
16
+ if (turn - @turn) >= @rate
17
+
18
+ @level.play
19
+
20
+ @turn = turn
21
+ end
22
+
23
+ @level.update
24
+ end
25
+
26
+ sleep REFRESH_RATE
27
+ end
28
+
29
+ # Set the "start game" flag.
30
+ #
31
+ # @return [true]
32
+ def start
33
+ @status = true
34
+
35
+ true
36
+ end
37
+
38
+ # Set the "stop game" flag.
39
+ #
40
+ # @return [true]
41
+ def stop
42
+ @status = false
43
+
44
+ true
45
+ end
46
+
47
+ # @return [Boolean]
48
+ def stopped?
49
+ !@status
50
+ end
51
+
52
+ # A game is "over" when any of these conditions are met
53
+ #
54
+ # - the game has been stopped via {Game#stop}
55
+ # - all of the player's lives are gone
56
+ # - all of the invaders are dead
57
+ #
58
+ # @return [Boolean]
59
+ def over?
60
+ stopped? || @players.zero?
61
+ end
62
+
63
+ # @return [String]
64
+ def to_s
65
+ <<-TO_S.gsub(/^[ ]{8}/, '') % statistics
66
+ GAME OVER!
67
+
68
+ + %<eggs>4i x oo (eggs) squished (x20)
69
+ + %<workers>4i x ├┤ (workers) killed (x20)
70
+ + %<soldiers>4i x ╟╢ (soldiers) crushed (x50)
71
+ + %<queens>4i x ╬╬ (queens) defeated (x50)
72
+
73
+ - %<deaths>4i x ◄▶ (players) destroyed (x100)
74
+ -------------------------------
75
+ %<points>6i points
76
+ TO_S
77
+ end
78
+
79
+ def pause
80
+ if @popup
81
+ @popup.close
82
+ @popup = false
83
+
84
+ @level.update!
85
+ else
86
+ @popup = Console::PopUp.new(yield)
87
+ @popup.refresh
88
+ end
89
+ end
90
+
91
+ def setup!(level)
92
+ @level = level
93
+ @level.define_singleton_method(:pause, &method(:pause))
94
+
95
+ @popup = false
96
+
97
+ @status = true
98
+ @turn = Time.now.to_f
99
+
100
+ @level.setup
101
+ end
102
+
103
+ def show!
104
+ @level.update # update the level and refresh the console before displaying anything
105
+
106
+ @level.show(@players)
107
+ end
108
+
109
+ private
110
+
111
+ # @return [Hash]
112
+ def statistics
113
+ Hash.new(0).tap do |stats|
114
+ stats[:eggs] += Catalog.fetch(:destroyed, :egg, 0)
115
+ stats[:workers] += Catalog.fetch(:destroyed, :worker, 0)
116
+ stats[:soldiers] += Catalog.fetch(:destroyed, :soldier, 0)
117
+ stats[:queens] += Catalog.fetch(:destroyed, :queen, 0)
118
+ stats[:deaths] += Catalog.fetch(:destroyed, :player, 0)
119
+ stats[:points] +=
120
+ (stats[:eggs] * 20) +
121
+ (stats[:workers] * 20) +
122
+ (stats[:soldiers] * 50) +
123
+ (stats[:queens] * 50) -
124
+ (stats[:deaths] * 100)
125
+ end
126
+ end
127
+
128
+ # # @return [Tile]
129
+ # def find_or_spawn_player
130
+ # find_player || spawn_player
131
+ # end
132
+
133
+ # def find_player
134
+ # @player = @level.find_player
135
+ # end
136
+
137
+ def spawn_player
138
+ @players -= 1
139
+
140
+ pause do
141
+ <<-POPUP.gsub(/[ ]{10}/, '') % Console.key_info
142
+ YOU HAVE BEEN KILLED!
143
+ ----------------------------------------------
144
+ Press %<pause>s to continue.
145
+
146
+ (#@players lives remaining)
147
+ POPUP
148
+ end
149
+
150
+ @player = @level.spawn_player
151
+ end
152
+
153
+ def listen_for_keypress
154
+ Console.keypress do |command|
155
+ case command
156
+ when Console.up; @popup || @level.move_player(:north)
157
+ when Console.down; @popup || @level.move_player(:south)
158
+ when Console.left; @popup || @level.move_player(:west)
159
+ when Console.right; @popup || @level.move_player(:east)
160
+ when Console.pause; pause { 'PAUSED' }
161
+ when Console.quit; stop
162
+ end
163
+ STDIN.iflush # prevent command queueing
164
+ end
165
+ end
166
+
167
+ def initialize
168
+ @players = 5
169
+
170
+ @rate = 2.0
171
+ end
172
+ end
173
+ end