colstrom-rtanque 0.1.3

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 (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