rtanque 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.rvmrc +34 -0
- data/.travis.yml +11 -0
- data/.yardopts +4 -0
- data/Gemfile +4 -0
- data/Gemfile.ci +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +120 -0
- data/Rakefile +1 -0
- data/bin/rtanque +108 -0
- data/lib/rtanque.rb +40 -0
- data/lib/rtanque/arena.rb +8 -0
- data/lib/rtanque/bot.rb +117 -0
- data/lib/rtanque/bot/brain.rb +50 -0
- data/lib/rtanque/bot/brain_helper.rb +23 -0
- data/lib/rtanque/bot/command.rb +23 -0
- data/lib/rtanque/bot/radar.rb +54 -0
- data/lib/rtanque/bot/sensors.rb +33 -0
- data/lib/rtanque/bot/turret.rb +14 -0
- data/lib/rtanque/configuration.rb +46 -0
- data/lib/rtanque/explosion.rb +23 -0
- data/lib/rtanque/gui.rb +24 -0
- data/lib/rtanque/gui/bot.rb +42 -0
- data/lib/rtanque/gui/draw_group.rb +30 -0
- data/lib/rtanque/gui/explosion.rb +25 -0
- data/lib/rtanque/gui/shell.rb +20 -0
- data/lib/rtanque/gui/window.rb +51 -0
- data/lib/rtanque/heading.rb +172 -0
- data/lib/rtanque/match.rb +67 -0
- data/lib/rtanque/match/tick_group.rb +50 -0
- data/lib/rtanque/movable.rb +51 -0
- data/lib/rtanque/normalized_attr.rb +69 -0
- data/lib/rtanque/point.rb +140 -0
- data/lib/rtanque/runner.rb +88 -0
- data/lib/rtanque/shell.rb +40 -0
- data/lib/rtanque/version.rb +3 -0
- data/resources/images/body.png +0 -0
- data/resources/images/bullet.png +0 -0
- data/resources/images/explosions/explosion2-1.png +0 -0
- data/resources/images/explosions/explosion2-10.png +0 -0
- data/resources/images/explosions/explosion2-11.png +0 -0
- data/resources/images/explosions/explosion2-12.png +0 -0
- data/resources/images/explosions/explosion2-13.png +0 -0
- data/resources/images/explosions/explosion2-14.png +0 -0
- data/resources/images/explosions/explosion2-15.png +0 -0
- data/resources/images/explosions/explosion2-16.png +0 -0
- data/resources/images/explosions/explosion2-17.png +0 -0
- data/resources/images/explosions/explosion2-18.png +0 -0
- data/resources/images/explosions/explosion2-19.png +0 -0
- data/resources/images/explosions/explosion2-2.png +0 -0
- data/resources/images/explosions/explosion2-20.png +0 -0
- data/resources/images/explosions/explosion2-21.png +0 -0
- data/resources/images/explosions/explosion2-22.png +0 -0
- data/resources/images/explosions/explosion2-23.png +0 -0
- data/resources/images/explosions/explosion2-24.png +0 -0
- data/resources/images/explosions/explosion2-25.png +0 -0
- data/resources/images/explosions/explosion2-26.png +0 -0
- data/resources/images/explosions/explosion2-27.png +0 -0
- data/resources/images/explosions/explosion2-28.png +0 -0
- data/resources/images/explosions/explosion2-29.png +0 -0
- data/resources/images/explosions/explosion2-3.png +0 -0
- data/resources/images/explosions/explosion2-30.png +0 -0
- data/resources/images/explosions/explosion2-31.png +0 -0
- data/resources/images/explosions/explosion2-32.png +0 -0
- data/resources/images/explosions/explosion2-33.png +0 -0
- data/resources/images/explosions/explosion2-34.png +0 -0
- data/resources/images/explosions/explosion2-35.png +0 -0
- data/resources/images/explosions/explosion2-36.png +0 -0
- data/resources/images/explosions/explosion2-37.png +0 -0
- data/resources/images/explosions/explosion2-38.png +0 -0
- data/resources/images/explosions/explosion2-39.png +0 -0
- data/resources/images/explosions/explosion2-4.png +0 -0
- data/resources/images/explosions/explosion2-40.png +0 -0
- data/resources/images/explosions/explosion2-41.png +0 -0
- data/resources/images/explosions/explosion2-42.png +0 -0
- data/resources/images/explosions/explosion2-43.png +0 -0
- data/resources/images/explosions/explosion2-44.png +0 -0
- data/resources/images/explosions/explosion2-45.png +0 -0
- data/resources/images/explosions/explosion2-46.png +0 -0
- data/resources/images/explosions/explosion2-47.png +0 -0
- data/resources/images/explosions/explosion2-48.png +0 -0
- data/resources/images/explosions/explosion2-49.png +0 -0
- data/resources/images/explosions/explosion2-5.png +0 -0
- data/resources/images/explosions/explosion2-50.png +0 -0
- data/resources/images/explosions/explosion2-51.png +0 -0
- data/resources/images/explosions/explosion2-52.png +0 -0
- data/resources/images/explosions/explosion2-53.png +0 -0
- data/resources/images/explosions/explosion2-54.png +0 -0
- data/resources/images/explosions/explosion2-55.png +0 -0
- data/resources/images/explosions/explosion2-56.png +0 -0
- data/resources/images/explosions/explosion2-57.png +0 -0
- data/resources/images/explosions/explosion2-58.png +0 -0
- data/resources/images/explosions/explosion2-59.png +0 -0
- data/resources/images/explosions/explosion2-6.png +0 -0
- data/resources/images/explosions/explosion2-60.png +0 -0
- data/resources/images/explosions/explosion2-61.png +0 -0
- data/resources/images/explosions/explosion2-62.png +0 -0
- data/resources/images/explosions/explosion2-63.png +0 -0
- data/resources/images/explosions/explosion2-64.png +0 -0
- data/resources/images/explosions/explosion2-65.png +0 -0
- data/resources/images/explosions/explosion2-66.png +0 -0
- data/resources/images/explosions/explosion2-67.png +0 -0
- data/resources/images/explosions/explosion2-68.png +0 -0
- data/resources/images/explosions/explosion2-69.png +0 -0
- data/resources/images/explosions/explosion2-7.png +0 -0
- data/resources/images/explosions/explosion2-70.png +0 -0
- data/resources/images/explosions/explosion2-71.png +0 -0
- data/resources/images/explosions/explosion2-8.png +0 -0
- data/resources/images/explosions/explosion2-9.png +0 -0
- data/resources/images/grass.png +0 -0
- data/resources/images/radar.png +0 -0
- data/resources/images/turret.png +0 -0
- data/rtanque.gemspec +33 -0
- data/sample_bots/camper.rb +79 -0
- data/sample_bots/keyboard.rb +50 -0
- data/sample_bots/seek_and_destroy.rb +51 -0
- data/screenshots/battle_1.png +0 -0
- data/screenshots/battle_2.png +0 -0
- data/spec/rtanque/bot_spec.rb +239 -0
- data/spec/rtanque/heading_spec.rb +279 -0
- data/spec/rtanque/match_spec.rb +36 -0
- data/spec/rtanque/normalized_attr_spec.rb +90 -0
- data/spec/rtanque/point_spec.rb +196 -0
- data/spec/rtanque/radar_spec.rb +87 -0
- data/spec/rtanque/shell_spec.rb +35 -0
- data/spec/rtanque_spec.rb +6 -0
- data/spec/spec_helper.rb +51 -0
- data/templates/bot.erb +11 -0
- 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
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
data/.yardopts
ADDED
data/Gemfile
ADDED
data/Gemfile.ci
ADDED
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'
|
data/lib/rtanque/bot.rb
ADDED
@@ -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
|