brawl 0.0.0.alpha → 0.0.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.
- 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
|