rubywarrior 0.1.0
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/CHANGELOG.rdoc +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +170 -0
- data/Rakefile +21 -0
- data/bin/rubywarrior +5 -0
- data/features/command_options.feature +46 -0
- data/features/levels.feature +51 -0
- data/features/profiles.feature +56 -0
- data/features/step_definitions/common_steps.rb +19 -0
- data/features/step_definitions/interaction_steps.rb +65 -0
- data/features/support/env.rb +12 -0
- data/features/support/mockio.rb +57 -0
- data/features/towers.feature +14 -0
- data/lib/ruby_warrior.rb +44 -0
- data/lib/ruby_warrior/abilities/attack.rb +24 -0
- data/lib/ruby_warrior/abilities/base.rb +36 -0
- data/lib/ruby_warrior/abilities/bind.rb +19 -0
- data/lib/ruby_warrior/abilities/direction_of.rb +13 -0
- data/lib/ruby_warrior/abilities/direction_of_stairs.rb +13 -0
- data/lib/ruby_warrior/abilities/distance.rb +13 -0
- data/lib/ruby_warrior/abilities/explode.rb +16 -0
- data/lib/ruby_warrior/abilities/feel.rb +13 -0
- data/lib/ruby_warrior/abilities/health.rb +13 -0
- data/lib/ruby_warrior/abilities/listen.rb +15 -0
- data/lib/ruby_warrior/abilities/look.rb +15 -0
- data/lib/ruby_warrior/abilities/pivot.rb +16 -0
- data/lib/ruby_warrior/abilities/rescue.rb +23 -0
- data/lib/ruby_warrior/abilities/rest.rb +20 -0
- data/lib/ruby_warrior/abilities/shoot.rb +23 -0
- data/lib/ruby_warrior/abilities/walk.rb +20 -0
- data/lib/ruby_warrior/config.rb +22 -0
- data/lib/ruby_warrior/core_additions.rb +25 -0
- data/lib/ruby_warrior/floor.rb +71 -0
- data/lib/ruby_warrior/game.rb +193 -0
- data/lib/ruby_warrior/level.rb +119 -0
- data/lib/ruby_warrior/level_loader.rb +56 -0
- data/lib/ruby_warrior/player_generator.rb +41 -0
- data/lib/ruby_warrior/position.rb +80 -0
- data/lib/ruby_warrior/profile.rb +104 -0
- data/lib/ruby_warrior/runner.rb +33 -0
- data/lib/ruby_warrior/space.rb +63 -0
- data/lib/ruby_warrior/tower.rb +14 -0
- data/lib/ruby_warrior/turn.rb +38 -0
- data/lib/ruby_warrior/ui.rb +54 -0
- data/lib/ruby_warrior/units/archer.rb +34 -0
- data/lib/ruby_warrior/units/base.rb +95 -0
- data/lib/ruby_warrior/units/captive.rb +30 -0
- data/lib/ruby_warrior/units/sludge.rb +30 -0
- data/lib/ruby_warrior/units/thick_sludge.rb +13 -0
- data/lib/ruby_warrior/units/warrior.rb +58 -0
- data/lib/ruby_warrior/units/wizard.rb +34 -0
- data/spec/fixtures/short-tower/level_001.rb +15 -0
- data/spec/fixtures/short-tower/level_002.rb +15 -0
- data/spec/fixtures/walking_player.rb +5 -0
- data/spec/ruby_warrior/abilities/attack_spec.rb +51 -0
- data/spec/ruby_warrior/abilities/base_spec.rb +24 -0
- data/spec/ruby_warrior/abilities/bind_spec.rb +19 -0
- data/spec/ruby_warrior/abilities/direction_of_spec.rb +13 -0
- data/spec/ruby_warrior/abilities/direction_of_stairs_spec.rb +13 -0
- data/spec/ruby_warrior/abilities/distance_spec.rb +13 -0
- data/spec/ruby_warrior/abilities/explode_spec.rb +21 -0
- data/spec/ruby_warrior/abilities/feel_spec.rb +13 -0
- data/spec/ruby_warrior/abilities/health_spec.rb +13 -0
- data/spec/ruby_warrior/abilities/listen_spec.rb +17 -0
- data/spec/ruby_warrior/abilities/look_spec.rb +15 -0
- data/spec/ruby_warrior/abilities/pivot_spec.rb +18 -0
- data/spec/ruby_warrior/abilities/rescue_spec.rb +40 -0
- data/spec/ruby_warrior/abilities/rest_spec.rb +29 -0
- data/spec/ruby_warrior/abilities/shoot_spec.rb +22 -0
- data/spec/ruby_warrior/abilities/walk_spec.rb +25 -0
- data/spec/ruby_warrior/floor_spec.rb +81 -0
- data/spec/ruby_warrior/game_spec.rb +119 -0
- data/spec/ruby_warrior/level_loader_spec.rb +54 -0
- data/spec/ruby_warrior/level_spec.rb +203 -0
- data/spec/ruby_warrior/player_generator_spec.rb +12 -0
- data/spec/ruby_warrior/position_spec.rb +103 -0
- data/spec/ruby_warrior/profile_spec.rb +144 -0
- data/spec/ruby_warrior/space_spec.rb +165 -0
- data/spec/ruby_warrior/tower_spec.rb +15 -0
- data/spec/ruby_warrior/turn_spec.rb +42 -0
- data/spec/ruby_warrior/ui_spec.rb +93 -0
- data/spec/ruby_warrior/units/archer_spec.rb +23 -0
- data/spec/ruby_warrior/units/base_spec.rb +133 -0
- data/spec/ruby_warrior/units/captive_spec.rb +34 -0
- data/spec/ruby_warrior/units/sludge_spec.rb +27 -0
- data/spec/ruby_warrior/units/thick_sludge_spec.rb +19 -0
- data/spec/ruby_warrior/units/warrior_spec.rb +60 -0
- data/spec/ruby_warrior/units/wizard_spec.rb +23 -0
- data/spec/spec_helper.rb +10 -0
- data/templates/README.erb +20 -0
- data/templates/player.rb +5 -0
- data/towers/beginner/level_001.rb +16 -0
- data/towers/beginner/level_002.rb +18 -0
- data/towers/beginner/level_003.rb +21 -0
- data/towers/beginner/level_004.rb +18 -0
- data/towers/beginner/level_005.rb +22 -0
- data/towers/beginner/level_006.rb +19 -0
- data/towers/beginner/level_007.rb +18 -0
- data/towers/beginner/level_008.rb +21 -0
- data/towers/beginner/level_009.rb +20 -0
- data/towers/intermediate/level_001.rb +18 -0
- data/towers/intermediate/level_002.rb +20 -0
- data/towers/intermediate/level_003.rb +23 -0
- data/towers/intermediate/level_004.rb +24 -0
- data/towers/intermediate/level_005.rb +19 -0
- data/towers/intermediate/level_006.rb +24 -0
- metadata +167 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
class MockIO
|
|
2
|
+
class Timeout < Exception
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
def initialize(receiver = nil)
|
|
6
|
+
@buffer = ""
|
|
7
|
+
@receiver = receiver || MockIO.new(self)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def gets
|
|
11
|
+
1000.times do |n|
|
|
12
|
+
sleep 0.01 if n > 800 # throttle if it seems to be taking a while
|
|
13
|
+
unless @buffer.empty?
|
|
14
|
+
content = @buffer
|
|
15
|
+
@buffer = ""
|
|
16
|
+
return content
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
raise Timeout, "MockIO Timeout: No content was received for gets."
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def gets_until_include(phrase)
|
|
23
|
+
content = gets
|
|
24
|
+
while !content.include?(phrase)
|
|
25
|
+
begin
|
|
26
|
+
content += gets
|
|
27
|
+
rescue Timeout
|
|
28
|
+
raise "Unable to find \"#{phrase}\" in \"#{content}\""
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
content
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# TODO make this thread safe
|
|
35
|
+
def puts(str)
|
|
36
|
+
@receiver << "#{str}\n"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def print(str)
|
|
40
|
+
@receiver << str.to_s
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def <<(str)
|
|
44
|
+
@buffer << str
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def start
|
|
48
|
+
main_thread = Thread.current
|
|
49
|
+
Thread.new do
|
|
50
|
+
begin
|
|
51
|
+
yield(@receiver)
|
|
52
|
+
rescue Exception => e
|
|
53
|
+
main_thread.raise(e)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Feature: Manage towers
|
|
2
|
+
In order extend ruby warrior
|
|
3
|
+
As a developer
|
|
4
|
+
I want to manage towers
|
|
5
|
+
|
|
6
|
+
Background:
|
|
7
|
+
Given no profile at "tmp"
|
|
8
|
+
|
|
9
|
+
Scenario: Add a tower
|
|
10
|
+
When I copy fixture "short-tower" to "towers/short"
|
|
11
|
+
And I run rubywarrior
|
|
12
|
+
And I answer "y" to "create one?"
|
|
13
|
+
Then I should see "short"
|
|
14
|
+
|
data/lib/ruby_warrior.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
$: << File.dirname(__FILE__)
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
require 'ruby_warrior/core_additions'
|
|
6
|
+
|
|
7
|
+
require 'ruby_warrior/runner'
|
|
8
|
+
require 'ruby_warrior/game'
|
|
9
|
+
require 'ruby_warrior/profile'
|
|
10
|
+
require 'ruby_warrior/ui'
|
|
11
|
+
require 'ruby_warrior/config'
|
|
12
|
+
require 'ruby_warrior/player_generator'
|
|
13
|
+
require 'ruby_warrior/level_loader'
|
|
14
|
+
require 'ruby_warrior/tower'
|
|
15
|
+
require 'ruby_warrior/level'
|
|
16
|
+
require 'ruby_warrior/turn'
|
|
17
|
+
require 'ruby_warrior/floor'
|
|
18
|
+
require 'ruby_warrior/space'
|
|
19
|
+
require 'ruby_warrior/position'
|
|
20
|
+
|
|
21
|
+
require 'ruby_warrior/units/base'
|
|
22
|
+
require 'ruby_warrior/units/warrior'
|
|
23
|
+
require 'ruby_warrior/units/sludge'
|
|
24
|
+
require 'ruby_warrior/units/archer'
|
|
25
|
+
require 'ruby_warrior/units/thick_sludge'
|
|
26
|
+
require 'ruby_warrior/units/captive'
|
|
27
|
+
require 'ruby_warrior/units/wizard'
|
|
28
|
+
|
|
29
|
+
require 'ruby_warrior/abilities/base'
|
|
30
|
+
require 'ruby_warrior/abilities/walk'
|
|
31
|
+
require 'ruby_warrior/abilities/attack'
|
|
32
|
+
require 'ruby_warrior/abilities/feel'
|
|
33
|
+
require 'ruby_warrior/abilities/rest'
|
|
34
|
+
require 'ruby_warrior/abilities/health'
|
|
35
|
+
require 'ruby_warrior/abilities/look'
|
|
36
|
+
require 'ruby_warrior/abilities/shoot'
|
|
37
|
+
require 'ruby_warrior/abilities/rescue'
|
|
38
|
+
require 'ruby_warrior/abilities/pivot'
|
|
39
|
+
require 'ruby_warrior/abilities/distance'
|
|
40
|
+
require 'ruby_warrior/abilities/bind'
|
|
41
|
+
require 'ruby_warrior/abilities/listen'
|
|
42
|
+
require 'ruby_warrior/abilities/direction_of_stairs'
|
|
43
|
+
require 'ruby_warrior/abilities/direction_of'
|
|
44
|
+
require 'ruby_warrior/abilities/explode'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Attack < Base
|
|
4
|
+
def description
|
|
5
|
+
"Attack the unit in given direction (forward by default)."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform(direction = :forward)
|
|
9
|
+
receiver = unit(direction)
|
|
10
|
+
if receiver
|
|
11
|
+
@unit.say "attacks #{receiver}"
|
|
12
|
+
if direction == :backward
|
|
13
|
+
power = (@unit.attack_power/2.0).ceil
|
|
14
|
+
else
|
|
15
|
+
power = @unit.attack_power
|
|
16
|
+
end
|
|
17
|
+
damage(receiver, power)
|
|
18
|
+
else
|
|
19
|
+
@unit.say "attacks and hits nothing"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Base
|
|
4
|
+
DIRECTIONS = {
|
|
5
|
+
:forward => [1, 0],
|
|
6
|
+
:right => [0, 1],
|
|
7
|
+
:backward => [-1, 0],
|
|
8
|
+
:left => [0, -1]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def initialize(unit)
|
|
12
|
+
@unit = unit
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def offset(direction, amount = 1)
|
|
16
|
+
DIRECTIONS[direction].map { |i| i*amount }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def space(direction, amount = 1)
|
|
20
|
+
@unit.position.relative_space(*offset(direction, amount))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def unit(direction, amount = 1)
|
|
24
|
+
space(direction, amount).unit
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def damage(receiver, amount)
|
|
28
|
+
receiver.take_damage(amount)
|
|
29
|
+
@unit.earn_points(receiver.max_health) unless receiver.alive?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def description
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Bind < Base
|
|
4
|
+
def description
|
|
5
|
+
"Bind unit in given direction to keep him from moving (forward by default)."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform(direction = :forward)
|
|
9
|
+
receiver = unit(direction)
|
|
10
|
+
if receiver
|
|
11
|
+
@unit.say "binds #{receiver}"
|
|
12
|
+
receiver.bind
|
|
13
|
+
else
|
|
14
|
+
@unit.say "binds nothing"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class DirectionOf < Base
|
|
4
|
+
def description
|
|
5
|
+
"Pass a Space as an argument, and the direction (:left, :right, :forward, :backward) to that space will be returned."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform(space)
|
|
9
|
+
@unit.position.relative_direction_of(space)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class DirectionOfStairs < Base
|
|
4
|
+
def description
|
|
5
|
+
"Returns the direction (:left, :right, :forward, :backward) the stairs are from your location."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform
|
|
9
|
+
@unit.position.relative_direction_of_stairs
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Explode < Base
|
|
4
|
+
def description
|
|
5
|
+
"Kills you. You probably don't want to do this intentionally."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform
|
|
9
|
+
if @unit.position
|
|
10
|
+
@unit.say "explodes"
|
|
11
|
+
@unit.take_damage(@unit.health)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Listen < Base
|
|
4
|
+
def description
|
|
5
|
+
"Returns an array of all spaces which have units in them."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform
|
|
9
|
+
@unit.position.floor.units.map do |unit|
|
|
10
|
+
unit.position.space unless unit == @unit
|
|
11
|
+
end.compact
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Look < Base
|
|
4
|
+
def description
|
|
5
|
+
"Returns an array of Spaces in given direction (forward by default)."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform(direction = :forward)
|
|
9
|
+
[1, 2, 3].map do |amount|
|
|
10
|
+
space(direction, amount)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Pivot < Base
|
|
4
|
+
ROTATION_DIRECTIONS = [:forward, :right, :backward, :left]
|
|
5
|
+
|
|
6
|
+
def description
|
|
7
|
+
"Rotate :left, :right or :backward (default)"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def perform(direction = :backward)
|
|
11
|
+
@unit.position.rotate(ROTATION_DIRECTIONS.index(direction))
|
|
12
|
+
@unit.say "pivots #{direction}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Rescue < Base
|
|
4
|
+
def description
|
|
5
|
+
"Rescue a captive from his chains (earning 20 points) in given direction (forward by default)."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform(direction = :forward)
|
|
9
|
+
if space(direction).captive?
|
|
10
|
+
recipient = unit(direction)
|
|
11
|
+
@unit.say "unbinds #{recipient}"
|
|
12
|
+
recipient.unbind
|
|
13
|
+
if recipient.kind_of? Units::Captive
|
|
14
|
+
recipient.position = nil
|
|
15
|
+
@unit.earn_points(20)
|
|
16
|
+
end
|
|
17
|
+
else
|
|
18
|
+
@unit.say "rescues nothing"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Rest < Base
|
|
4
|
+
def description
|
|
5
|
+
"Gain 10% of max health back, but do nothing more."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform
|
|
9
|
+
if @unit.health < @unit.max_health
|
|
10
|
+
amount = (@unit.max_health*0.1).round
|
|
11
|
+
amount = @unit.max_health-@unit.health if (@unit.health + amount) > @unit.max_health
|
|
12
|
+
@unit.health += amount
|
|
13
|
+
@unit.say "receives #{amount} health from resting, up to #{@unit.health} health"
|
|
14
|
+
else
|
|
15
|
+
@unit.say "is already fit as a fiddle"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Shoot < Base
|
|
4
|
+
def description
|
|
5
|
+
"Shoot your bow & arrow in given direction (forward by default)."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform(direction = :forward)
|
|
9
|
+
receiver = multi_unit(direction, 1..3).compact.first
|
|
10
|
+
if receiver
|
|
11
|
+
@unit.say "shoots #{receiver}"
|
|
12
|
+
damage(receiver, @unit.shoot_power)
|
|
13
|
+
else
|
|
14
|
+
@unit.say "shoots and hits nothing"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def multi_unit(direction, range)
|
|
19
|
+
range.map { |n| unit(direction, n) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module RubyWarrior
|
|
2
|
+
module Abilities
|
|
3
|
+
class Walk < Base
|
|
4
|
+
def description
|
|
5
|
+
"Move in given direction (forward by default)."
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def perform(direction = :forward)
|
|
9
|
+
if @unit.position
|
|
10
|
+
@unit.say "walks #{direction}"
|
|
11
|
+
if space(direction).empty?
|
|
12
|
+
@unit.position.move(*offset(direction))
|
|
13
|
+
else
|
|
14
|
+
@unit.say "bumps into #{space(direction)}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|