colstrom-rtanque 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +10 -0
  7. data/.yardopts +4 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.ci +6 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +168 -0
  12. data/Rakefile +1 -0
  13. data/bin/rtanque +117 -0
  14. data/lib/rtanque.rb +31 -0
  15. data/lib/rtanque/arena.rb +8 -0
  16. data/lib/rtanque/bot.rb +117 -0
  17. data/lib/rtanque/bot/brain.rb +50 -0
  18. data/lib/rtanque/bot/brain_helper.rb +23 -0
  19. data/lib/rtanque/bot/command.rb +23 -0
  20. data/lib/rtanque/bot/radar.rb +54 -0
  21. data/lib/rtanque/bot/sensors.rb +37 -0
  22. data/lib/rtanque/bot/turret.rb +14 -0
  23. data/lib/rtanque/configuration.rb +47 -0
  24. data/lib/rtanque/explosion.rb +23 -0
  25. data/lib/rtanque/gui.rb +25 -0
  26. data/lib/rtanque/gui/bot.rb +71 -0
  27. data/lib/rtanque/gui/bot/health_color_calculator.rb +37 -0
  28. data/lib/rtanque/gui/draw_group.rb +30 -0
  29. data/lib/rtanque/gui/explosion.rb +25 -0
  30. data/lib/rtanque/gui/shell.rb +31 -0
  31. data/lib/rtanque/gui/window.rb +64 -0
  32. data/lib/rtanque/heading.rb +162 -0
  33. data/lib/rtanque/match.rb +73 -0
  34. data/lib/rtanque/match/tick_group.rb +50 -0
  35. data/lib/rtanque/movable.rb +51 -0
  36. data/lib/rtanque/normalized_attr.rb +69 -0
  37. data/lib/rtanque/point.rb +140 -0
  38. data/lib/rtanque/runner.rb +88 -0
  39. data/lib/rtanque/shell.rb +44 -0
  40. data/lib/rtanque/version.rb +3 -0
  41. data/resources/images/body.png +0 -0
  42. data/resources/images/bullet.png +0 -0
  43. data/resources/images/explosions/explosion2-1.png +0 -0
  44. data/resources/images/explosions/explosion2-10.png +0 -0
  45. data/resources/images/explosions/explosion2-11.png +0 -0
  46. data/resources/images/explosions/explosion2-12.png +0 -0
  47. data/resources/images/explosions/explosion2-13.png +0 -0
  48. data/resources/images/explosions/explosion2-14.png +0 -0
  49. data/resources/images/explosions/explosion2-15.png +0 -0
  50. data/resources/images/explosions/explosion2-16.png +0 -0
  51. data/resources/images/explosions/explosion2-17.png +0 -0
  52. data/resources/images/explosions/explosion2-18.png +0 -0
  53. data/resources/images/explosions/explosion2-19.png +0 -0
  54. data/resources/images/explosions/explosion2-2.png +0 -0
  55. data/resources/images/explosions/explosion2-20.png +0 -0
  56. data/resources/images/explosions/explosion2-21.png +0 -0
  57. data/resources/images/explosions/explosion2-22.png +0 -0
  58. data/resources/images/explosions/explosion2-23.png +0 -0
  59. data/resources/images/explosions/explosion2-24.png +0 -0
  60. data/resources/images/explosions/explosion2-25.png +0 -0
  61. data/resources/images/explosions/explosion2-26.png +0 -0
  62. data/resources/images/explosions/explosion2-27.png +0 -0
  63. data/resources/images/explosions/explosion2-28.png +0 -0
  64. data/resources/images/explosions/explosion2-29.png +0 -0
  65. data/resources/images/explosions/explosion2-3.png +0 -0
  66. data/resources/images/explosions/explosion2-30.png +0 -0
  67. data/resources/images/explosions/explosion2-31.png +0 -0
  68. data/resources/images/explosions/explosion2-32.png +0 -0
  69. data/resources/images/explosions/explosion2-33.png +0 -0
  70. data/resources/images/explosions/explosion2-34.png +0 -0
  71. data/resources/images/explosions/explosion2-35.png +0 -0
  72. data/resources/images/explosions/explosion2-36.png +0 -0
  73. data/resources/images/explosions/explosion2-37.png +0 -0
  74. data/resources/images/explosions/explosion2-38.png +0 -0
  75. data/resources/images/explosions/explosion2-39.png +0 -0
  76. data/resources/images/explosions/explosion2-4.png +0 -0
  77. data/resources/images/explosions/explosion2-40.png +0 -0
  78. data/resources/images/explosions/explosion2-41.png +0 -0
  79. data/resources/images/explosions/explosion2-42.png +0 -0
  80. data/resources/images/explosions/explosion2-43.png +0 -0
  81. data/resources/images/explosions/explosion2-44.png +0 -0
  82. data/resources/images/explosions/explosion2-45.png +0 -0
  83. data/resources/images/explosions/explosion2-46.png +0 -0
  84. data/resources/images/explosions/explosion2-47.png +0 -0
  85. data/resources/images/explosions/explosion2-48.png +0 -0
  86. data/resources/images/explosions/explosion2-49.png +0 -0
  87. data/resources/images/explosions/explosion2-5.png +0 -0
  88. data/resources/images/explosions/explosion2-50.png +0 -0
  89. data/resources/images/explosions/explosion2-51.png +0 -0
  90. data/resources/images/explosions/explosion2-52.png +0 -0
  91. data/resources/images/explosions/explosion2-53.png +0 -0
  92. data/resources/images/explosions/explosion2-54.png +0 -0
  93. data/resources/images/explosions/explosion2-55.png +0 -0
  94. data/resources/images/explosions/explosion2-56.png +0 -0
  95. data/resources/images/explosions/explosion2-57.png +0 -0
  96. data/resources/images/explosions/explosion2-58.png +0 -0
  97. data/resources/images/explosions/explosion2-59.png +0 -0
  98. data/resources/images/explosions/explosion2-6.png +0 -0
  99. data/resources/images/explosions/explosion2-60.png +0 -0
  100. data/resources/images/explosions/explosion2-61.png +0 -0
  101. data/resources/images/explosions/explosion2-62.png +0 -0
  102. data/resources/images/explosions/explosion2-63.png +0 -0
  103. data/resources/images/explosions/explosion2-64.png +0 -0
  104. data/resources/images/explosions/explosion2-65.png +0 -0
  105. data/resources/images/explosions/explosion2-66.png +0 -0
  106. data/resources/images/explosions/explosion2-67.png +0 -0
  107. data/resources/images/explosions/explosion2-68.png +0 -0
  108. data/resources/images/explosions/explosion2-69.png +0 -0
  109. data/resources/images/explosions/explosion2-7.png +0 -0
  110. data/resources/images/explosions/explosion2-70.png +0 -0
  111. data/resources/images/explosions/explosion2-71.png +0 -0
  112. data/resources/images/explosions/explosion2-8.png +0 -0
  113. data/resources/images/explosions/explosion2-9.png +0 -0
  114. data/resources/images/grass.png +0 -0
  115. data/resources/images/radar.png +0 -0
  116. data/resources/images/turret.png +0 -0
  117. data/rtanque.gemspec +34 -0
  118. data/sample_bots/camper.rb +79 -0
  119. data/sample_bots/keyboard.rb +50 -0
  120. data/sample_bots/seek_and_destroy.rb +53 -0
  121. data/screenshots/battle_1.png +0 -0
  122. data/screenshots/battle_2.png +0 -0
  123. data/spec/rtanque/bot_spec.rb +239 -0
  124. data/spec/rtanque/heading_spec.rb +279 -0
  125. data/spec/rtanque/match_spec.rb +46 -0
  126. data/spec/rtanque/normalized_attr_spec.rb +90 -0
  127. data/spec/rtanque/point_spec.rb +196 -0
  128. data/spec/rtanque/radar_spec.rb +87 -0
  129. data/spec/rtanque/shell_spec.rb +35 -0
  130. data/spec/rtanque_spec.rb +34 -0
  131. data/spec/spec_helper.rb +51 -0
  132. data/templates/bot.erb +17 -0
  133. metadata +315 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5bb81d35a1207166bc9b7916b483204c4de420b6
4
+ data.tar.gz: d432f6a4c1422a07238ac0bb28181afb2e8f9a50
5
+ SHA512:
6
+ metadata.gz: 7af7f69becd7608f1f9c6f50e38cf9d4679f54d3bd1ac9ce031405b7886d677cb7f9aed246e45e325a26ebded823845ffc1cde7fd49168b29804deccf94832d4
7
+ data.tar.gz: 390ed015f716aa4fd65e340f522d02b909117e9dfeee12bfecac0865298f879ee843eaaf7dc958a22b291c0960a8a49fde68602642eef8c5f2acc6d48a6b6788
@@ -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
@@ -0,0 +1 @@
1
+ RTanque
@@ -0,0 +1 @@
1
+ 2.0.0
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ gemfile: Gemfile.ci
3
+ script: bundle exec rspec
4
+ rvm:
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - 2.0.0
8
+ branches:
9
+ only:
10
+ - master
@@ -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
@@ -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'
@@ -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.
@@ -0,0 +1,168 @@
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
+ [Getting started guide](http://awilliams.github.io/articles/rtanque-getting-started/)
7
+
8
+ 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.
9
+
10
+ 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.
11
+
12
+ 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.
13
+
14
+ 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.
15
+
16
+ How does it look? Here's a video of a battle:
17
+
18
+ <a href="http://www.youtube.com/watch?feature=player_embedded&v=UPBqwOgGlVY
19
+ " target="_blank"><img src="http://img.youtube.com/vi/UPBqwOgGlVY/0.jpg"
20
+ alt="RTanque Demo" width="640" height="480" border="10" /></a>
21
+
22
+ #### Influences
23
+ 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.
24
+
25
+ * [RobotWar](http://corewar.co.uk/robotwar/) - Perhaps the canonical version. Created in 1970's and set in the distant 2002. Apple II
26
+ * [Robocode](http://robocode.sourceforge.net/) - Java game, originally from IBM. Tank & explosion images taken from here.
27
+ * [RRobots](http://rrobots.rubyforge.org/) - Ruby port of Robocode. 2005
28
+ * [RRobots fork](https://github.com/ralreegorganon/rrobots)
29
+ * [FightCode](http://fightcodegame.com/) - Online javascript tank game
30
+ * [Scalatron](http://scalatron.github.com/) - Scala bot game
31
+ * [Many more...](https://www.google.com/?q=robocode%20clone)
32
+
33
+ ## Requirements
34
+
35
+ * 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.
36
+ * Ruby 2.0.0 or 1.9.3 (tested on 1.8.7 and 1.9.2)
37
+
38
+ ## Quick Start
39
+
40
+ Make a project directory, init bundler, add the RTanque gem, and create a bot:
41
+
42
+ $ mkdir RTanque; cd RTanque
43
+ $ bundle init
44
+ $ echo "gem 'rtanque'" >> Gemfile
45
+ $ bundle
46
+ $ bundle exec rtanque new_bot my_deadly_bot
47
+ $ bundle exec rtanque start bots/my_deadly_bot sample_bots/keyboard sample_bots/camper:x2
48
+
49
+ *Drive the Keyboard bot with asdf. Aim/fire with the arrow keys*
50
+
51
+ ## [RTanque Documentation](http://rubydoc.info/github/awilliams/RTanque/master/frames/file/README.md)
52
+
53
+ * [RTanque](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque)
54
+ * [RTanque::Bot::Brain](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque/Bot/Brain)
55
+ * [RTanque::Heading](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque/Heading)
56
+ * [RTanque::Point](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque/Point)
57
+
58
+ ## Advanced Options
59
+
60
+ Set arena dimensions
61
+
62
+ $ bundle exec rtanque start --width=400 --height=400 sample_bots/camper:x4
63
+
64
+ Adjust max ticks allowed
65
+
66
+ $ bundle exec rtanque start --max_ticks=500 sample_bots/camper:x4
67
+
68
+ Run headless match (no gui)
69
+
70
+ $ bundle exec rtanque start --gui=false sample_bots/camper:x4
71
+
72
+ Run team match (teams are currently determined by bot name. Bots with same name are on the same team. The match finished if alive bots have the same name.) https://github.com/awilliams/RTanque/pull/10
73
+
74
+ $ bundle exec rtanque start --teams sample_bots/camper:x4 sample_bots/seek_and_destroy.rb:4
75
+
76
+ Quiet mode (less console chatter).
77
+
78
+ $ bundle exec rtanque start --quiet sample_bots/camper:x4
79
+
80
+ Set random number seed, allowing same battle to be repeated. https://github.com/awilliams/RTanque/pull/7
81
+
82
+ $ bundle exec rtanque start --seed 1234 sample_bots/camper:x4
83
+
84
+ **Experimental** Disable garbage collection during match
85
+
86
+ $ bundle exec rtanque start --gc=false sample_bots/camper:x4
87
+
88
+ ## Sharing
89
+ At some point you'll want to compete against other bots, or maybe you'll even organize a small tournament. Sharing bots is easy.
90
+
91
+ Ask your friends to upload their bot(s) in a [gist](https://gist.github.com/), which you can then download with the following command:
92
+
93
+ bundle exec rtanque get_gist <gist_id> ...
94
+
95
+ For example, to download [Marksman](https://gist.github.com/SteveRidout/5909793)
96
+
97
+ bundle exec rtanque get_gist 5909793
98
+
99
+ If you'd like to publicly share your bot, post its gist id on the wiki https://github.com/awilliams/RTanque/wiki/bot-gists
100
+
101
+ ## Bot API
102
+
103
+ The tank api consists of reading input from Brain#sensors and giving output to Brain#command
104
+
105
+ **[Brain#sensors](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque/Bot/Sensors)**
106
+
107
+ ```ruby
108
+ class Bot < RTanque::Bot::Brain
109
+ # RTanque::Bot::Sensors =
110
+ # Struct.new(:ticks, :health, :speed, :position, :heading, :radar, :turret)
111
+ def tick!
112
+ sensors.ticks # Integer
113
+ sensors.health # Float
114
+ sensors.position # RTanque::Point
115
+ sensors.heading # RTanque::Heading
116
+ sensors.speed # Float
117
+ sensors.radar_heading # RTanque::Heading
118
+ sensors.turret_heading # RTanque::Heading
119
+ sensors.radar.each do |scanned_bot|
120
+ # scanned_bot: RTanque::Bot::Radar::Reflection
121
+ # Reflection(:heading, :distance, :name)
122
+ end
123
+ end
124
+ end
125
+ ```
126
+ **[Brain#command](http://rubydoc.info/github/awilliams/RTanque/master/frames/RTanque/Bot/Command)**
127
+
128
+ ```ruby
129
+ class Bot < RTanque::Bot::Brain
130
+ # RTanque::Bot::Command =
131
+ # Struct.new(:speed, :heading, :radar_heading, :turret_heading, :fire_power)
132
+ def tick!
133
+ command.speed = 1
134
+ command.heading = Math::PI / 2.0
135
+ command.radar_heading = Math::PI
136
+ command.turret_heading = Math::PI
137
+ command.fire(3)
138
+ end
139
+ end
140
+ ```
141
+
142
+ **RTanque::Heading**
143
+ This class handles angles. It is a wrapper around Float bound to `(0..Math::PI*2)`
144
+
145
+ ```ruby
146
+ RTanque::Heading.new(Math::PI)
147
+ => <RTanque::Heading: 3.141592653589793rad 180.0deg>
148
+
149
+ RTanque::Heading.new_from_degrees(180)
150
+ => <RTanque::Heading: 3.141592653589793rad 180.0deg>
151
+
152
+ RTanque::Heading.new_from_degrees(180) + RTanque::Heading.new(Math::PI)
153
+ => <RTanque::Heading: 0.0rad 0.0deg>
154
+
155
+ RTanque::Heading.new(Math::PI) + (Math::PI / 2.0)
156
+ => <RTanque::Heading: 4.71238898038469rad 270.0deg>
157
+
158
+ RTanque::Heading.new == 0
159
+ => true
160
+ ```
161
+
162
+ ## Contributing
163
+
164
+ 1. Fork it
165
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
166
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
167
+ 4. Push to the branch (`git push origin my-new-feature`)
168
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,117 @@
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 :teams, :default => false, :type => :boolean, :banner => 'true to do a team based match based on tank names'
27
+ method_option :gui, :default => true, :type => :boolean, :banner => 'false to run headless'
28
+ method_option :gc, :default => true, :type => :boolean, :banner => 'disable GC (EXPERIMENTAL)'
29
+ method_option :quiet, :aliases => '-q', :default => false, :type => :boolean, :banner => 'disable chatter'
30
+ method_option :seed, :default => Kernel.srand, :type => :numeric, :banner => 'random number seed value'
31
+ def start(*brain_paths)
32
+ Kernel.srand(options[:seed])
33
+ runner = RTanque::Runner.new(options[:width], options[:height], options[:max_ticks], options[:teams])
34
+ brain_paths.each { |brain_path|
35
+ begin
36
+ runner.add_brain_path(brain_path)
37
+ rescue RTanque::Runner::LoadError => e
38
+ say e.message, :red
39
+ exit false
40
+ end
41
+ }
42
+ self.print_start_banner(runner) unless options[:quiet]
43
+ self.set_gc(options[:gc]) { runner.start(options[:gui]) }
44
+ self.print_runner_stats(runner) unless options[:quiet]
45
+ end
46
+
47
+ desc "new_bot <bot_name>", "Creates a new bot"
48
+ long_desc <<-LONGDESC
49
+ Helper to create a basic brain template in the bots directory
50
+ LONGDESC
51
+ def new_bot(bot_name)
52
+ @bot_name = bot_name
53
+ @bot_class_name = Thor::Util.camel_case(bot_name)
54
+ template('templates/bot.erb', "#{BOT_DIR}/#{Thor::Util.snake_case(bot_name)}.rb")
55
+ end
56
+
57
+ desc "get_gist <gist_id> <gist_id> ...", "Downloads files from given gist ids into #{BOT_DIR} directory"
58
+ long_desc <<-LONGDESC
59
+ Helper to download tanks from github gists for easier sharing. Gists can be both 'secret' and 'public'.
60
+ LONGDESC
61
+ method_option :force, :aliases => '-f', :default => false, :type => :boolean, :banner => 'overwrite existing file without prompt'
62
+ def get_gist(*gist_ids)
63
+ gist_ids.each { |gist_id| self.download_gist(gist_id, options) }
64
+ end
65
+
66
+ protected
67
+
68
+ def print_start_banner(runner)
69
+ self.print_stats{ |table|
70
+ # print options
71
+ options.each { |opts| table << [set_color(opts[0], :yellow), opts[1].to_s] }
72
+ # print bots
73
+ table << [set_color('Bots', :green), runner.match.bots.map { |bot| bot.name }]
74
+ }
75
+ end
76
+
77
+ def print_runner_stats(runner)
78
+ say '='*30
79
+ self.print_stats{ |table|
80
+ table << [set_color('Ticks', :blue), runner.match.ticks.to_s]
81
+ table << [set_color('Survivors', :green)] + runner.match.bots.map { |bot| "#{bot.name} [#{bot.health.round}]" }
82
+ }
83
+ end
84
+
85
+ def set_gc(gc = true)
86
+ if gc
87
+ yield
88
+ else
89
+ GC.disable
90
+ begin
91
+ yield
92
+ ensure
93
+ GC.enable
94
+ end
95
+ end
96
+ end
97
+
98
+ def download_gist(gist_id, options)
99
+ client = Octokit::Client.new
100
+ begin
101
+ gist = client.gist(gist_id)
102
+ rescue Octokit::NotFound
103
+ puts set_color("Error! Gist #{gist_id} not found. Please ensure the gist id is correct.", :red)
104
+ else
105
+ gist.files.attrs.each do |name, gist_file|
106
+ gist_path = "#{BOT_DIR}/#{gist.user.login}.#{gist_id}/#{name}"
107
+ create_file(gist_path, gist_file.content, options)
108
+ end
109
+ end
110
+ end
111
+
112
+ def print_stats(indent = 2, &block)
113
+ self.print_table([].tap(&block), :indent => indent)
114
+ end
115
+ end
116
+
117
+ RTanqueCLI.start
@@ -0,0 +1,31 @@
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
+ module RTanque
11
+
12
+ end
13
+
14
+ require 'rtanque/version'
15
+ require 'rtanque/point'
16
+ require 'rtanque/heading'
17
+ require 'rtanque/configuration'
18
+ require 'rtanque/arena'
19
+ require 'rtanque/normalized_attr'
20
+ require 'rtanque/movable'
21
+ require 'rtanque/bot'
22
+ require 'rtanque/bot/radar'
23
+ require 'rtanque/bot/turret'
24
+ require 'rtanque/bot/sensors'
25
+ require 'rtanque/bot/command'
26
+ require 'rtanque/bot/brain'
27
+ require 'rtanque/bot/brain_helper'
28
+ require 'rtanque/explosion'
29
+ require 'rtanque/shell'
30
+ require 'rtanque/match'
31
+ 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