Open amirrajan opened 5 years ago
module ADR
describe "rules of world events" do
[
{ :type => SwampEvent },
{ :type => ShipEvent },
{ :type => IronMineEvent },
{ :type => SulphurMineEvent },
{ :type => HouseEvent },
{ :type => CoalMineEvent },
{ :type => CaveEvent },
{ :type => BattlefieldEvent },
{ :type => BoreholeEvent },
{
:type => TownEvent,
:rolls => [
# a1 b1 c1 d1 end1
[ 0.35, 0.45, 0.45, 0.45, 0.45 ],
# a1 b1 c1 d1 end2
[ 0.35, 0.45, 0.45, 0.45, 0.95 ],
# a1 b2 c2 d1 end2
[ 0.35, 0.95, 0.45, 0.45, 0.95 ],
# a1 b2 c3 d1 end2
[ 0.35, 0.95, 0.95, 0.45, 0.95 ],
# a2 b3 c4 d2 end3
[ 0.95, 0.45, 0.45, 0.45, 0.45 ],
# a2 b4 c5 d2 end4
[ 0.95, 0.95, 0.45, 0.45, 0.95 ],
# a2 b4 c6 d2 end4
[ 0.95, 0.95, 0.95, 0.45, 0.95 ],
]
},
{
:type => CityEvent,
:rolls => [
# a1 b1 c1 d1 end1
[ 0.35, 0.45, 0.45, 0.45, 0.45 ],
# a1 b2 c2 d2 end2
[ 0.35, 0.95, 0.45, 0.95, 0.95 ],
# a1 b2 c3 d3 end3
[ 0.35, 0.95, 0.95, 0.95, 0.95 ],
# a2 b3 c4 d4 end4
[ 0.75, 0.45, 0.45, 0.45, 0.45 ],
# a2 b3 c4 d5 end5
[ 0.75, 0.45, 0.45, 0.95, 0.95 ],
# a2 b3 c5 d5 end5
[ 0.75, 0.45, 0.95, 0.95, 0.95 ],
# a2 b4 c5 d5 end5
[ 0.75, 0.95, 0.45, 0.45, 0.45 ],
# a2 b4 c6 d5 end5
[ 0.75, 0.95, 0.95, 0.45, 0.45 ],
# a2 b4 c6 d6 end5
[ 0.75, 0.95, 0.95, 0.95, 0.45 ],
# a2 b4 c6 d6 end6
[ 0.75, 0.95, 0.95, 0.95, 0.95 ],
# a3 b5 c7 d7 end7
[ 0.95, 0.45, 0.45, 0.45, 0.45 ],
# a3 b5 c7 d7 end8
[ 0.95, 0.45, 0.45, 0.45, 0.95 ],
# a3 b5 c7 d8 end8
[ 0.95, 0.45, 0.45, 0.95, 0.95 ],
# a3 b6 c8 d8 end8
[ 0.95, 0.95, 0.45, 0.95, 0.95 ],
# a3 b6 c9 d9 end9
[ 0.95, 0.95, 0.95, 0.95, 0.95 ],
]
},
].each do |row|
it "#{row[:type]}: each scene contains two or fewer options" do
for_all_scenes_of row[:type], row[:rolls] do |event|
current_scene(event).should have_one_or_two_options
end
end
it "#{row[:type]}: each scene has text associated with it" do
for_all_scenes_of row[:type], row[:rolls] do |event|
current_scene(event)[:text].class.should.equal Array
end
end
it "#{row[:type]}: loot scenes either have a continue and run, or leave option" do
for_all_scenes_of row[:type], row[:rolls] do |event|
if current_scene(event)[:loot]
current_scene(event).should have_loot_specific_options
end
end
end
it "#{row[:type]}: clears the event at the end (or becomes and outpost)" do
for_each_completion_of row[:type], row[:rolls] do |world, event|
if event.becomes_outpost?
world.landmarks[[30,30]].should.equal :outpost
elsif event.on_clear_replace_with
world.landmarks[[30,30]].should.equal event.on_clear_replace_with
else
world.cleared[[30,30]].should.equal true
end
end
end
it "#{row[:type]}: combat scenes only have continue and run" do
for_all_scenes_of row[:type], row[:rolls] do |event|
if current_scene(event)[:combat]
current_scene(event).should have_combat_specific_options
end
end
end
it "#{row[:type]}: accounts for all scenes" do
all_scenes = []
for_all_scenes_of row[:type], row[:rolls] do |event|
all_scenes << event.current_scene
end
event = row[:type].new(game_ready_to_play)
event.init_scenes
event.scenes.keys.sort.should.equal all_scenes.uniq.sort
end
end
def for_each_completion_of type, rolls = nil
game = game_ready_to_play
event = type.new game
event.init_scenes
rolls ||= rolls_for event
rolls.each do |roll|
game = game_ready_to_play
event = type.new game
event.init_scenes
turn_index = 0
Event.stub_roll roll[turn_index]
until move_forward(event) == :end
turn_index += 1
if roll[turn_index] != nil
Event.stub_roll roll[turn_index]
end
end
yield game.world, event
end
"no op".should.equal "no op" #prevents empty specification warning
end
def for_all_scenes_of type, rolls
game = game_ready_to_play
event = type.new game
event.init_scenes
rolls ||= rolls_for event
rolls.each do |roll|
game = game_ready_to_play
event = type.new game
event.init_scenes
turn_index = 0
Event.stub_roll roll[turn_index]
yield event
while move_forward(event) != :end
turn_index += 1
if roll[turn_index] != nil
Event.stub_roll roll[turn_index]
end
yield event
end
end
"no op".should.equal "no op" #prevents empty specification warning
end
def current_scene event
event.scenes[event.current_scene]
end
def move_forward event
next_scene = current_scene(event)[:options].keys.first
event.change_scene next_scene
event.current_scene
end
def rolls_for event
#this method finds all random rolls needed to traverse the event tree
#it's my attempt to avoid creating a recursive method that generates the tree
#and then attempts to traverse it
rolls = []
event.scenes.keys.each do |key|
scene = event.scenes[key]
scene[:options].keys.each do |option_key|
option = scene[:options][option_key]
if option[:next_scene].is_a?(Hash)
option[:next_scene].keys.each do |roll|
rolls << [roll - 0.05]
end
end
end
end
rolls << [1] if rolls.length == 0
rolls.uniq
end
def have_one_or_two_options
lambda do |scene|
if(scene[:options].keys.count == 1 or scene[:options].keys.count == 2)
true
else
raise "Scene #{scene} doesn't have the write amount of options"
end
end
end
def have_combat_specific_options
lambda do |scene|
passed = true
keys = scene[:options].keys
if keys.count != 2
passed = false
message = "combat scene didn't contain exactly two options"
end
if !scene[:max_health] or
!scene[:health] or
!scene[:attack_delay] or
!scene[:enemy] or
!scene[:damage]
passed = false
message = "combat scene is missing max_health, health, enemy, damage or attack delay"
end
if keys.first != :continue
passed = false
message = "combat scene's first option wasn't continue"
end
if keys[-1] != :run
passed = false
message = "combat scene's last option wasn't run"
end
if !passed
raise "combat scene doesn't have the right stuff (#{message}) #{scene}"
end
passed
end
end
def have_loot_specific_options
lambda do |scene|
passed = true
keys = scene[:options].keys
message = ""
if keys.count > 2 or keys.count < 1
passed = false
message = "loot options where greater than 2 or less then 1"
end
guarenteed_loot = false
scene[:loot].keys.each do |loot_key|
if scene[:loot][loot_key][:chance] > 0.98
guarenteed_loot = true
end
end
if !guarenteed_loot
passed = false
message = "loot not guranteed for scene"
end
if keys.count == 2
if keys.first != :continue
passed = false
message = "it had two options, but the first option wasn't :continue"
end
if keys[-1] != :run
passed = false
message = "it had two options, but the last option wasn't :run"
end
end
if keys.count == 1
if keys.first != :leave
passed = false
message = "it had one options, but the option wasn't :leave"
end
end
if !passed
raise "loot scene doesn't have the right stuff (#{message}) #{scene}"
end
passed
end
end
def events
World.event_map.keys.map { |key| World.event_map[key] }
end
def game_ready_to_play
game = Game.new
game.load
game.game_state.clear
game.room.unlock_forest
game.room.builder_ready
game.room.unlock_crafts
game.world.stores[:torch] = 10000
game.world.stores[:cure_meat] = 10000
game.world.stores[:jewel] = 10000
game
end
end
end
module ADR
describe Room do
before do
@game = Game.new
@game.load
@game.game_state.clear
@tick_executed = false
end
def update
@tick_executed = true
end
it "title is based off of fire" do
@game.room.title.should.equal "a dark room"
end
describe "stoking and lighting fire after forest" do
before do
@game = Game.new
@game.load
@game.game_state.clear
@game.room.light_fire
@game.room.unlock_forest
end
it "requires 1 wood to stoke fire" do
@game.room.stoke_ready_after.times { @game.tick }
@game.room.begining_of_game.should.equal false
@game.room.stoke.should.equal true
@game.stores[:wood].should.equal 3
end
it "isn't preformed if there aren't enough funds" do
@game.room.stoke_ready_after.times { @game.tick }
@game.stores[:wood] = 0
@game.room.stoke.should.equal false
end
it "requires 5 wood to light fire" do
@game.room.fire = :dead
@game.room.light_fire.should.equal false
@game.stores[:wood] = 10
@game.room.light_fire.should.equal true
@game.stores[:wood].should.equal 5
end
end
describe "starting and stoking fire" do
it "a fire can be lit" do
@game.room.light_fire
@game.room.fire.should.equal :burning
end
it "allows stoking after a timer interval has passed" do
@game.room.light_fire
@game.room.can_stoke?.should.equal false
@game.room.stoke_ready_after.times do |i|
@game.room.current_stoke_ticks.should.equal i
@game.tick
end
@game.room.can_stoke?.should.equal true
end
it "stoke is ignored if it cannot stoke" do
@game.room.light_fire
@game.room.can_stoke?.should.equal false
@game.room.stoke
@game.room.fire.should.equal :burning
end
it "current stoke ticks doesn't surpass ready ticks" do
@game.room.light_fire
(@game.room.stoke_ready_after + 1).times do |i|
@game.tick
end
@game.room.current_stoke_ticks.should.equal @game.room.stoke_ready_after
end
it "stoke ticks remains zero if fire is dead" do
@game.tick
@game.room.current_stoke_ticks.should.equal 0
end
it "stoking interval and fire status increase reset after stoking" do
@game.room.light_fire
@game.room.stoke_ready_after.times { @game.tick }
@game.room.stoke
@game.room.fire.should.equal :roaring
@game.room.can_stoke?.should.equal false
end
it "initialize the game with fire state" do
@game.room.tick
fire_history.last.should.equal "the fire is dead."
end
it "notifies the state of the fire" do
@game.room.light_fire
@game.room.tick
fire_history.last.should.equal "the fire is burning."
end
it "records fire state everytime the fire is stoked" do
@game.tick
@game.room.light_fire
@game.room.stoke_ready_after.times { @game.tick }
@game.room.stoke
@game.room.stoke_ready_after.times { @game.tick }
@game.room.stoke
@game.tick
fire_history[0].should.equal "the fire is dead."
fire_history[1].should.equal "the fire is burning."
fire_history[2].should.equal "the fire is roaring."
fire_history[3].should.equal "the fire is roaring."
end
def fire_history
@game.history.select { |h| h.match /^the fire/ }
end
end
describe "cooling fire" do
it "cools if it hasn't been stoked" do
@game.room.light_fire
@game.room.stoke_ready_after.times { @game.tick }
@game.room.stoke
@game.room.fire.should.equal :roaring
@game.room.cool_fire_after.times { @game.tick }
@game.room.fire.should.equal :burning
@game.room.cool_fire_after.times { @game.tick }
@game.room.fire.should.equal :flickering
@game.room.cool_fire_after.times { @game.tick }
@game.room.fire.should.equal :smoldering
@game.room.cool_fire_after.times { @game.tick }
@game.room.fire.should.equal :dead
@game.room.cool_fire_after.times { @game.tick }
@game.room.fire.should.equal :dead
end
it "resets cooling if fire is stoked" do
@game.room.light_fire
@game.room.stoke_ready_after.times { @game.tick }
@game.room.stoke
@game.room.fire.should.equal :roaring
(@game.room.cool_fire_after - 1).times { @game.tick }
@game.room.stoke
(@game.room.cool_fire_after - 1).times { @game.tick }
@game.room.fire.should.equal :roaring
end
end
it "scripted unlock of forest" do
@game.room.light_fire
@game.room.builder_status_change_after.times { @game.tick }
@game.room.forest_unlocked?.should.equal false
@game.room.builder_status_change_after.times { @game.tick }
@game.room.forest_unlocked?.should.equal true
@game.stores[:wood].should.equal 4
end
describe "room heating" do
it "room heats up relative to fire's current state, but doesn't move past" do
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.temperature.should.equal :freezing
@game.room.light_fire
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.temperature.should.equal :cold
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.temperature.should.equal :mild
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.temperature.should.equal :warm
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.temperature.should.equal :warm
@game.room.stoke
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.temperature.should.equal :hot
end
it "records temp changes" do
@game.tick
@game.room.light_fire
@game.room.room_temp_changes_after.times { @game.tick }
temperature_history[0].should.equal "the room is cold."
temperature_history[1].should.equal "the room is mild."
end
it "records cooling temperatures" do
@game.tick
@game.room.light_fire
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.temperature.should.equal :warm
@game.room.stoke
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.temperature.should.equal :hot
@game.room.cool_fire_after.times { @game.tick }
@game.room.fire.should.equal :burning
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.temperature.should.equal :warm
temperature_history.last.should.equal "the room is warm."
end
def temperature_history
@game.history.select { |h| h.match /the room/ }
end
end
it "builder squence" do
@game.room.light_fire
@game.room.builder_status_change_after.times { @game.tick }
@game.history
.select { |h| h.match /from the windows/ }
.first
.should
.equal "the light from the fire spills from the windows, out into the dark."
@game.room.builder_status_change_after.times { @game.tick }
@game.history
.select { |h| h.match /stumbles/ }
.count.should.equal 1
@game.room.room_temp_changes_after.times { @game.tick }
@game.room.builder_status_change_after.times { @game.tick }
@game.history
.select { |h| h.match /mumbles/ }
.count.should.equal 1
@game.room.builder_status_change_after.times { @game.tick }
@game.history
.select { |h| h.match /calms/ }
.count.should.equal 1
end
end
end
Here are some sample tests written with bacon. Feel free to add your own.