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/_spike/break.rb
ADDED
data/_spike/circles.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "bigdecimal"
|
2
|
+
require "bigdecimal/math"
|
3
|
+
|
4
|
+
include BigMath
|
5
|
+
|
6
|
+
def to_radians(angle)
|
7
|
+
big_angle = BigDecimal(angle.to_s)
|
8
|
+
big_angle * (PI(100) / 180.0)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_degree(angle)
|
12
|
+
big_angle = BigDecimal(angle.to_s)
|
13
|
+
big_angle * (180.0 / PI(100))
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
angle = -320
|
18
|
+
puts "angle: #{angle}"
|
19
|
+
radians = to_radians angle
|
20
|
+
sin = Math.sin(radians).abs
|
21
|
+
puts "rad: #{radians}"
|
22
|
+
puts "sin: #{sin}"
|
23
|
+
asin = Math.asin(sin)
|
24
|
+
puts "asin: #{asin}"
|
25
|
+
wrap_angle = to_degree asin
|
26
|
+
puts "wrap: #{wrap_angle.round}"
|
27
|
+
# radian90 = to radian: 90
|
28
|
+
# puts "90 degrees to radian: #{radian90}"
|
29
|
+
# puts "90 radians to degrees: #{to degree: radian90}"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Foo
|
2
|
+
attr_accessor :bar
|
3
|
+
def initialize(args={})
|
4
|
+
args.each do |key, value|
|
5
|
+
instance_variable_set("@#{key}", value) if public_methods(false).include? key
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
puts Foo.instance_methods(false)
|
11
|
+
|
12
|
+
f = Foo.new(bar: "foo!")
|
13
|
+
puts f.bar
|
data/_spike/comlink.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
class Foo
|
2
|
+
def bar(value)
|
3
|
+
"doh!"
|
4
|
+
end
|
5
|
+
def baz(value1, value2)
|
6
|
+
"Oh, noos!"
|
7
|
+
end
|
8
|
+
def qux
|
9
|
+
"Fudge!"
|
10
|
+
end
|
11
|
+
def quux(value1, value2, &block)
|
12
|
+
"Nope, nope, nope"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class FooProxy
|
17
|
+
def bar(value)
|
18
|
+
"#{value}"
|
19
|
+
end
|
20
|
+
def baz(value1, value2)
|
21
|
+
"#{value1}, #{value2}"
|
22
|
+
end
|
23
|
+
def qux
|
24
|
+
yield "FooProxy"
|
25
|
+
end
|
26
|
+
def quux(value1, value2, &block)
|
27
|
+
yield ["FooProxy", "#{value1}, #{value2}"]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class NullModem
|
32
|
+
def link_method(client, server, method)
|
33
|
+
client.class.send :define_method, method do |*params, &block|
|
34
|
+
server.send method, *params, &block
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ComLinker
|
40
|
+
def self.link(comlayer, client, server)
|
41
|
+
client.public_methods(false).each do |method|
|
42
|
+
comlayer.link_method(client, server, method)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
foo = Foo.new
|
49
|
+
proxy = FooProxy.new
|
50
|
+
comlayer = NullModem.new
|
51
|
+
|
52
|
+
ComLinker.link(comlayer, foo, proxy)
|
53
|
+
|
54
|
+
puts foo.bar("hello!")
|
55
|
+
puts foo.baz("howdy", "ho")
|
56
|
+
puts foo.qux {|value| value}
|
57
|
+
puts (foo.quux("look", "at this") {|value| value}).inspect
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
data/_spike/concat.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
x = [1,2,3]
|
4
|
+
puts x.object_id
|
5
|
+
|
6
|
+
x.concat [4,5] # same object
|
7
|
+
puts x.object_id
|
8
|
+
|
9
|
+
x += [6,7] # different object
|
10
|
+
puts x.object_id
|
11
|
+
|
12
|
+
|
13
|
+
Benchmark.bmbm do |bm|
|
14
|
+
x = ("X"*100).split("")
|
15
|
+
y = ("Y"*100).split("")
|
16
|
+
times = 1000
|
17
|
+
|
18
|
+
bm.report{times.times {x.concat y}}
|
19
|
+
bm.report{times.times {x += y}} # substantially slower
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
=begin
|
24
|
+
user system total real
|
25
|
+
0.000000 0.000000 0.000000 ( 0.001476)
|
26
|
+
2.950000 2.140000 5.090000 ( 6.061063)
|
27
|
+
=end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class BasicBot
|
2
|
+
|
3
|
+
end
|
4
|
+
|
5
|
+
# this isn't meant to be a good bot, just to show the features of bots
|
6
|
+
code.loop do |bot|
|
7
|
+
|
8
|
+
scan_results = bot.scan direction: 0, angle: 45
|
9
|
+
|
10
|
+
unless (enemies = scan_results.contacts.select
|
11
|
+
{|contact| contact.type == :enemy}).empty?
|
12
|
+
|
13
|
+
# you should get the closest probably, not just the first in the array
|
14
|
+
enemy = enemies.first
|
15
|
+
|
16
|
+
if enemy.distance <= bot.weapon.range do
|
17
|
+
bot.fire direction: enemy.direction
|
18
|
+
else
|
19
|
+
bot.turn direction: enemy.direction
|
20
|
+
bot.move
|
21
|
+
end
|
22
|
+
|
23
|
+
else
|
24
|
+
|
25
|
+
# No enemy spotted so move around randomly
|
26
|
+
# mostly go straight but turn randomly sometimes
|
27
|
+
unless rand(100) > 70 and scan_result.any?
|
28
|
+
{|contact| contact.direction == :front && contact.distance == 1}
|
29
|
+
bot.move direction: :forward
|
30
|
+
else
|
31
|
+
[:right, :left].sample do |direction|
|
32
|
+
# if the enemy gets right behind you, you could go into an endless loop
|
33
|
+
bot.turn direction until
|
34
|
+
(bot.scan direction: 0, angle: 0).contacts.empty
|
35
|
+
|
36
|
+
bot.move
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
class Bar
|
4
|
+
def baz
|
5
|
+
puts "baz!"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Foo
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
attr_accessor :parts
|
13
|
+
|
14
|
+
def initialize(args)
|
15
|
+
|
16
|
+
@parts = args[:parts]
|
17
|
+
|
18
|
+
@parts.each do |part, instance|
|
19
|
+
instance_variable_set("@#{part}", instance)
|
20
|
+
self.class.def_delegators "@#{part}", *(instance.public_methods(false))
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
f = Foo.new(parts: {bar: Bar.new})
|
29
|
+
puts f.public_methods(false)
|
30
|
+
puts
|
31
|
+
puts f.instance_variables
|
32
|
+
f.baz
|
data/_spike/hash_loop.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
x = [{a: "a"}, {b: "b"}, {c: "c"}]
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
def each_hash
|
5
|
+
self.each_pair do |key, value|
|
6
|
+
yield key => value
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def foo(hashes)
|
12
|
+
hashes.each_hash do |hash|
|
13
|
+
puts hash.inspect
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
foo(a: "a", b: "b", c: "c")
|
data/_spike/hashing.rb
ADDED
data/_spike/hook.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class Foo
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
singleton_class.send :alias_method, :__hook__, :bar
|
5
|
+
singleton_class.send :define_method, :bar do |*params|
|
6
|
+
puts "no mo ba"
|
7
|
+
send :__hook__
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def bar
|
12
|
+
puts "bar"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
f = Foo.new
|
18
|
+
|
19
|
+
f.bar
|
data/_spike/mod.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
def point_in_cone(args)
|
3
|
+
radius, angle, direction, bot, target = args[:radius], args[:angle], args[:direction], args[:bot], args[:target]
|
4
|
+
x1, y1, x2, y2 = bot[:x], bot[:y], target[:x], target[:y]
|
5
|
+
distance = Math.sqrt((x1 - target[:x])**2 + (y1 - y2)**2)
|
6
|
+
return false if distance > radius
|
7
|
+
|
8
|
+
azimuth = Math.atan2(x2 - x1, y2 - y1)
|
9
|
+
|
10
|
+
min = (Math::PI / 180) * ((direction - (angle / 2)) % 360)
|
11
|
+
max = (Math::PI / 180) * ((direction + (angle / 2)) % 360)
|
12
|
+
|
13
|
+
if min > max
|
14
|
+
azimuth >= min || azimuth <= max
|
15
|
+
else
|
16
|
+
azimuth >= min && azimuth <= max
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
puts point_in_cone radius: 10, angle: 90, direction: 0, bot: {x: 0, y: 0}, target: {x: 10, y: 0}
|
data/_spike/runner.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
class Arena
|
2
|
+
def initialize
|
3
|
+
@secret = "super secret"
|
4
|
+
end
|
5
|
+
|
6
|
+
def protected
|
7
|
+
puts "protected called"
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
class ArenaProxy
|
13
|
+
def initialize(arena)
|
14
|
+
@arena = arena
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class BasicBot
|
19
|
+
def initialize(arena_proxy)
|
20
|
+
@arena_proxy = arena_proxy
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Code
|
25
|
+
def self.loop
|
26
|
+
arena = Arena.new
|
27
|
+
ap = ArenaProxy.new(arena)
|
28
|
+
bot = BasicBot.new(ap)
|
29
|
+
yield bot
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Runner
|
34
|
+
secret = ""
|
35
|
+
Thread.start {
|
36
|
+
$SAFE = 4
|
37
|
+
Code.loop do |bot|
|
38
|
+
ap = bot.instance_variable_get(:@arena_proxy)
|
39
|
+
a = ap.instance_variable_get(:@arena)
|
40
|
+
secret = a.instance_variable_get(:@secret)
|
41
|
+
end
|
42
|
+
}.join
|
43
|
+
puts secret
|
44
|
+
end
|
data/_spike/safe.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Bar
|
2
|
+
def private_parts
|
3
|
+
"huge"
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class Foo
|
8
|
+
def initialize
|
9
|
+
@bar = Bar.new
|
10
|
+
@secret = ["secret"]
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
f = Foo.new
|
16
|
+
vars = []
|
17
|
+
vars.taint
|
18
|
+
Thread.start {
|
19
|
+
$SAFE = 3
|
20
|
+
bar = Bar.new
|
21
|
+
f.instance_variable_set :@secret, "hahahaha"
|
22
|
+
vars = f.instance_variable_get(:@secret)
|
23
|
+
bar = f.instance_variable_get(:@bar)
|
24
|
+
vars << "#{vars}, #{bar.private_parts}"
|
25
|
+
}.join
|
26
|
+
|
27
|
+
puts "vars: #{vars}"
|
28
|
+
puts "Done"
|
data/brawl.gemspec
CHANGED
@@ -8,10 +8,11 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["Mike Bethany"]
|
9
9
|
s.email = ["mikbe.tk@gmail.com"]
|
10
10
|
s.homepage = "http://mikbe.tk"
|
11
|
-
s.summary = %q{A
|
12
|
-
s.description = %q{
|
11
|
+
s.summary = %q{A robot combat game where you write Ruby programs to control your robots.}
|
12
|
+
s.description = %q{Write robotic assassins using Ruby and watch them as they do your evil bidding.}
|
13
13
|
|
14
|
-
s.
|
14
|
+
s.add_dependency "uuidtools"
|
15
|
+
s.add_dependency "eventable"
|
15
16
|
|
16
17
|
s.files = `git ls-files`.split("\n")
|
17
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -0,0 +1,173 @@
|
|
1
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), "/../lib"))
|
2
|
+
$: << '.'
|
3
|
+
|
4
|
+
require 'brawl'
|
5
|
+
|
6
|
+
battle_bot_code = <<CODE
|
7
|
+
code do |bot|
|
8
|
+
@dir ||= 0
|
9
|
+
targets = bot.scan direction: @dir, angle: 90
|
10
|
+
@dir += 45
|
11
|
+
@dir %= 360
|
12
|
+
unless targets.each do |target|
|
13
|
+
unless target[:class] == Brawl::Wall
|
14
|
+
results = shoot(target[:bearing])
|
15
|
+
end
|
16
|
+
end.empty?
|
17
|
+
else
|
18
|
+
if rand(0) > 0.5
|
19
|
+
bot.turn [:left,:right,:around].sample
|
20
|
+
end
|
21
|
+
bot.move rand(3) + 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
CODE
|
25
|
+
|
26
|
+
arena_data = {size: {width: 10, length: 10}}
|
27
|
+
clock_data = {tick_rate: 0.01}
|
28
|
+
|
29
|
+
motor = {Brawl::BasicMotor=>{move_max: 3, turn_max: 360}}
|
30
|
+
scanner = {Brawl::BasicScanner=>{scan_max: 10, angle_max: 180}}
|
31
|
+
weapon = {Brawl::BasicWeapon=>{range: 11, power: 1}}
|
32
|
+
parts = motor.merge(scanner).merge(weapon)
|
33
|
+
|
34
|
+
battle_bot1 =
|
35
|
+
{
|
36
|
+
name: "Battle Bot 1",
|
37
|
+
class: Brawl::BasicBot,
|
38
|
+
params: {parts: parts},
|
39
|
+
code: battle_bot_code
|
40
|
+
}
|
41
|
+
|
42
|
+
battle_bot2 =
|
43
|
+
{
|
44
|
+
name: "Battle Bot 2",
|
45
|
+
class: Brawl::BasicBot,
|
46
|
+
params: {parts: parts},
|
47
|
+
code: battle_bot_code
|
48
|
+
}
|
49
|
+
|
50
|
+
battle_bot3 =
|
51
|
+
{
|
52
|
+
name: "Battle Bot 3",
|
53
|
+
class: Brawl::BasicBot,
|
54
|
+
params: {parts: parts},
|
55
|
+
code: battle_bot_code
|
56
|
+
}
|
57
|
+
|
58
|
+
battle_bot4 =
|
59
|
+
{
|
60
|
+
name: "Battle Bot 4",
|
61
|
+
class: Brawl::BasicBot,
|
62
|
+
params: {parts: parts},
|
63
|
+
code: battle_bot_code
|
64
|
+
}
|
65
|
+
|
66
|
+
class LogView
|
67
|
+
|
68
|
+
def initialize(logfile)
|
69
|
+
@logfile = logfile
|
70
|
+
@file = nil
|
71
|
+
@verbosity_throttle = 0
|
72
|
+
@verbosity_level = 25
|
73
|
+
end
|
74
|
+
|
75
|
+
def open
|
76
|
+
@file = File.open(@logfile, "w")
|
77
|
+
end
|
78
|
+
|
79
|
+
def close
|
80
|
+
@file.flush
|
81
|
+
@file.close
|
82
|
+
end
|
83
|
+
|
84
|
+
def write(msg="")
|
85
|
+
@file.puts msg
|
86
|
+
end
|
87
|
+
|
88
|
+
def bot_msg_callback(*params)
|
89
|
+
|
90
|
+
bot_info = params[0]
|
91
|
+
method = params[1]
|
92
|
+
method_params = params[2]
|
93
|
+
@verbosity_throttle += 1
|
94
|
+
|
95
|
+
case method
|
96
|
+
when :damage
|
97
|
+
text = "#{bot_info[:name]}" +
|
98
|
+
" at #{bot_info[:location]}" +
|
99
|
+
" was hit for #{method_params} damage" +
|
100
|
+
" and now has #{bot_info[:health]} health."
|
101
|
+
text += " #{bot_info[:name]} is dead!" if bot_info[:health] == 0
|
102
|
+
@file.puts
|
103
|
+
@file.puts text
|
104
|
+
puts
|
105
|
+
puts text
|
106
|
+
when :scan
|
107
|
+
text = "#{bot_info[:name]}" +
|
108
|
+
" at #{bot_info[:location]}" +
|
109
|
+
" scans #{method_params}"
|
110
|
+
@file.puts
|
111
|
+
@file.puts text
|
112
|
+
if speak?
|
113
|
+
puts
|
114
|
+
puts text
|
115
|
+
end
|
116
|
+
when :move
|
117
|
+
text = "#{bot_info[:name]}" +
|
118
|
+
" at #{bot_info[:location]}" +
|
119
|
+
" moves #{method_params}"
|
120
|
+
@file.puts
|
121
|
+
@file.puts text
|
122
|
+
if speak?
|
123
|
+
puts
|
124
|
+
puts text
|
125
|
+
end
|
126
|
+
when :shoot
|
127
|
+
text = "#{bot_info[:name]}" +
|
128
|
+
" at #{bot_info[:location]}" +
|
129
|
+
" shoots at bearing #{method_params.round(1)}."
|
130
|
+
@file.puts
|
131
|
+
@file.puts text
|
132
|
+
puts
|
133
|
+
puts text
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def speak?
|
138
|
+
@verbosity_throttle % @verbosity_level == 0
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
battle = Brawl::BattleController.new
|
144
|
+
battle.make_arena(arena_data)
|
145
|
+
battle.make_clock(clock_data)
|
146
|
+
|
147
|
+
bot_data = [battle_bot1, battle_bot2, battle_bot3, battle_bot4]
|
148
|
+
battle.make_bots(bot_data)
|
149
|
+
log = LogView.new("logview.txt")
|
150
|
+
log.open
|
151
|
+
battle.register_for_event(
|
152
|
+
event: :bot_msg,
|
153
|
+
listener: log,
|
154
|
+
callback: :bot_msg_callback
|
155
|
+
)
|
156
|
+
|
157
|
+
battle.start
|
158
|
+
until battle.victory?
|
159
|
+
sleep(0.5)
|
160
|
+
end
|
161
|
+
|
162
|
+
winner = battle.arena.get_object(class: Brawl::BasicBot)
|
163
|
+
if winner
|
164
|
+
done_text = "Battle won!\n#{winner[:name]} is the champion!"
|
165
|
+
else
|
166
|
+
done_text = "Battle draw! The last two bots killed other simultaniously! Doh!"
|
167
|
+
end
|
168
|
+
|
169
|
+
puts
|
170
|
+
puts done_text
|
171
|
+
log.write
|
172
|
+
log.write done_text
|
173
|
+
log.close
|