rtanque 0.0.2

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.
Files changed (130) hide show
  1. data/.gitignore +22 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +34 -0
  4. data/.travis.yml +11 -0
  5. data/.yardopts +4 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.ci +6 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +120 -0
  10. data/Rakefile +1 -0
  11. data/bin/rtanque +108 -0
  12. data/lib/rtanque.rb +40 -0
  13. data/lib/rtanque/arena.rb +8 -0
  14. data/lib/rtanque/bot.rb +117 -0
  15. data/lib/rtanque/bot/brain.rb +50 -0
  16. data/lib/rtanque/bot/brain_helper.rb +23 -0
  17. data/lib/rtanque/bot/command.rb +23 -0
  18. data/lib/rtanque/bot/radar.rb +54 -0
  19. data/lib/rtanque/bot/sensors.rb +33 -0
  20. data/lib/rtanque/bot/turret.rb +14 -0
  21. data/lib/rtanque/configuration.rb +46 -0
  22. data/lib/rtanque/explosion.rb +23 -0
  23. data/lib/rtanque/gui.rb +24 -0
  24. data/lib/rtanque/gui/bot.rb +42 -0
  25. data/lib/rtanque/gui/draw_group.rb +30 -0
  26. data/lib/rtanque/gui/explosion.rb +25 -0
  27. data/lib/rtanque/gui/shell.rb +20 -0
  28. data/lib/rtanque/gui/window.rb +51 -0
  29. data/lib/rtanque/heading.rb +172 -0
  30. data/lib/rtanque/match.rb +67 -0
  31. data/lib/rtanque/match/tick_group.rb +50 -0
  32. data/lib/rtanque/movable.rb +51 -0
  33. data/lib/rtanque/normalized_attr.rb +69 -0
  34. data/lib/rtanque/point.rb +140 -0
  35. data/lib/rtanque/runner.rb +88 -0
  36. data/lib/rtanque/shell.rb +40 -0
  37. data/lib/rtanque/version.rb +3 -0
  38. data/resources/images/body.png +0 -0
  39. data/resources/images/bullet.png +0 -0
  40. data/resources/images/explosions/explosion2-1.png +0 -0
  41. data/resources/images/explosions/explosion2-10.png +0 -0
  42. data/resources/images/explosions/explosion2-11.png +0 -0
  43. data/resources/images/explosions/explosion2-12.png +0 -0
  44. data/resources/images/explosions/explosion2-13.png +0 -0
  45. data/resources/images/explosions/explosion2-14.png +0 -0
  46. data/resources/images/explosions/explosion2-15.png +0 -0
  47. data/resources/images/explosions/explosion2-16.png +0 -0
  48. data/resources/images/explosions/explosion2-17.png +0 -0
  49. data/resources/images/explosions/explosion2-18.png +0 -0
  50. data/resources/images/explosions/explosion2-19.png +0 -0
  51. data/resources/images/explosions/explosion2-2.png +0 -0
  52. data/resources/images/explosions/explosion2-20.png +0 -0
  53. data/resources/images/explosions/explosion2-21.png +0 -0
  54. data/resources/images/explosions/explosion2-22.png +0 -0
  55. data/resources/images/explosions/explosion2-23.png +0 -0
  56. data/resources/images/explosions/explosion2-24.png +0 -0
  57. data/resources/images/explosions/explosion2-25.png +0 -0
  58. data/resources/images/explosions/explosion2-26.png +0 -0
  59. data/resources/images/explosions/explosion2-27.png +0 -0
  60. data/resources/images/explosions/explosion2-28.png +0 -0
  61. data/resources/images/explosions/explosion2-29.png +0 -0
  62. data/resources/images/explosions/explosion2-3.png +0 -0
  63. data/resources/images/explosions/explosion2-30.png +0 -0
  64. data/resources/images/explosions/explosion2-31.png +0 -0
  65. data/resources/images/explosions/explosion2-32.png +0 -0
  66. data/resources/images/explosions/explosion2-33.png +0 -0
  67. data/resources/images/explosions/explosion2-34.png +0 -0
  68. data/resources/images/explosions/explosion2-35.png +0 -0
  69. data/resources/images/explosions/explosion2-36.png +0 -0
  70. data/resources/images/explosions/explosion2-37.png +0 -0
  71. data/resources/images/explosions/explosion2-38.png +0 -0
  72. data/resources/images/explosions/explosion2-39.png +0 -0
  73. data/resources/images/explosions/explosion2-4.png +0 -0
  74. data/resources/images/explosions/explosion2-40.png +0 -0
  75. data/resources/images/explosions/explosion2-41.png +0 -0
  76. data/resources/images/explosions/explosion2-42.png +0 -0
  77. data/resources/images/explosions/explosion2-43.png +0 -0
  78. data/resources/images/explosions/explosion2-44.png +0 -0
  79. data/resources/images/explosions/explosion2-45.png +0 -0
  80. data/resources/images/explosions/explosion2-46.png +0 -0
  81. data/resources/images/explosions/explosion2-47.png +0 -0
  82. data/resources/images/explosions/explosion2-48.png +0 -0
  83. data/resources/images/explosions/explosion2-49.png +0 -0
  84. data/resources/images/explosions/explosion2-5.png +0 -0
  85. data/resources/images/explosions/explosion2-50.png +0 -0
  86. data/resources/images/explosions/explosion2-51.png +0 -0
  87. data/resources/images/explosions/explosion2-52.png +0 -0
  88. data/resources/images/explosions/explosion2-53.png +0 -0
  89. data/resources/images/explosions/explosion2-54.png +0 -0
  90. data/resources/images/explosions/explosion2-55.png +0 -0
  91. data/resources/images/explosions/explosion2-56.png +0 -0
  92. data/resources/images/explosions/explosion2-57.png +0 -0
  93. data/resources/images/explosions/explosion2-58.png +0 -0
  94. data/resources/images/explosions/explosion2-59.png +0 -0
  95. data/resources/images/explosions/explosion2-6.png +0 -0
  96. data/resources/images/explosions/explosion2-60.png +0 -0
  97. data/resources/images/explosions/explosion2-61.png +0 -0
  98. data/resources/images/explosions/explosion2-62.png +0 -0
  99. data/resources/images/explosions/explosion2-63.png +0 -0
  100. data/resources/images/explosions/explosion2-64.png +0 -0
  101. data/resources/images/explosions/explosion2-65.png +0 -0
  102. data/resources/images/explosions/explosion2-66.png +0 -0
  103. data/resources/images/explosions/explosion2-67.png +0 -0
  104. data/resources/images/explosions/explosion2-68.png +0 -0
  105. data/resources/images/explosions/explosion2-69.png +0 -0
  106. data/resources/images/explosions/explosion2-7.png +0 -0
  107. data/resources/images/explosions/explosion2-70.png +0 -0
  108. data/resources/images/explosions/explosion2-71.png +0 -0
  109. data/resources/images/explosions/explosion2-8.png +0 -0
  110. data/resources/images/explosions/explosion2-9.png +0 -0
  111. data/resources/images/grass.png +0 -0
  112. data/resources/images/radar.png +0 -0
  113. data/resources/images/turret.png +0 -0
  114. data/rtanque.gemspec +33 -0
  115. data/sample_bots/camper.rb +79 -0
  116. data/sample_bots/keyboard.rb +50 -0
  117. data/sample_bots/seek_and_destroy.rb +51 -0
  118. data/screenshots/battle_1.png +0 -0
  119. data/screenshots/battle_2.png +0 -0
  120. data/spec/rtanque/bot_spec.rb +239 -0
  121. data/spec/rtanque/heading_spec.rb +279 -0
  122. data/spec/rtanque/match_spec.rb +36 -0
  123. data/spec/rtanque/normalized_attr_spec.rb +90 -0
  124. data/spec/rtanque/point_spec.rb +196 -0
  125. data/spec/rtanque/radar_spec.rb +87 -0
  126. data/spec/rtanque/shell_spec.rb +35 -0
  127. data/spec/rtanque_spec.rb +6 -0
  128. data/spec/spec_helper.rb +51 -0
  129. data/templates/bot.erb +11 -0
  130. metadata +310 -0
@@ -0,0 +1,50 @@
1
+ module RTanque
2
+ class Bot
3
+ # Commands the associated {RTanque::Bot}. This class should be inherited from and {NAME} and {#tick!} overridden.
4
+ #
5
+ # See {RTanque::Bot::BrainHelper} for a useful mixin
6
+ #
7
+ # Sample bots:
8
+ #
9
+ # * {file:sample_bots/seek_and_destroy.rb SeekAndDestroy}
10
+ # * {file:sample_bots/camper.rb Camper}
11
+ # * {file:sample_bots/keyboard.rb Keyboard} Special bot controlled by the keyboard
12
+ #
13
+ class Brain
14
+ # Bot's display name
15
+ # @!parse NAME = 'bot name'
16
+
17
+ # @!attribute [r] sensors
18
+ # @return [RTanque::Bot::Sensors]
19
+ # @!attribute [r] command
20
+ # @return [RTanque::Bot::Command]
21
+ attr_accessor :sensors, :command
22
+ # @return [RTanque::Arena]
23
+ attr_reader :arena
24
+
25
+ # @!visibility private
26
+ def initialize(arena)
27
+ @arena = arena
28
+ end
29
+
30
+ # @!visibility private
31
+ def tick(sensors)
32
+ self.sensors = sensors
33
+ RTanque::Bot::Command.new.tap do |empty_command|
34
+ self.command = empty_command
35
+ self.tick!
36
+ end
37
+ end
38
+
39
+ # Main logic goes here
40
+ #
41
+ # Get input from {#sensors}. See {RTanque::Bot::Sensors}
42
+ #
43
+ # Give output to {#command}. See {RTanque::Bot::Command}
44
+ # @abstract
45
+ def tick!
46
+ # Sweet bot logic
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ module RTanque
2
+ class Bot
3
+ # Some helpful constants and methods for use as mixin in {RTanque::Bot::Brain}
4
+ module BrainHelper
5
+ BOT_RADIUS = Bot::RADIUS
6
+ MAX_FIRE_POWER = Bot::MAX_FIRE_POWER
7
+ MIN_FIRE_POWER = Bot::MIN_FIRE_POWER
8
+ MAX_HEALTH = Bot::MAX_HEALTH
9
+ MAX_BOT_SPEED = Bot::MAX_SPEED
10
+ MAX_BOT_ROTATION = Configuration.bot.turn_step
11
+ MAX_TURRET_ROTATION = Configuration.turret.turn_step
12
+ MAX_RADAR_ROTATION = Configuration.radar.turn_step
13
+
14
+ # Run block every 'num_of_ticks'
15
+ # @param [Integer] num_of_ticks tick interval at which to execute block
16
+ # @yield
17
+ # @return [void]
18
+ def at_tick_interval(num_of_ticks)
19
+ yield if sensors.ticks % num_of_ticks == 0
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module RTanque
2
+ class Bot
3
+ # Command provide output from the {RTanque::Bot::Brain} about the current state of the {RTanque::Match}
4
+ #
5
+ # They are made available to {RTanque::Bot::Brain} via {RTanque::Bot::Brain#command}
6
+ #
7
+ # All values are bound. Setting an out-of-bounds value will result in it being set to the max/min allowed value.
8
+ #
9
+ # @attr_writer [Float] speed
10
+ # @attr_writer [Float, RTanque::Heading] heading
11
+ # @attr_writer [Float, RTanque::Heading] radar_heading
12
+ # @attr_writer [Float, RTanque::Heading] turret_heading
13
+ # @attr_writer [Float, nil] fire_power sets firing power. Setting to nil will stop firing. See {#fire}
14
+ #
15
+ # @param [Float] power alias to {#fire_power=}
16
+ # @!method fire(power)
17
+ Command = Struct.new(:speed, :heading, :radar_heading, :turret_heading, :fire_power) do
18
+ def fire(power = 3)
19
+ self.fire_power = power
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,54 @@
1
+ module RTanque
2
+ class Bot
3
+ class Radar
4
+ include Enumerable
5
+ include Movable
6
+ extend NormalizedAttr
7
+ VISION_RANGE = Configuration.radar.vision
8
+ attr_normalized(:heading, Heading::FULL_RANGE, Configuration.radar.turn_step)
9
+
10
+ # A Reflection is the information obtained for a bot detected by {RTanque::Bot::Radar}
11
+ #
12
+ # @attr_reader [RTanque::Heading] heading
13
+ # @attr_reader [Float] distance
14
+ # @attr_reader [String] name
15
+ Reflection = Struct.new(:heading, :distance, :name) do
16
+ def self.new_from_points(from_position, to_position, &tap)
17
+ self.new(from_position.heading(to_position), from_position.distance(to_position)).tap(&tap)
18
+ end
19
+ end
20
+
21
+ def initialize(bot, heading)
22
+ @bot = bot
23
+ @heading = heading
24
+ @reflections = []
25
+ end
26
+
27
+ def position
28
+ @bot.position
29
+ end
30
+
31
+ def each(&block)
32
+ @reflections.each(&block)
33
+ end
34
+
35
+ def empty?
36
+ self.count == 0
37
+ end
38
+
39
+ def scan(bots)
40
+ @reflections.clear
41
+ bots.each do |other_bot|
42
+ if self.can_detect?(other_bot)
43
+ @reflections << Reflection.new_from_points(self.position, other_bot.position) { |reflection| reflection.name = other_bot.name }
44
+ end
45
+ end
46
+ self
47
+ end
48
+
49
+ def can_detect?(other_bot)
50
+ VISION_RANGE.include?(Heading.delta_between_points(self.position, self.heading, other_bot.position))
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,33 @@
1
+ module RTanque
2
+ class Bot
3
+ # Sensors provide input to the {RTanque::Bot::Brain} about the current state of the {RTanque::Match}
4
+ #
5
+ # They are made available to {RTanque::Bot::Brain} via {RTanque::Bot::Brain#sensors}
6
+ #
7
+ # @attr_reader [Integer] ticks number of ticks, starts at 0
8
+ # @attr_reader [Float] health health of bot. if == 0, dead
9
+ # @attr_reader [Float] gun_energy energy of cannon. if < 0, cannot fire
10
+ # @attr_reader [Float] speed
11
+ # @attr_reader [RTanque::Point] position
12
+ # @attr_reader [RTanque::Heading] heading
13
+ # @attr_reader [RTanque::Heading] radar_heading
14
+ # @attr_reader [RTanque::Heading] turret_heading
15
+ # @attr_reader [Enumerator] radar enumerates all bots scanned by the radar, yielding {RTanque::Bot::Radar::Reflection}
16
+ Sensors = Struct.new(:ticks, :health, :speed, :position, :heading, :radar, :radar_heading, :turret_heading, :gun_energy) do
17
+ def initialize(*args, &block)
18
+ super(*args)
19
+ block.call(self) if block
20
+ self.freeze
21
+ end
22
+
23
+ def button_down?(button_id)
24
+ button_id = Gosu::Window.char_to_button_id(button_id) unless button_id.kind_of?(Integer)
25
+ @gui_window && @gui_window.button_down?(button_id)
26
+ end
27
+
28
+ def gui_window=(gui_window)
29
+ @gui_window = gui_window
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ module RTanque
2
+ class Bot
3
+ class Turret
4
+ include Movable
5
+ extend NormalizedAttr
6
+ LENGTH = Configuration.turret.length
7
+ attr_normalized(:heading, Heading::FULL_RANGE, Configuration.turret.turn_step)
8
+
9
+ def initialize(heading)
10
+ @heading = heading
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,46 @@
1
+ require 'configuration'
2
+
3
+ module RTanque
4
+ one_degree = (Math::PI / 180.0)
5
+
6
+ # @!visibility private
7
+ Configuration = ::Configuration.for('default') do
8
+ raise_brain_tick_errors true
9
+
10
+ bot do
11
+ radius 19
12
+ health_reduction_on_exception 2
13
+ health 0..100
14
+ speed -3..3
15
+ speed_step 0.05
16
+ turn_step one_degree * 1.5
17
+ fire_power 1..5
18
+ gun_energy_max 10
19
+ gun_energy_factor 10
20
+ end
21
+ turret do
22
+ length 28
23
+ turn_step (one_degree * 2.0)
24
+ end
25
+ radar do
26
+ turn_step 0.05
27
+ vision -(one_degree * 10.0)..(one_degree * 10.0)
28
+ end
29
+ shell do
30
+ speed_factor 4.5
31
+ ratio 1.5 # used by Bot#adjust_fire_power and to calculate damage done by shell to bot
32
+ end
33
+ explosion do
34
+ life_span 70 * 1 # should be multiple of the number of frames in the explosion animation
35
+ end
36
+ gui do
37
+ update_interval 16.666666 # in milliseconds. 16.666666 == 60 FPS
38
+ fonts do
39
+ small 16
40
+ end
41
+ end
42
+ end
43
+ def Configuration.config(&block)
44
+ ::Configuration::DSL.evaluate(self, &block)
45
+ end
46
+ end
@@ -0,0 +1,23 @@
1
+ module RTanque
2
+ class Explosion
3
+ include Movable
4
+ attr_reader :position
5
+ LIFE_SPAN = Configuration.explosion.life_span # ticks
6
+ def initialize(position)
7
+ @position = position
8
+ @ticks = 0
9
+ end
10
+
11
+ def percent_dead
12
+ @ticks / LIFE_SPAN.to_f
13
+ end
14
+
15
+ def tick
16
+ @ticks += 1
17
+ end
18
+
19
+ def dead?
20
+ @ticks > LIFE_SPAN
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module RTanque
2
+ module Gui
3
+ RESOURCE_DIR = File.expand_path('../../../resources', __FILE__)
4
+ def self.resource_path(*components)
5
+ File.join(RESOURCE_DIR, *components)
6
+ end
7
+
8
+ module ZOrder
9
+ BACKGROUND = -1
10
+ BOT_NAME = 0
11
+ BOT_BODY = 1
12
+ BOT_TURRET = 2
13
+ BOT_RADAR = 3
14
+ SHELL = 4
15
+ EXPLOSION = 10
16
+ end
17
+ end
18
+ end
19
+
20
+ require 'rtanque/gui/draw_group'
21
+ require 'rtanque/gui/window'
22
+ require 'rtanque/gui/bot'
23
+ require 'rtanque/gui/shell'
24
+ require 'rtanque/gui/explosion'
@@ -0,0 +1,42 @@
1
+ require 'gosu'
2
+
3
+ module RTanque
4
+ module Gui
5
+ class Bot
6
+ attr_reader :bot
7
+
8
+ def initialize(window, bot)
9
+ @window = window
10
+ @bot = bot
11
+ @body_image = Gosu::Image.new(@window, Gui.resource_path("images/body.png"))
12
+ @turret_image = Gosu::Image.new(@window, Gui.resource_path("images/turret.png"))
13
+ @radar_image = Gosu::Image.new(@window, Gui.resource_path("images/radar.png"))
14
+ @name_font = Gosu::Font.new(@window, Window::FONT_NAME, Window::SMALL_FONT_SIZE)
15
+ @x_factor = 1
16
+ @y_factor = 1
17
+ end
18
+
19
+ def draw
20
+ position = [@bot.position.x, @window.height - @bot.position.y]
21
+ draw_bot(position)
22
+ draw_name(position)
23
+ end
24
+
25
+ def grow(factor = 2, step = 0.002)
26
+ @x_factor += step unless @x_factor > factor
27
+ @y_factor += step unless @y_factor > factor
28
+ end
29
+
30
+ def draw_bot(position)
31
+ @body_image.draw_rot(position[0], position[1], ZOrder::BOT_BODY, Gosu.radians_to_degrees(@bot.heading.to_f), 0.5, 0.5, @x_factor, @y_factor)
32
+ @turret_image.draw_rot(position[0], position[1], ZOrder::BOT_TURRET, Gosu.radians_to_degrees(@bot.turret.heading.to_f), 0.5, 0.5, @x_factor, @y_factor)
33
+ @radar_image.draw_rot(position[0], position[1], ZOrder::BOT_RADAR, Gosu.radians_to_degrees(@bot.radar.heading.to_f), 0.5, 0.5, @x_factor, @y_factor)
34
+ end
35
+
36
+ def draw_name(position)
37
+ x,y = *position
38
+ @name_font.draw_rel(self.bot.name, x, y + (RTanque::Bot::RADIUS * @y_factor) + Window::SMALL_FONT_SIZE.to_i, ZOrder::BOT_NAME, 0.5, 0.5, @x_factor, @y_factor)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,30 @@
1
+ module RTanque
2
+ module Gui
3
+ class DrawGroup
4
+ include Enumerable
5
+
6
+ def initialize(tick_group, &create_drawable)
7
+ @tick_group = tick_group
8
+ @mapped_drawables = Hash.new do |h, tickable|
9
+ h[tickable] = create_drawable.call(tickable)
10
+ end
11
+ end
12
+
13
+ def each(&block)
14
+ @tick_group.each do |tickable|
15
+ if tickable.dead?
16
+ @mapped_drawables.delete(tickable)
17
+ else
18
+ block.call(@mapped_drawables[tickable]) # This invokes @mapped_drawables's block if tickable not already in the hash
19
+ end
20
+ end
21
+ end
22
+
23
+ def draw
24
+ self.each do |drawable|
25
+ drawable.draw
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,25 @@
1
+ require 'gosu'
2
+
3
+ module RTanque
4
+ module Gui
5
+ class Explosion
6
+ FRAMES = (1..71)
7
+
8
+ def initialize(window, explosion)
9
+ @window = window
10
+ @explosion = explosion
11
+ @position = [explosion.position.x, window.height - explosion.position.y]
12
+ @explosion_images = FRAMES.map { |i| Gosu::Image.new(@window, Gui.resource_path("images/explosions/explosion2-#{i}.png")) }
13
+ end
14
+
15
+ def draw
16
+ frame.draw_rot(@position[0], @position[1], 5, ZOrder::EXPLOSION)
17
+ end
18
+
19
+ def frame
20
+ @frames_length ||= @explosion_images.length - 1
21
+ @explosion_images[(@explosion.percent_dead * @frames_length).floor]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ require 'gosu'
2
+
3
+ module RTanque
4
+ module Gui
5
+ class Shell
6
+ attr_reader :shell
7
+
8
+ def initialize(window, shell)
9
+ @window = window
10
+ @shell = shell
11
+ @shell_image = Gosu::Image.new(@window, Gui.resource_path("images/bullet.png"))
12
+ end
13
+
14
+ def draw
15
+ position = [self.shell.position.x, @window.height - self.shell.position.y]
16
+ @shell_image.draw_rot(position[0], position[1], ZOrder::SHELL, 0, 0.5, 0.5)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,51 @@
1
+ require 'gosu'
2
+
3
+ module RTanque
4
+ module Gui
5
+ class Window < Gosu::Window
6
+ UPDATE_INTERVAL = Configuration.gui.update_interval
7
+ FONT_NAME = Gosu::default_font_name
8
+ SMALL_FONT_SIZE = Configuration.gui.fonts.small
9
+
10
+ attr_reader :gui_bots, :gui_shells, :gui_explosions
11
+
12
+ def initialize(match)
13
+ @match = match
14
+ @arena = match.arena
15
+ match.bots.each { |bot| bot.gui_window = self }
16
+ @gui_bots = DrawGroup.new(match.bots) do |bot|
17
+ RTanque::Gui::Bot.new(self, bot)
18
+ end
19
+ @gui_shells = DrawGroup.new(match.shells) do |shell|
20
+ RTanque::Gui::Shell.new(self, shell)
21
+ end
22
+ @gui_explosions = DrawGroup.new(match.explosions) do |explosion|
23
+ RTanque::Gui::Explosion.new(self, explosion)
24
+ end
25
+ # Fullscreen: https://github.com/jlnr/gosu/issues/159#issuecomment-12473172
26
+ super(@arena.width, @arena.height, false, UPDATE_INTERVAL)
27
+ self.caption = self.class.name.split('::').first
28
+ @background = Gosu::Image.new(self, Gui.resource_path("images/grass.png"))
29
+ end
30
+
31
+ def update
32
+ @match.tick
33
+ end
34
+
35
+ def draw
36
+ self.close if button_down?(Gosu::Button::KbEscape)
37
+ @background.draw(0, 0, ZOrder::BACKGROUND)
38
+ if @match.finished?
39
+ self.gui_bots.each { |bot| bot.grow(2) }
40
+ end
41
+ self.draw_drawables
42
+ end
43
+
44
+ def draw_drawables
45
+ self.gui_bots.draw
46
+ self.gui_shells.draw
47
+ self.gui_explosions.draw
48
+ end
49
+ end
50
+ end
51
+ end