brawl 0.0.0.alpha → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +0 -0
- data/README.md +43 -0
- data/_design/ClassDescriptions.txt +98 -0
- data/_design/Psudo_Program.rb +113 -0
- data/_design/array.rb +4 -0
- data/_design/threads.rb +71 -0
- data/_spike/Communications/communications.rb +2 -0
- data/_spike/Communications/lib/communications/com_linker.rb +11 -0
- data/_spike/Communications/lib/communications/null_modem.rb +11 -0
- data/_spike/Communications/spec/communications/com_linker_spec.rb +37 -0
- data/_spike/DRb/arena.rb +32 -0
- data/_spike/DRb/battle_controller.rb +54 -0
- data/_spike/DRb/bot.rb +23 -0
- data/_spike/DRb/bot_runner.rb +110 -0
- data/_spike/DRb/comment.txt +11 -0
- data/_spike/DRb/server.rb +28 -0
- data/_spike/DRb/test_bot.rb +6 -0
- data/_spike/array.rb +7 -0
- data/_spike/atan2.rb +39 -0
- data/_spike/battle.rb +202 -0
- data/_spike/break.rb +10 -0
- data/_spike/circles.rb +29 -0
- data/_spike/cleaner_attribs.rb +13 -0
- data/_spike/comlink.rb +61 -0
- data/_spike/concat.rb +27 -0
- data/_spike/example_bot.rb +42 -0
- data/_spike/forwarding_instance.rb +32 -0
- data/_spike/hash_loop.rb +17 -0
- data/_spike/hashing.rb +7 -0
- data/_spike/hook.rb +19 -0
- data/_spike/mod.rb +10 -0
- data/_spike/point_in_cone.rb +20 -0
- data/_spike/runner.rb +44 -0
- data/_spike/safe.rb +28 -0
- data/brawl.gemspec +4 -3
- data/example/example_battle.rb +173 -0
- data/example/logview.txt +6394 -0
- data/lib/brawl.rb +14 -3
- data/lib/brawl/_helper.rb +65 -0
- data/lib/brawl/arena.rb +118 -0
- data/lib/brawl/basic_arena_object.rb +35 -0
- data/lib/brawl/basic_bot.rb +69 -0
- data/lib/brawl/battle_controller.rb +97 -0
- data/lib/brawl/bot_proxy.rb +60 -0
- data/lib/brawl/clock.rb +36 -0
- data/lib/brawl/parts.rb +3 -0
- data/lib/brawl/parts/basic_motor.rb +55 -0
- data/lib/brawl/parts/basic_scanner.rb +33 -0
- data/lib/brawl/parts/basic_weapon.rb +47 -0
- data/lib/brawl/version.rb +1 -1
- data/lib/brawl/wall.rb +9 -0
- data/spec/brawl/arena_spec.rb +163 -0
- data/spec/brawl/basic_arena_object_spec.rb +40 -0
- data/spec/brawl/basic_bot_spec.rb +84 -0
- data/spec/brawl/battle_controller_spec.rb +138 -0
- data/spec/brawl/clock_spec.rb +28 -0
- data/spec/brawl/helper_spec.rb +23 -0
- data/spec/brawl/parts/basic_motor_spec.rb +220 -0
- data/spec/brawl/parts/basic_scanner_spec.rb +101 -0
- data/spec/brawl/parts/basic_weapon_spec.rb +146 -0
- data/spec/spec_helper.rb +5 -0
- metadata +101 -10
data/lib/brawl.rb
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
require "brawl/version"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require "brawl/_helper"
|
4
|
+
|
5
|
+
require "brawl/clock"
|
6
|
+
|
7
|
+
require "brawl/basic_arena_object"
|
8
|
+
require "brawl/wall"
|
9
|
+
|
10
|
+
require "brawl/arena"
|
11
|
+
|
12
|
+
require "brawl/basic_bot"
|
13
|
+
require "brawl/parts"
|
14
|
+
require "brawl/bot_proxy"
|
15
|
+
|
16
|
+
require "brawl/battle_controller"
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Brawl
|
2
|
+
|
3
|
+
DECIMAL_PLACES = 1
|
4
|
+
|
5
|
+
class Helper
|
6
|
+
|
7
|
+
def self.to_radians(angle)
|
8
|
+
angle * (Math::PI / 180)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.to_degrees(angle)
|
12
|
+
angle * (180.0 / Math::PI)
|
13
|
+
end
|
14
|
+
|
15
|
+
# returns a shorter angle if one exists (angle - 180's complement)
|
16
|
+
# e.g. wrap_angle(270) = -90
|
17
|
+
# wrap_angle(-320) = 40
|
18
|
+
# wrap_angle(45) = 45
|
19
|
+
def self.wrap_angle(angle)
|
20
|
+
# refactor? : can this be simplified?
|
21
|
+
return angle unless angle > 180 || angle < -180
|
22
|
+
(360 - angle.abs) * (angle.abs / angle) * -1
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.point_in_cone?(params)
|
26
|
+
|
27
|
+
origin = params[:origin]
|
28
|
+
radius = params[:radius]
|
29
|
+
angle = params[:angle]
|
30
|
+
direction = params[:direction]
|
31
|
+
point = params[:point]
|
32
|
+
|
33
|
+
distance_to_target = distance(origin, point)
|
34
|
+
return false if distance_to_target > radius
|
35
|
+
|
36
|
+
bearing_to_target = bearing(origin, point)
|
37
|
+
|
38
|
+
min_cone_angle = (direction - (angle / 2)) % 360
|
39
|
+
max_cone_angle = (direction + (angle / 2)) % 360
|
40
|
+
|
41
|
+
greater_than = bearing_to_target >= min_cone_angle
|
42
|
+
less_than = bearing_to_target <= max_cone_angle
|
43
|
+
|
44
|
+
if (min_cone_angle > max_cone_angle && (greater_than || less_than)) ||
|
45
|
+
(min_cone_angle <= max_cone_angle && (greater_than && less_than))
|
46
|
+
{ distance: distance_to_target.round(DECIMAL_PLACES),
|
47
|
+
bearing: bearing_to_target}
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.bearing(origin, target)
|
53
|
+
x1, y1, x2, y2 = origin[:x], origin[:y], target[:x], target[:y]
|
54
|
+
# correct mathy angles to what everyone on the face of the planet uses
|
55
|
+
to_degrees(Math.atan2(x2 - x1, y2 - y1)) % 360
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.distance(point1, point2)
|
59
|
+
x1, y1, x2, y2 = point1[:x], point1[:y], point2[:x], point2[:y]
|
60
|
+
distance = Math.sqrt((x1 - x2)**2 + (y1 - y2)**2)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/lib/brawl/arena.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
module Brawl
|
2
|
+
|
3
|
+
class Arena
|
4
|
+
|
5
|
+
attr_reader :size, :clock
|
6
|
+
|
7
|
+
def initialize(params={})
|
8
|
+
@size = params[:size]
|
9
|
+
@objects = []
|
10
|
+
|
11
|
+
set_clock(params[:clock])
|
12
|
+
|
13
|
+
add_walls
|
14
|
+
end
|
15
|
+
|
16
|
+
def length
|
17
|
+
@size[:length]
|
18
|
+
end
|
19
|
+
|
20
|
+
def width
|
21
|
+
@size[:width]
|
22
|
+
end
|
23
|
+
|
24
|
+
def ticks
|
25
|
+
@clock.ticks
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_clock(clock=nil)
|
29
|
+
@clock = clock || Clock.new
|
30
|
+
@clock.start
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_object(object)
|
34
|
+
add_objects([object]).first
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_objects(objects_array)
|
38
|
+
objects_array.collect do |object|
|
39
|
+
if get_object(id: object.id) || ping(object.location)
|
40
|
+
false
|
41
|
+
else
|
42
|
+
!!(@objects << object)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_object(object)
|
48
|
+
@objects.delete(object)
|
49
|
+
end
|
50
|
+
|
51
|
+
def move_object(object_location_hash)
|
52
|
+
object = object_location_hash.keys.first
|
53
|
+
location = object_location_hash.values.first
|
54
|
+
|
55
|
+
return if ping(location)
|
56
|
+
return unless in_bounds?(location)
|
57
|
+
|
58
|
+
!!(object.location = location)
|
59
|
+
end
|
60
|
+
|
61
|
+
# allows searching by object properties like location or id
|
62
|
+
def get_object(property_hash)
|
63
|
+
property, value = property_hash.first
|
64
|
+
@objects.each do |object|
|
65
|
+
return object.properties if object.properties[property] == value
|
66
|
+
end
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_all_objects
|
71
|
+
@objects.collect {|object| object.properties}
|
72
|
+
end
|
73
|
+
|
74
|
+
def in_bounds?(location)
|
75
|
+
return false if location[:x] >= @size[:width] || location[:x] < 0
|
76
|
+
return false if location[:y] >= @size[:length] || location[:y] < 0
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
def ping(location)
|
81
|
+
get_object(location: location)
|
82
|
+
end
|
83
|
+
|
84
|
+
def forward_damage(params)
|
85
|
+
target = @objects.select{|object| object.id == params[:target]}.first
|
86
|
+
target.damage(params[:damage])
|
87
|
+
end_game if victory?
|
88
|
+
end
|
89
|
+
|
90
|
+
def victory?
|
91
|
+
@objects.one? do |object|
|
92
|
+
object.class != Wall && object.health > 0
|
93
|
+
end ||
|
94
|
+
@objects.none? do |object|
|
95
|
+
object.class != Wall && object.health > 0
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def end_game
|
100
|
+
@clock.stop
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def add_walls
|
106
|
+
(0...width).each do |col|
|
107
|
+
@objects << Wall.new(location: {x: col, y: -1})
|
108
|
+
@objects << Wall.new(location: {x: col, y: length})
|
109
|
+
end
|
110
|
+
(0...length).each do |row|
|
111
|
+
@objects << Wall.new(location: {x: -1, y: row})
|
112
|
+
@objects << Wall.new(location: {x: width, y: row})
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'uuidtools'
|
2
|
+
|
3
|
+
module Brawl
|
4
|
+
|
5
|
+
class BasicArenaObject
|
6
|
+
|
7
|
+
attr_reader :id, :clock, :name
|
8
|
+
attr_accessor :location, :heading, :health
|
9
|
+
|
10
|
+
def initialize(params={})
|
11
|
+
@clock = params[:clock]
|
12
|
+
@id = params[:id] || UUIDTools::UUID.timestamp_create.to_s
|
13
|
+
@name = params[:name] || self.object_id
|
14
|
+
@location = params[:location] || {x: 0, y: 0}
|
15
|
+
@heading = params[:heading] || 0
|
16
|
+
@health = params[:health] || 1
|
17
|
+
@properties = params[:properties] ||
|
18
|
+
[:id, :name, :class, :location, :health, :heading]
|
19
|
+
end
|
20
|
+
|
21
|
+
# properties are values that are OK to be publicly 'seen' in the arena
|
22
|
+
def properties
|
23
|
+
@properties.each_with_object({}) do |property, hash|
|
24
|
+
hash[property] = self.send(property)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def damage(damage_points)
|
29
|
+
@health -= damage_points
|
30
|
+
@health < 0 ? @health = 0 : @health
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'uuidtools'
|
3
|
+
require 'eventable'
|
4
|
+
|
5
|
+
module Brawl
|
6
|
+
|
7
|
+
class BasicBot < BasicArenaObject
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
include Eventable
|
11
|
+
event :bot_ran_method
|
12
|
+
event :bot_damaged
|
13
|
+
|
14
|
+
attr_reader :parts
|
15
|
+
|
16
|
+
DECIMAL_PLACES = 1
|
17
|
+
|
18
|
+
def initialize(params={})
|
19
|
+
@arena = params[:arena]
|
20
|
+
@parts = params[:parts]
|
21
|
+
|
22
|
+
add_parts(@parts, params)
|
23
|
+
hook_methods_for_listeners
|
24
|
+
|
25
|
+
super
|
26
|
+
|
27
|
+
@arena.add_object(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def damage(damage_points)
|
31
|
+
super
|
32
|
+
fire_event(:bot_damaged,
|
33
|
+
properties,
|
34
|
+
:damage,
|
35
|
+
damage_points
|
36
|
+
)
|
37
|
+
@arena.remove_object(self) if @health <= 0
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def add_parts(parts, params)
|
43
|
+
return unless parts
|
44
|
+
parts.each do |part_module, init_params|
|
45
|
+
params.merge! init_params
|
46
|
+
# make sure we can fire the module's initialize method
|
47
|
+
part_module.send :alias_method, :initialize_parts, :initialize
|
48
|
+
extend part_module
|
49
|
+
initialize_parts(params)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def hook_methods_for_listeners
|
54
|
+
public_methods(false).each do |method|
|
55
|
+
next if [:properties,:damage].include?(method)
|
56
|
+
singleton_class.send :alias_method, "_hook_#{method}", method
|
57
|
+
|
58
|
+
singleton_class.send :define_method, method do |*params, &block|
|
59
|
+
return if @health <= 0
|
60
|
+
fire_event(:bot_ran_method, properties, method, *params)
|
61
|
+
send "_hook_#{method}".to_sym, *params, &block
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'eventable'
|
2
|
+
|
3
|
+
module Brawl
|
4
|
+
|
5
|
+
class BattleController
|
6
|
+
|
7
|
+
include Eventable
|
8
|
+
event :bot_msg
|
9
|
+
|
10
|
+
attr_accessor :arena, :bots, :clock
|
11
|
+
|
12
|
+
def initialize(params = {})
|
13
|
+
super
|
14
|
+
|
15
|
+
arena_data = params[:arena]
|
16
|
+
make_arena(arena_data)
|
17
|
+
|
18
|
+
bot_data = params[:bots]
|
19
|
+
make_bots(bot_data)
|
20
|
+
|
21
|
+
clock_data = params[:clock]
|
22
|
+
make_clock(clock_data)
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def make_arena(arena_data)
|
27
|
+
return unless arena_data
|
28
|
+
@arena = Brawl::Arena.new(arena_data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def make_clock(clock_data)
|
32
|
+
return unless clock_data
|
33
|
+
@clock = Clock.new(clock_data[:tick_rate])
|
34
|
+
@arena.set_clock(clock) if @arena
|
35
|
+
end
|
36
|
+
|
37
|
+
def make_bots(bot_data)
|
38
|
+
return unless bot_data
|
39
|
+
|
40
|
+
@bots = []
|
41
|
+
bot_data.each do |bot_datum|
|
42
|
+
bot_params = bot_datum[:params]
|
43
|
+
|
44
|
+
bot_params.merge!(arena: arena)
|
45
|
+
bot_params.merge!(name: bot_datum[:name])
|
46
|
+
|
47
|
+
location = {location: {x: rand(arena.width), y:rand(arena.length)}}
|
48
|
+
bot_params.merge!(location)
|
49
|
+
|
50
|
+
bot_instance = bot_datum[:class].new(bot_params)
|
51
|
+
|
52
|
+
bot_instance.register_for_event(
|
53
|
+
event: :bot_ran_method,
|
54
|
+
listener: self,
|
55
|
+
callback: :bot_msg_callback
|
56
|
+
)
|
57
|
+
bot_instance.register_for_event(
|
58
|
+
event: :bot_damaged,
|
59
|
+
listener: self,
|
60
|
+
callback: :bot_msg_callback
|
61
|
+
)
|
62
|
+
|
63
|
+
bot_proxy = BotProxy.new(
|
64
|
+
clock: clock,
|
65
|
+
bot: bot_instance,
|
66
|
+
code: bot_datum[:code]
|
67
|
+
)
|
68
|
+
|
69
|
+
@bots << {
|
70
|
+
name: bot_datum[:name],
|
71
|
+
proxy: bot_proxy
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def bot_msg_callback(*params, &block)
|
77
|
+
fire_event(:bot_msg, *params, &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
def victory?
|
81
|
+
arena.victory?
|
82
|
+
end
|
83
|
+
|
84
|
+
def start
|
85
|
+
@bots.each do |bot|
|
86
|
+
bot[:proxy].start
|
87
|
+
end
|
88
|
+
@clock.start
|
89
|
+
end
|
90
|
+
|
91
|
+
def stop
|
92
|
+
@clock.stop
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Brawl
|
2
|
+
|
3
|
+
class BotProxy
|
4
|
+
|
5
|
+
attr_accessor :bot, :last_tick, :msg_log, :clock
|
6
|
+
|
7
|
+
def initialize(params)
|
8
|
+
@clock = params[:clock]
|
9
|
+
@bot = params[:bot]
|
10
|
+
@code = params[:code]
|
11
|
+
|
12
|
+
@last_tick = @clock.ticks
|
13
|
+
@run = false
|
14
|
+
|
15
|
+
add_proxy_methods
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
@bot.name
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_proxy_methods
|
23
|
+
# I'm sure there's a better way of doing this
|
24
|
+
combined_methods = @bot.public_methods(false)
|
25
|
+
combined_methods.concat BasicArenaObject.instance_methods(false)
|
26
|
+
|
27
|
+
combined_methods.each do |method|
|
28
|
+
next if method.to_s[0] == "_"
|
29
|
+
singleton_class.send :define_method, method do |*params, &block|
|
30
|
+
sleep(0.01) while @clock.ticks <= @last_tick
|
31
|
+
@last_tick = @clock.ticks
|
32
|
+
@bot.send method, *params, &block
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def start
|
39
|
+
@run = true
|
40
|
+
Thread.start {
|
41
|
+
until @clock.state == :stopped || !@run || @health == 0
|
42
|
+
sleep(0.01) if @clock.state == :wait
|
43
|
+
if @clock.state == :running
|
44
|
+
instance_eval("Thread.start{$SAFE=3;#{@code}}.join")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@run = false
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def stop
|
52
|
+
@run = false
|
53
|
+
end
|
54
|
+
|
55
|
+
def code
|
56
|
+
yield self
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|