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
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
19
+ twitter_images/
20
+ bots/
21
+ example.rb
22
+ examples/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 1.9.3" > .rvmrc
9
+ environment_id="ruby-2.0.0@RTanque"
10
+
11
+ # Uncomment the following lines if you want to verify rvm version per project
12
+ # rvmrc_rvm_version="1.17.2 (stable)" # 1.10.1 seams as a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+
18
+ # First we attempt to load the desired environment directly from the environment
19
+ # file. This is very fast and efficient compared to running through the entire
20
+ # CLI and selector. If you want feedback on which environment was used then
21
+ # insert the word 'use' after --create as this triggers verbose mode.
22
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
+ then
25
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
+ [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
27
+ \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
28
+ else
29
+ # If the environment file has not yet been created, use the RVM CLI to select.
30
+ rvm --create "$environment_id" || {
31
+ echo "Failed to create RVM environment '${environment_id}'."
32
+ return 1
33
+ }
34
+ fi
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ gemfile: Gemfile.ci
3
+ script: bundle exec rspec
4
+ rvm:
5
+ - 1.8.7
6
+ - 1.9.2
7
+ - 1.9.3
8
+ - 2.0.0
9
+ branches:
10
+ only:
11
+ - master
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ --markup markdown
2
+ --no-private
3
+ --files sample_bots/*
4
+ -
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rtanque.gemspec
4
+ gemspec
data/Gemfile.ci ADDED
@@ -0,0 +1,6 @@
1
+ # Gemfile for Travis. Doesn't require gosu which doesn't compile in Travis
2
+ source 'https://rubygems.org'
3
+
4
+ gem 'rspec'
5
+ gem 'rspec-mocks'
6
+ gem 'configuration'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Adam Williams
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,120 @@
1
+ # RTanque [![Build Status](https://travis-ci.org/awilliams/RTanque.png?branch=dev)](https://travis-ci.org/awilliams/RTanque) [![CodeClimate](https://codeclimate.com/github/awilliams/RTanque.png)](https://codeclimate.com/github/awilliams/RTanque)
2
+
3
+ **What is this?**
4
+ RTanque is a game for ( *Ruby* ) programmers. Players program the brain of a tank and then send their tank+brain into battle with other tanks. All tanks are otherwise equal.
5
+
6
+ Rules of the game are simple: Last bot standing wins. Gameplay is also pretty simple. Each tank has a **base**, **turret** and **radar**, each of which rotate independently. The base moves the tank, the turret has a gun mounted to it which can fire at other tanks, and the radar detects other tanks in its field of vision.
7
+
8
+ Have fun competing against friends' tanks or the sample ones included. Maybe you'll start a small league at your local Ruby meetup. CLI provides easy way to download bots from gists.
9
+
10
+ Sound difficult or time consuming? It's not! Check out the included sample tank [Seek&Destroy](https://github.com/awilliams/RTanque/blob/master/sample_bots/seek_and_destroy.rb) (which is actually fairly difficult to beat with the keyboard controlled bot). Note that it clocks in at under 50 LOC.
11
+
12
+ This is not an original idea, see [influences](https://github.com/awilliams/RTanque#influences). There's a lot of resources out there around tank design and tactics that could be applied to RTanque.
13
+
14
+ How does it look? Here's a [video](https://www.youtube.com/watch?v=G7i5X8pI6dk&hd=1) and screenshot of the game:
15
+ ![Battle](https://raw.github.com/awilliams/RTanque/master/screenshots/battle_1.png)
16
+
17
+ #### Influences
18
+ RTanque is based on the Java project [Robocode](http://robocode.sourceforge.net/) and inspired by other Ruby ports. Thanks and credit go to them both.
19
+
20
+ * [RobotWar](http://corewar.co.uk/robotwar/) - Perhaps the canonical version. Created in 1970's and set in the distant 2002. Apple II
21
+ * [Robocode](http://robocode.sourceforge.net/) - Java game, originally from IBM. Tank & explosion images taken from here.
22
+ * [RRobots](http://rrobots.rubyforge.org/) - Ruby port of Robocode. 2005
23
+ * [RRobots fork](https://github.com/ralreegorganon/rrobots)
24
+ * [FightCode](http://fightcodegame.com/) - Online javascript tank game
25
+ * [Scalatron](http://scalatron.github.com/) - Scala bot game
26
+ * [Many more...](https://www.google.com/?q=robocode%20clone)
27
+
28
+ ## Requirements
29
+
30
+ * The [Gosu](https://github.com/jlnr/gosu) library used for rendering has some dependencies. Use the [Gosu getting started](https://github.com/jlnr/gosu/wiki/Getting-Started-on-Linux) to resolve any for your system.
31
+ * Ruby 2.0.0 or 1.9.3 (tested on 1.8.7 and 1.9.2)
32
+
33
+ ## Quick Start
34
+
35
+ Make a project directory, init bundler, add the RTanque gem, and create a bot:
36
+
37
+ $ mkdir RTanque; cd RTanque
38
+ $ bundle init
39
+ $ echo "gem 'rtanque'" >> Gemfile
40
+ $ bundle
41
+ $ bundle exec rtanque new_bot my_deadly_bot
42
+ $ bundle exec rtanque start bots/my_deadly_bot sample_bots/keyboard sample_bots/camper:x2
43
+
44
+ *Drive the Keyboard bot with asdf. Aim/fire with the arrow keys*
45
+
46
+ ## [RTanque Documentation](http://rubydoc.info/github/awilliams/RTanque/master/frames/file/README.md)
47
+
48
+ * [RTanque](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque)
49
+ * [RTanque::Bot::Brain](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque/Bot/Brain)
50
+ * [RTanque::Heading](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque/Heading)
51
+ * [RTanque::Point](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque/Point)
52
+
53
+ ## Bot API
54
+
55
+ The tank api consists of reading input from Brain#sensors and giving output to Brain#command
56
+
57
+ **Brain#sensors**
58
+
59
+ ```ruby
60
+ class Bot < RTanque::Bot::Brain
61
+ # RTanque::Bot::Sensors =
62
+ # Struct.new(:ticks, :health, :speed, :position, :heading, :radar, :turret)
63
+ def tick!
64
+ sensors.ticks # Integer
65
+ sensors.health # Float
66
+ sensors.position # RTanque::Point
67
+ sensors.heading # RTanque::Heading
68
+ sensors.speed # Float
69
+ sensors.radar_heading # RTanque::Heading
70
+ sensors.turret_heading # RTanque::Heading
71
+ sensors.radar.each do |scanned_bot|
72
+ # scanned_bot: RTanque::Bot::Radar::Reflection
73
+ # Reflection(:heading, :distance, :name)
74
+ end
75
+ end
76
+ end
77
+ ```
78
+ **Brain#command**
79
+
80
+ ```ruby
81
+ class Bot < RTanque::Bot::Brain
82
+ # RTanque::Bot::Command =
83
+ # Struct.new(:speed, :heading, :radar_heading, :turret_heading, :fire_power)
84
+ def tick!
85
+ command.speed = 1
86
+ command.heading = Math::PI / 2.0
87
+ command.radar_heading = Math::PI
88
+ command.turret_heading = Math::PI
89
+ command.fire(3)
90
+ end
91
+ end
92
+ ```
93
+
94
+ **RTanque::Heading**
95
+ This class handles angles. It is a wrapper around Float bound to `(0..Math::PI*2)`
96
+
97
+ ```ruby
98
+ RTanque::Heading.new(Math::PI)
99
+ => <RTanque::Heading: 3.141592653589793rad 180.0deg>
100
+
101
+ RTanque::Heading.new_from_degrees(180)
102
+ => <RTanque::Heading: 3.141592653589793rad 180.0deg>
103
+
104
+ RTanque::Heading.new_from_degrees(180) + RTanque::Heading.new(Math::PI)
105
+ => <RTanque::Heading: 0.0rad 0.0deg>
106
+
107
+ RTanque::Heading.new(Math::PI) + (Math::PI / 2.0)
108
+ => <RTanque::Heading: 4.71238898038469rad 270.0deg>
109
+
110
+ RTanque::Heading.new == 0
111
+ => true
112
+ ```
113
+
114
+ ## Contributing
115
+
116
+ 1. Fork it
117
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
118
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
119
+ 4. Push to the branch (`git push origin my-new-feature`)
120
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/rtanque ADDED
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'thor'
4
+ require 'rtanque'
5
+ require 'rtanque/runner'
6
+ require 'octokit'
7
+
8
+ class RTanqueCLI < Thor
9
+ include Thor::Actions
10
+ BOT_DIR = 'bots'
11
+ source_root(File.expand_path('../../', __FILE__))
12
+
13
+ desc "start <path_to_brain> <path_to_brain>:x3 ...", "Starts match with given bots"
14
+ long_desc <<-LONGDESC
15
+ Start an RTanque match. Provide as arguments paths to the brains.
16
+ Note that multiple of the same bot can be loaded by appending ':x<int>' to the path. Eg 'bot/my_bot:x2'
17
+
18
+ There exist a few sample bots to help get started:
19
+ \x5 * sample_bots/keyboard (special bot controlled with keyboard: a/s/d/f and arrow keys)
20
+ \x5 * sample_bots/seek_and_destroy
21
+ \x5 * sample_bots/camper
22
+ LONGDESC
23
+ method_option :width, :aliases => '-w', :default => 1200, :type => :numeric, :banner => 'width of window'
24
+ method_option :height, :aliases => '-h', :default => 700, :type => :numeric, :banner => 'height of window'
25
+ method_option :max_ticks, :aliases => '-m', :default => Float::INFINITY, :type => :numeric, :banner => 'max ticks allowed per match'
26
+ method_option :gui, :default => true, :type => :boolean, :banner => 'false to run headless'
27
+ method_option :gc, :default => true, :type => :boolean, :banner => 'disable GC (EXPERIMENTAL)'
28
+ method_option :quiet, :aliases => '-q', :default => false, :type => :boolean, :banner => 'disable chatter'
29
+ def start(*brain_paths)
30
+ runner = RTanque::Runner.new(options[:width], options[:height], options[:max_ticks])
31
+ brain_paths.each { |brain_path|
32
+ begin
33
+ runner.add_brain_path(brain_path)
34
+ rescue RTanque::Runner::LoadError => e
35
+ say e.message, :red
36
+ exit false
37
+ end
38
+ }
39
+ self.print_start_banner(runner) unless options[:quiet]
40
+ self.set_gc(options[:gc]) { runner.start(options[:gui]) }
41
+ self.print_runner_stats(runner) unless options[:quiet]
42
+ end
43
+
44
+ desc "new_bot <bot_name>", "Creates a new bot"
45
+ long_desc <<-LONGDESC
46
+ Helper to create a basic brain template in the bots directory
47
+ LONGDESC
48
+ def new_bot(bot_name)
49
+ @bot_name = bot_name
50
+ @bot_class_name = Thor::Util.camel_case(bot_name)
51
+ template('templates/bot.erb', "#{BOT_DIR}/#{Thor::Util.snake_case(bot_name)}.rb")
52
+ end
53
+
54
+ desc "get_gist <gist_id> <gist_id> ...", "Downloads files from given gist ids into #{BOT_DIR} directory"
55
+ long_desc <<-LONGDESC
56
+ Helper to download tanks from github gists for easier sharing. Gists can be both 'secret' and 'public'.
57
+ LONGDESC
58
+ method_option :force, :aliases => '-f', :default => false, :type => :boolean, :banner => 'overwrite existing file without prompt'
59
+ def get_gist(*gist_ids)
60
+ gist_ids.each { |gist_id| self.download_gist(gist_id, options) }
61
+ end
62
+
63
+ protected
64
+
65
+ def print_start_banner(runner)
66
+ self.print_stats{ |table|
67
+ # print options
68
+ options.each { |opts| table << [set_color(opts[0], :yellow), opts[1].to_s] }
69
+ # print bots
70
+ table << [set_color('Bots', :green), runner.match.bots.map { |bot| bot.name }]
71
+ }
72
+ end
73
+
74
+ def print_runner_stats(runner)
75
+ say '='*30
76
+ self.print_stats{ |table|
77
+ table << [set_color('Ticks', :blue), runner.match.ticks.to_s]
78
+ table << [set_color('Survivors', :green)] + runner.match.bots.map { |bot| "#{bot.name} [#{bot.health.round}]" }
79
+ }
80
+ end
81
+
82
+ def set_gc(gc = true)
83
+ if gc
84
+ yield
85
+ else
86
+ GC.disable
87
+ begin
88
+ yield
89
+ ensure
90
+ GC.enable
91
+ end
92
+ end
93
+ end
94
+
95
+ def download_gist(gist_id, options)
96
+ gist = Octokit.gist(gist_id)
97
+ gist.files.values.map do |gist_file|
98
+ gist_path = "#{BOT_DIR}/#{gist.user.login}.#{gist_id}/#{gist_file.filename}"
99
+ create_file(gist_path, gist_file.content, options)
100
+ end
101
+ end
102
+
103
+ def print_stats(indent = 2, &block)
104
+ self.print_table([].tap(&block), :indent => indent)
105
+ end
106
+ end
107
+
108
+ RTanqueCLI.start
data/lib/rtanque.rb ADDED
@@ -0,0 +1,40 @@
1
+ # RTanque
2
+ #
3
+ # #Interesting classes for the spelunker:
4
+ #
5
+ # * {RTanque::Bot::Brain} All brains should inherit from this class
6
+ # * {RTanque::Bot::Sensors} Instance provided to {RTanque::Bot::Brain#sensors}
7
+ # * {RTanque::Bot::Command} Instance provided to {RTanque::Bot::Brain#command}
8
+ # * {RTanque::Heading} Handles angles
9
+ # * {RTanque::Point} Handles coordinates
10
+ # * {RTanqueCLI} CLI
11
+ module RTanque
12
+ # @!visibility private
13
+ def self.round(numeric, precision = nil)
14
+ if RUBY_VERSION >= '1.9'
15
+ numeric.round(precision)
16
+ else
17
+ # https://github.com/rails/rails/blob/v2.3.8/activesupport/lib/active_support/core_ext/float/rounding.rb
18
+ precision ? numeric.round : (numeric * (10 ** precision)).round / (10 ** precision).to_f
19
+ end
20
+ end
21
+ end
22
+
23
+ require 'rtanque/version'
24
+ require 'rtanque/point'
25
+ require 'rtanque/heading'
26
+ require 'rtanque/configuration'
27
+ require 'rtanque/arena'
28
+ require 'rtanque/normalized_attr'
29
+ require 'rtanque/movable'
30
+ require 'rtanque/bot'
31
+ require 'rtanque/bot/radar'
32
+ require 'rtanque/bot/turret'
33
+ require 'rtanque/bot/sensors'
34
+ require 'rtanque/bot/command'
35
+ require 'rtanque/bot/brain'
36
+ require 'rtanque/bot/brain_helper'
37
+ require 'rtanque/explosion'
38
+ require 'rtanque/shell'
39
+ require 'rtanque/match'
40
+ require 'rtanque/match/tick_group'
@@ -0,0 +1,8 @@
1
+ module RTanque
2
+ Arena = Struct.new(:width, :height) do
3
+ def initialize(*args, &block)
4
+ super
5
+ self.freeze
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,117 @@
1
+ module RTanque
2
+ class Bot
3
+ include Movable
4
+ extend NormalizedAttr
5
+ HEALTH_REDUCTION_ON_EXCEPTION = Configuration.bot.health_reduction_on_exception
6
+ RADIUS = Configuration.bot.radius
7
+ MAX_GUN_ENERGY = Configuration.bot.gun_energy_max
8
+ GUN_ENERGY_FACTOR = Configuration.bot.gun_energy_factor
9
+ attr_reader :arena, :brain, :radar, :turret, :ticks, :health, :fire_power, :gun_energy
10
+ attr_accessor :gui_window
11
+ attr_normalized(:speed, Configuration.bot.speed, Configuration.bot.speed_step)
12
+ attr_normalized(:heading, Heading::FULL_RANGE, Configuration.bot.turn_step)
13
+ attr_normalized(:fire_power, Configuration.bot.fire_power)
14
+ attr_normalized(:health, Configuration.bot.health)
15
+
16
+ def self.new_random_location(*args)
17
+ self.new(*args).tap do |bot|
18
+ rand_heading = Heading.rand
19
+ bot.position = Point.rand(bot.arena)
20
+ bot.heading = rand_heading
21
+ bot.radar.heading = rand_heading
22
+ bot.turret.heading = rand_heading
23
+ end
24
+ end
25
+
26
+ def initialize(arena, brain_klass = Brain)
27
+ @arena = arena
28
+ @brain = brain_klass.new(self.arena)
29
+ @ticks = 0
30
+ self.health = self.class::MAX_HEALTH
31
+ self.speed = 0
32
+ self.fire_power = nil
33
+ self.heading = Heading.new
34
+ self.position = Point.new(0, 0, self.arena)
35
+ @radar = Radar.new(self, self.heading.clone)
36
+ @turret = Turret.new(self.heading.clone)
37
+ end
38
+
39
+ def name
40
+ @name ||= self.brain.class.const_defined?(:NAME) ? self.brain.class.const_get(:NAME) : [self.brain.class.name, self.object_id].join(':')
41
+ end
42
+
43
+ def health=(val)
44
+ @health = val
45
+ end
46
+
47
+ def fire_power=(power)
48
+ @fire_power = power || 0
49
+ end
50
+
51
+ def adjust_fire_power
52
+ @gun_energy ||= MAX_GUN_ENERGY
53
+ if @gun_energy <= 0
54
+ self.fire_power = 0
55
+ else
56
+ @gun_energy -= (self.fire_power**RTanque::Shell::RATIO) * GUN_ENERGY_FACTOR
57
+ end
58
+ @gun_energy += 1
59
+ @gun_energy = MAX_GUN_ENERGY if @gun_energy > MAX_GUN_ENERGY
60
+ end
61
+
62
+ def firing?
63
+ self.fire_power && self.fire_power > 0
64
+ end
65
+
66
+ def reduce_health(reduce_by)
67
+ self.health -= reduce_by
68
+ end
69
+
70
+ def dead?
71
+ self.health <= self.class::MIN_HEALTH
72
+ end
73
+
74
+ def tick
75
+ @ticks += 1
76
+ self.tick_brain
77
+ self.adjust_fire_power
78
+ super
79
+ end
80
+
81
+ def tick_brain
82
+ begin
83
+ self.execute_command(self.brain.tick(self.sensors))
84
+ rescue Exception => brain_error
85
+ if Configuration.raise_brain_tick_errors
86
+ raise brain_error
87
+ else
88
+ puts brain_error
89
+ self.reduce_health(HEALTH_REDUCTION_ON_EXCEPTION)
90
+ end
91
+ end
92
+ end
93
+
94
+ def execute_command(command)
95
+ self.fire_power = self.normalize_fire_power(self.fire_power, command.fire_power)
96
+ self.speed = self.normalize_speed(self.speed, command.speed)
97
+ self.heading = self.normalize_heading(self.heading, command.heading)
98
+ self.radar.heading = self.radar.normalize_heading(self.radar.heading, command.radar_heading)
99
+ self.turret.heading = self.turret.normalize_heading(self.turret.heading, command.turret_heading)
100
+ end
101
+
102
+ def sensors
103
+ Sensors.new do |sensors|
104
+ sensors.ticks = self.ticks
105
+ sensors.health = self.health
106
+ sensors.speed = self.speed
107
+ sensors.position = self.position
108
+ sensors.heading = self.heading
109
+ sensors.radar = self.radar.to_enum
110
+ sensors.radar_heading = self.radar.heading
111
+ sensors.gun_energy = self.gun_energy
112
+ sensors.turret_heading = self.turret.heading
113
+ sensors.gui_window = self.gui_window
114
+ end
115
+ end
116
+ end
117
+ end