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.
Files changed (107) hide show
  1. data/CHANGELOG.rdoc +3 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +170 -0
  4. data/Rakefile +21 -0
  5. data/bin/rubywarrior +5 -0
  6. data/features/command_options.feature +46 -0
  7. data/features/levels.feature +51 -0
  8. data/features/profiles.feature +56 -0
  9. data/features/step_definitions/common_steps.rb +19 -0
  10. data/features/step_definitions/interaction_steps.rb +65 -0
  11. data/features/support/env.rb +12 -0
  12. data/features/support/mockio.rb +57 -0
  13. data/features/towers.feature +14 -0
  14. data/lib/ruby_warrior.rb +44 -0
  15. data/lib/ruby_warrior/abilities/attack.rb +24 -0
  16. data/lib/ruby_warrior/abilities/base.rb +36 -0
  17. data/lib/ruby_warrior/abilities/bind.rb +19 -0
  18. data/lib/ruby_warrior/abilities/direction_of.rb +13 -0
  19. data/lib/ruby_warrior/abilities/direction_of_stairs.rb +13 -0
  20. data/lib/ruby_warrior/abilities/distance.rb +13 -0
  21. data/lib/ruby_warrior/abilities/explode.rb +16 -0
  22. data/lib/ruby_warrior/abilities/feel.rb +13 -0
  23. data/lib/ruby_warrior/abilities/health.rb +13 -0
  24. data/lib/ruby_warrior/abilities/listen.rb +15 -0
  25. data/lib/ruby_warrior/abilities/look.rb +15 -0
  26. data/lib/ruby_warrior/abilities/pivot.rb +16 -0
  27. data/lib/ruby_warrior/abilities/rescue.rb +23 -0
  28. data/lib/ruby_warrior/abilities/rest.rb +20 -0
  29. data/lib/ruby_warrior/abilities/shoot.rb +23 -0
  30. data/lib/ruby_warrior/abilities/walk.rb +20 -0
  31. data/lib/ruby_warrior/config.rb +22 -0
  32. data/lib/ruby_warrior/core_additions.rb +25 -0
  33. data/lib/ruby_warrior/floor.rb +71 -0
  34. data/lib/ruby_warrior/game.rb +193 -0
  35. data/lib/ruby_warrior/level.rb +119 -0
  36. data/lib/ruby_warrior/level_loader.rb +56 -0
  37. data/lib/ruby_warrior/player_generator.rb +41 -0
  38. data/lib/ruby_warrior/position.rb +80 -0
  39. data/lib/ruby_warrior/profile.rb +104 -0
  40. data/lib/ruby_warrior/runner.rb +33 -0
  41. data/lib/ruby_warrior/space.rb +63 -0
  42. data/lib/ruby_warrior/tower.rb +14 -0
  43. data/lib/ruby_warrior/turn.rb +38 -0
  44. data/lib/ruby_warrior/ui.rb +54 -0
  45. data/lib/ruby_warrior/units/archer.rb +34 -0
  46. data/lib/ruby_warrior/units/base.rb +95 -0
  47. data/lib/ruby_warrior/units/captive.rb +30 -0
  48. data/lib/ruby_warrior/units/sludge.rb +30 -0
  49. data/lib/ruby_warrior/units/thick_sludge.rb +13 -0
  50. data/lib/ruby_warrior/units/warrior.rb +58 -0
  51. data/lib/ruby_warrior/units/wizard.rb +34 -0
  52. data/spec/fixtures/short-tower/level_001.rb +15 -0
  53. data/spec/fixtures/short-tower/level_002.rb +15 -0
  54. data/spec/fixtures/walking_player.rb +5 -0
  55. data/spec/ruby_warrior/abilities/attack_spec.rb +51 -0
  56. data/spec/ruby_warrior/abilities/base_spec.rb +24 -0
  57. data/spec/ruby_warrior/abilities/bind_spec.rb +19 -0
  58. data/spec/ruby_warrior/abilities/direction_of_spec.rb +13 -0
  59. data/spec/ruby_warrior/abilities/direction_of_stairs_spec.rb +13 -0
  60. data/spec/ruby_warrior/abilities/distance_spec.rb +13 -0
  61. data/spec/ruby_warrior/abilities/explode_spec.rb +21 -0
  62. data/spec/ruby_warrior/abilities/feel_spec.rb +13 -0
  63. data/spec/ruby_warrior/abilities/health_spec.rb +13 -0
  64. data/spec/ruby_warrior/abilities/listen_spec.rb +17 -0
  65. data/spec/ruby_warrior/abilities/look_spec.rb +15 -0
  66. data/spec/ruby_warrior/abilities/pivot_spec.rb +18 -0
  67. data/spec/ruby_warrior/abilities/rescue_spec.rb +40 -0
  68. data/spec/ruby_warrior/abilities/rest_spec.rb +29 -0
  69. data/spec/ruby_warrior/abilities/shoot_spec.rb +22 -0
  70. data/spec/ruby_warrior/abilities/walk_spec.rb +25 -0
  71. data/spec/ruby_warrior/floor_spec.rb +81 -0
  72. data/spec/ruby_warrior/game_spec.rb +119 -0
  73. data/spec/ruby_warrior/level_loader_spec.rb +54 -0
  74. data/spec/ruby_warrior/level_spec.rb +203 -0
  75. data/spec/ruby_warrior/player_generator_spec.rb +12 -0
  76. data/spec/ruby_warrior/position_spec.rb +103 -0
  77. data/spec/ruby_warrior/profile_spec.rb +144 -0
  78. data/spec/ruby_warrior/space_spec.rb +165 -0
  79. data/spec/ruby_warrior/tower_spec.rb +15 -0
  80. data/spec/ruby_warrior/turn_spec.rb +42 -0
  81. data/spec/ruby_warrior/ui_spec.rb +93 -0
  82. data/spec/ruby_warrior/units/archer_spec.rb +23 -0
  83. data/spec/ruby_warrior/units/base_spec.rb +133 -0
  84. data/spec/ruby_warrior/units/captive_spec.rb +34 -0
  85. data/spec/ruby_warrior/units/sludge_spec.rb +27 -0
  86. data/spec/ruby_warrior/units/thick_sludge_spec.rb +19 -0
  87. data/spec/ruby_warrior/units/warrior_spec.rb +60 -0
  88. data/spec/ruby_warrior/units/wizard_spec.rb +23 -0
  89. data/spec/spec_helper.rb +10 -0
  90. data/templates/README.erb +20 -0
  91. data/templates/player.rb +5 -0
  92. data/towers/beginner/level_001.rb +16 -0
  93. data/towers/beginner/level_002.rb +18 -0
  94. data/towers/beginner/level_003.rb +21 -0
  95. data/towers/beginner/level_004.rb +18 -0
  96. data/towers/beginner/level_005.rb +22 -0
  97. data/towers/beginner/level_006.rb +19 -0
  98. data/towers/beginner/level_007.rb +18 -0
  99. data/towers/beginner/level_008.rb +21 -0
  100. data/towers/beginner/level_009.rb +20 -0
  101. data/towers/intermediate/level_001.rb +18 -0
  102. data/towers/intermediate/level_002.rb +20 -0
  103. data/towers/intermediate/level_003.rb +23 -0
  104. data/towers/intermediate/level_004.rb +24 -0
  105. data/towers/intermediate/level_005.rb +19 -0
  106. data/towers/intermediate/level_006.rb +24 -0
  107. metadata +167 -0
@@ -0,0 +1,12 @@
1
+ require 'cucumber'
2
+ require 'spec'
3
+
4
+ require File.dirname(__FILE__) + '/../../lib/ruby_warrior'
5
+
6
+ Before do
7
+ RubyWarrior::Config.reset
8
+ end
9
+
10
+ After do
11
+ FileUtils.rm_rf "towers/short"
12
+ end
@@ -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
+
@@ -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,13 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Distance < Base
4
+ def description
5
+ "Returns the number of spaces the stairs are away."
6
+ end
7
+
8
+ def perform
9
+ @unit.position.distance_from_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,13 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Feel < Base
4
+ def description
5
+ "Returns a Space for the given direction (forward by default)."
6
+ end
7
+
8
+ def perform(direction = :forward)
9
+ space(direction)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module RubyWarrior
2
+ module Abilities
3
+ class Health < Base
4
+ def description
5
+ "Returns an integer representing your health."
6
+ end
7
+
8
+ def perform
9
+ @unit.health
10
+ end
11
+ end
12
+ end
13
+ 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