rtanque 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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