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/clock.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Brawl
|
2
|
+
|
3
|
+
class Clock
|
4
|
+
|
5
|
+
attr_reader :tick_rate, :ticks, :state
|
6
|
+
|
7
|
+
def initialize(tick_rate=0.05)
|
8
|
+
@tick_rate = tick_rate
|
9
|
+
@state = :wait
|
10
|
+
@ticks = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
@state = :running
|
15
|
+
ticking
|
16
|
+
end
|
17
|
+
|
18
|
+
def stop
|
19
|
+
@state = :stopping
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def ticking
|
25
|
+
Thread.start {
|
26
|
+
while @state == :running
|
27
|
+
sleep(@tick_rate)
|
28
|
+
@ticks += 1
|
29
|
+
end
|
30
|
+
@state = :stopped
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/brawl/parts.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Brawl
|
2
|
+
|
3
|
+
module BasicMotor
|
4
|
+
|
5
|
+
attr_reader :move_max, :turn_max
|
6
|
+
|
7
|
+
def initialize(params={})
|
8
|
+
@move_max = params[:move_max] || 5
|
9
|
+
@turn_max = params[:turn_max] || 90
|
10
|
+
end
|
11
|
+
|
12
|
+
def move(count=1)
|
13
|
+
|
14
|
+
heading_radian = Helper.to_radians((@heading + (count < 0 ? 180 : 0)))
|
15
|
+
count = count.abs
|
16
|
+
count = [count, @move_max].min
|
17
|
+
|
18
|
+
count.times do
|
19
|
+
x = @location[:x] + Math.sin(heading_radian).round(DECIMAL_PLACES)
|
20
|
+
y = @location[:y] + Math.cos(heading_radian).round(DECIMAL_PLACES)
|
21
|
+
break unless @arena.in_bounds?({x: x, y: y})
|
22
|
+
@location[:x], @location[:y] = x, y
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def turn(params)
|
27
|
+
# refactor
|
28
|
+
if params.is_a?(Hash)
|
29
|
+
turn_to(params[:to_angle]) if params[:to_angle]
|
30
|
+
turn_by(params[:by_degrees]) if params[:by_degrees]
|
31
|
+
else
|
32
|
+
angles = {left: -90, right: 90, around: 180}
|
33
|
+
turn_by(angles[params])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def turn_to(angle)
|
40
|
+
turn_by_angle = Helper.wrap_angle(angle - @heading)
|
41
|
+
turn_by(turn_by_angle)
|
42
|
+
end
|
43
|
+
|
44
|
+
def turn_by(degrees)
|
45
|
+
degrees = boundscheck_angle(degrees)
|
46
|
+
@heading = (@heading + degrees) % 360
|
47
|
+
end
|
48
|
+
|
49
|
+
def boundscheck_angle(angle)
|
50
|
+
[angle.abs % 360, @turn_max].min * (angle.abs / angle)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Brawl
|
2
|
+
module BasicScanner
|
3
|
+
|
4
|
+
attr_reader :scan_max, :angle_max
|
5
|
+
|
6
|
+
DECIMAL_PLACES = 1
|
7
|
+
|
8
|
+
def initialize(params={})
|
9
|
+
@scan_max = params[:scan_max] || 10
|
10
|
+
@angle_max = params[:angle_max] || 45
|
11
|
+
end
|
12
|
+
|
13
|
+
def scan(params={})
|
14
|
+
angle = [@angle_max, params[:angle] ||= @angle_max].min
|
15
|
+
|
16
|
+
cone = {
|
17
|
+
origin: @location,
|
18
|
+
direction: params[:direction],
|
19
|
+
radius: @scan_max,
|
20
|
+
angle: angle,
|
21
|
+
}
|
22
|
+
|
23
|
+
@arena.get_all_objects.collect do |info_hash|
|
24
|
+
next if info_hash[:id] == @id
|
25
|
+
if obj = Helper.point_in_cone?(cone.merge(point: info_hash[:location]))
|
26
|
+
info_hash.merge(obj)
|
27
|
+
end
|
28
|
+
end.compact
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Brawl
|
2
|
+
module BasicWeapon
|
3
|
+
|
4
|
+
attr_reader :range, :reload_time, :power
|
5
|
+
|
6
|
+
def initialize(params={})
|
7
|
+
|
8
|
+
@reload_time = params[:reload_time] || 10
|
9
|
+
@range = params[:range] || 10
|
10
|
+
@power = params[:power] || 1
|
11
|
+
|
12
|
+
@reload_countdown = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def shoot(direction=@heading)
|
16
|
+
return false if reload_countdown > 0
|
17
|
+
@reload_countdown = @reload_time + @arena.ticks
|
18
|
+
|
19
|
+
cone = {
|
20
|
+
origin: @location,
|
21
|
+
direction: direction,
|
22
|
+
radius: @range,
|
23
|
+
angle: 2
|
24
|
+
}
|
25
|
+
|
26
|
+
# refactor
|
27
|
+
possible_hits = @arena.get_all_objects.collect do |object|
|
28
|
+
if object[:id] != @id &&
|
29
|
+
hit = Helper.point_in_cone?(cone.merge(point: object[:location]))
|
30
|
+
object.merge(hit)
|
31
|
+
end
|
32
|
+
end.compact
|
33
|
+
return if possible_hits.empty?
|
34
|
+
|
35
|
+
# refactor
|
36
|
+
hit = possible_hits.sort{|a,b| a[:distance] <=> b[:distance]}.first
|
37
|
+
@arena.forward_damage(damage: @power, target: hit[:id])
|
38
|
+
hit
|
39
|
+
end
|
40
|
+
|
41
|
+
def reload_countdown
|
42
|
+
return 0 if @reload_countdown <= @arena.ticks
|
43
|
+
@reload_countdown - @arena.ticks
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/brawl/version.rb
CHANGED
data/lib/brawl/wall.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brawl::Arena do
|
4
|
+
|
5
|
+
let(:arena){Brawl::Arena.new(size: {width: 10, length: 10})}
|
6
|
+
let(:object1){Brawl::BasicArenaObject.new}
|
7
|
+
let(:object2){Brawl::BasicArenaobject.new}
|
8
|
+
|
9
|
+
context "when initializing" do
|
10
|
+
|
11
|
+
it "should start the clock automatically" do
|
12
|
+
arena = Brawl::Arena.new(size: {width: 100, length: 100})
|
13
|
+
expect{sleep(0.1)}.should change(arena, :ticks)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when determining size" do
|
18
|
+
|
19
|
+
it "should set the size when initialized" do
|
20
|
+
Brawl::Arena.new(
|
21
|
+
size: {width: 100, length: 100}
|
22
|
+
).size.should == {width: 100, length: 100}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should have convinence method for length" do
|
26
|
+
arena = Brawl::Arena.new(size: {width: 44, length: 923})
|
27
|
+
arena.length.should == 923
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should have convinence method for width" do
|
31
|
+
arena = Brawl::Arena.new(size: {width: 234, length: 63})
|
32
|
+
arena.width.should == 234
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when adding objects" do
|
38
|
+
|
39
|
+
it "should place an object" do
|
40
|
+
arena.add_object(object1).should be_true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not place objects where there is an object already" do
|
44
|
+
object1 = Brawl::BasicArenaObject.new(location: {x: 10, y: 10})
|
45
|
+
object2 = Brawl::BasicArenaObject.new(location: {x: 10, y: 10})
|
46
|
+
arena.add_object(object1)
|
47
|
+
arena.add_object(object2).should be_false
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should not place objects that already exist" do
|
51
|
+
arena.add_object(object1)
|
52
|
+
arena.add_object(object1).should be_false
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when removing objects" do
|
58
|
+
|
59
|
+
it "should remove an object using the object itself" do
|
60
|
+
arena.add_object(object1)
|
61
|
+
arena.remove_object(object1).should == object1
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should not remove objects that don't exist" do
|
65
|
+
arena.remove_object(object1).should be_nil
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when moving objects" do
|
71
|
+
|
72
|
+
before(:each) do
|
73
|
+
object1 = Brawl::BasicArenaObject.new(location: {x: 5, y: 5})
|
74
|
+
arena.add_object(object1)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should move an object from one location to another" do
|
78
|
+
arena.move_object(object1 => {x: 1, y: 1}).should be_true
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should not move an object if its already in the location" do
|
82
|
+
arena.move_object(object1 => {x: 5, y: 5}).should_not be_true
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should move an object to the correct location" do
|
86
|
+
arena.move_object(object1 => {x: 1, y: 1})
|
87
|
+
object1.location.should == {x: 1, y: 1}
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should not move above the bounds" do
|
91
|
+
# note the zero based locations
|
92
|
+
arena.move_object(object1 => {x: 0, y: 100}).should be_false
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should not move below the bounds" do
|
96
|
+
arena.move_object(object1 => {x: -1, y: 5}).should be_false
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
context "when getting objects" do
|
102
|
+
|
103
|
+
it "should retrieve objects by a key and value" do
|
104
|
+
object1 = Brawl::BasicArenaObject.new(location: {x: 5, y: 5})
|
105
|
+
arena.add_object(object1)
|
106
|
+
arena.get_object(id: object1.id).should_not be_nil
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should retrieve public object properties not the object itself" do
|
110
|
+
object1 = Brawl::BasicArenaObject.new(location: {x: 5, y: 5})
|
111
|
+
arena.add_object(object1)
|
112
|
+
arena.get_object(id: object1.id).should == object1.properties
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when pinging locations for objects" do
|
118
|
+
|
119
|
+
it "should return object information for a location" do
|
120
|
+
object1 = Brawl::BasicArenaObject.new(location: {x: 5, y: 5})
|
121
|
+
arena.add_object(object1)
|
122
|
+
arena.ping(x: 5, y: 5).should == object1.properties
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should return nothing for an empty location" do
|
126
|
+
arena.ping(x: 5, y: 5).should be_nil
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should return a wall for the areas around the arena" do
|
130
|
+
arena.ping(x:-1,y:0).select{|k,_|[:class,:location].include? k}.should ==
|
131
|
+
{class: Brawl::Wall, location: {x:-1, y:0}}
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should return nothing for areas outside the wall" do
|
135
|
+
# note that corners can't be "seen" from inside the arena so are not walls
|
136
|
+
arena.ping(x:-1,y:-1).should be_nil
|
137
|
+
arena.ping(x:10,y:10).should be_nil
|
138
|
+
arena.ping(x:-1,y:10).should be_nil
|
139
|
+
arena.ping(x:10,y:-1).should be_nil
|
140
|
+
arena.ping(x:0,y:101).should be_nil
|
141
|
+
arena.ping(x:11,y:0).should be_nil
|
142
|
+
arena.ping(x:11,y:11).should be_nil
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
context "when damage occurs" do
|
148
|
+
|
149
|
+
it 'should forward damage to the damaged object' do
|
150
|
+
expect{arena.forward_damage(damage: 1, target: object1.id)}.should
|
151
|
+
change(object1, :health).by(-1)
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should set the clock to the given clock object" do
|
157
|
+
clock = Brawl::Clock.new
|
158
|
+
arena.set_clock clock
|
159
|
+
arena.clock.should == clock
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brawl::BasicArenaObject do
|
4
|
+
|
5
|
+
let(:object){Brawl::BasicArenaObject.new}
|
6
|
+
|
7
|
+
it "should set a globally unique ID" do
|
8
|
+
object.id.should_not be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should set a default location of {x: 0, y: 0}" do
|
12
|
+
object.location.should == {x: 0, y: 0}
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should set a default heading of 0" do
|
16
|
+
object.heading.should == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should set a default health of 1" do
|
20
|
+
object.health.should == 1
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should override a default value if a specific value is given" do
|
24
|
+
object = Brawl::BasicArenaObject.new(health: 20)
|
25
|
+
object.health.should == 20
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have a hash of property values" do
|
29
|
+
object.properties.should be_a(Hash)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should reduce its health based on damage received" do
|
33
|
+
expect{object.damage(1)}.should change(object, :health).by(-1)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should never reduce health to less than 0" do
|
37
|
+
expect{object.damage(100)}.should change(object, :health).to(0)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brawl::BasicBot do
|
4
|
+
|
5
|
+
let(:arena){Brawl::Arena.new(size: {width: 100, length: 100})}
|
6
|
+
let(:bot) {Brawl::BasicBot.new(arena: arena, location: {x: 0.0, y: 0.0})}
|
7
|
+
|
8
|
+
it "should provide a hash of 'public' properites for the arena" do
|
9
|
+
bot.properties.should_not be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should have the methods of the parts that are injected into it" do
|
13
|
+
bot = Brawl::BasicBot.new(
|
14
|
+
location: {x: 0.0, y: 0.0},
|
15
|
+
heading: 0,
|
16
|
+
arena: arena,
|
17
|
+
parts: {Foo => {baz: "Baz set"}}
|
18
|
+
)
|
19
|
+
bot.should respond_to(:bar)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should access properties of the instance" do
|
23
|
+
bot = Brawl::BasicBot.new(
|
24
|
+
location: {x: 53.0, y: 34.0},
|
25
|
+
heading: 0,
|
26
|
+
arena: arena,
|
27
|
+
parts: {Foo => {baz: "Baz set"}}
|
28
|
+
)
|
29
|
+
bot.bar.should == {x: 53.0, y: 34.0}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should initialize parts" do
|
33
|
+
bot = Brawl::BasicBot.new(
|
34
|
+
location: {x: 0.0, y: 0.0},
|
35
|
+
heading: 0,
|
36
|
+
arena: arena,
|
37
|
+
parts: {Foo => {baz: "Baz set"}}
|
38
|
+
)
|
39
|
+
bot.baz.should == "Baz set"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should initialize more than one part" do
|
43
|
+
bot = Brawl::BasicBot.new(
|
44
|
+
location: {x: 0.0, y: 0.0},
|
45
|
+
heading: 0,
|
46
|
+
arena: arena,
|
47
|
+
parts: {Foo => {baz: "Baz set"}, Qux => {quux: "Quux set"}}
|
48
|
+
)
|
49
|
+
bot.quux.should == "Quux set"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should have methods of more than one part" do
|
53
|
+
bot = Brawl::BasicBot.new(
|
54
|
+
location: {x: 11.0, y: 222.0},
|
55
|
+
heading: 0,
|
56
|
+
arena: arena,
|
57
|
+
parts: {Foo => {baz: "Baz set"}, Qux => {quux: "Quux set"}}
|
58
|
+
)
|
59
|
+
bot.phoo.should == {x: 11.0, y: 222.0}
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
module Foo
|
65
|
+
attr_accessor :baz
|
66
|
+
def initialize(params={})
|
67
|
+
@baz = params[:baz]
|
68
|
+
end
|
69
|
+
def bar
|
70
|
+
@location
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module Qux
|
75
|
+
attr_accessor :quux
|
76
|
+
def initialize(params={})
|
77
|
+
@quux = params[:quux]
|
78
|
+
end
|
79
|
+
def phoo
|
80
|
+
@location
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|