zombie-chaser 0.0.3 → 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/History.txt +22 -0
- data/README.txt +57 -47
- data/Rakefile +26 -24
- data/bin/zombie-chaser +77 -79
- data/lib/{chaser.rb → zombie-chaser/chaser.rb} +392 -373
- data/lib/zombie-chaser/human.rb +312 -0
- data/{ui → lib/zombie-chaser}/icons/death.png +0 -0
- data/{ui → lib/zombie-chaser}/icons/robot.png +0 -0
- data/lib/zombie-chaser/interface.rb +153 -0
- data/{ui → lib/zombie-chaser}/sprites/robot-attacking.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/robot-dead.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/robot-dying.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/robot-idle.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/robot-moving.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/robot-turning.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/tank-attacking.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/tank-dead.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/tank-idle.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/tank-moving.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/tank-turning.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/witch-attacking.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/witch-dead.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/witch-idle.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/witch-moving.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/witch-turning.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/zombie-attacking.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/zombie-dead.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/zombie-dying.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/zombie-idle.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/zombie-moving.png +0 -0
- data/{ui → lib/zombie-chaser}/sprites/zombie-turning.png +0 -0
- data/lib/zombie-chaser/test_unit_handler.rb +78 -0
- data/{ui → lib/zombie-chaser}/tiles/grass.png +0 -0
- data/{ui → lib/zombie-chaser}/tiles/shrubbery.png +0 -0
- data/{ui → lib/zombie-chaser}/ui.rb +165 -127
- data/lib/{world.rb → zombie-chaser/world.rb} +105 -98
- data/lib/zombie-chaser/zombie_test_chaser.rb +139 -0
- data/test/fixtures/chased.rb +56 -56
- data/test/integration.rb +58 -0
- data/test/test_chaser.rb +150 -144
- data/test/test_unit.rb +2 -2
- data/test/test_zombie.rb +302 -108
- data/zombie-chaser.gemspec +88 -88
- metadata +40 -46
- data/lib/human.rb +0 -189
- data/lib/interface.rb +0 -86
- data/lib/test_unit_handler.rb +0 -43
- data/lib/zombie_test_chaser.rb +0 -133
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require "test/unit/collector/objectspace"
|
2
|
+
require "test/unit/ui/testrunnermediator"
|
3
|
+
|
4
|
+
require "thread"
|
5
|
+
|
6
|
+
class TestUnitHandler
|
7
|
+
attr_reader :test_suite_size, :result_queue
|
8
|
+
|
9
|
+
def initialize(test_pattern)
|
10
|
+
raise "Error: can't detect any files in test pattern #{test_pattern.inspect} (Don't forget to use forward slashes even in Windows)" if Dir.glob(test_pattern).empty?
|
11
|
+
Dir.glob(test_pattern).each {|test| require test} #In heckle, this is separated out
|
12
|
+
obj_sp = Test::Unit::Collector::ObjectSpace.new
|
13
|
+
test_suite = Test::Unit::TestSuite.new("Mutation slayer test suite")
|
14
|
+
test_suite << obj_sp.collect
|
15
|
+
@test_suite_size = test_suite.size
|
16
|
+
@test_runner_mediator = Test::Unit::UI::TestRunnerMediator.new(test_suite)
|
17
|
+
@test_runner_mediator.add_listener(Test::Unit::TestResult::FAULT) {test_failed}
|
18
|
+
@test_runner_mediator.add_listener(Test::Unit::TestCase::FINISHED) {test_finished}
|
19
|
+
@result_queue = Queue.new
|
20
|
+
@failure_encountered = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
begin
|
25
|
+
catch(:stop_test_runner) do
|
26
|
+
@test_runner_mediator.run_suite
|
27
|
+
end
|
28
|
+
rescue Chaser::Timeout
|
29
|
+
@result_queue.enq(:failure)
|
30
|
+
raise
|
31
|
+
ensure
|
32
|
+
@result_queue.enq(:end_of_work)
|
33
|
+
end
|
34
|
+
not @failure_encountered
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_failed
|
38
|
+
@result_queue.enq(:failure)
|
39
|
+
@failure_encountered = true
|
40
|
+
throw :stop_test_runner
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_finished
|
44
|
+
@result_queue.enq(:pass)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class MockTestHandler
|
50
|
+
attr_reader :result_queue
|
51
|
+
|
52
|
+
def initialize(results)
|
53
|
+
@results = results
|
54
|
+
@result_queue = Queue.new
|
55
|
+
@failure_encountered = false
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_suite_size
|
59
|
+
@results.size
|
60
|
+
end
|
61
|
+
|
62
|
+
def run
|
63
|
+
begin
|
64
|
+
@results.each do |result|
|
65
|
+
raise Timeout::Error, "Out of time" if result == :timeout
|
66
|
+
@result_queue.enq(result)
|
67
|
+
if result == :failure
|
68
|
+
@failure_encountered = true
|
69
|
+
break
|
70
|
+
end
|
71
|
+
end
|
72
|
+
ensure
|
73
|
+
@result_queue.enq(:end_of_work)
|
74
|
+
end
|
75
|
+
not @failure_encountered
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
File without changes
|
File without changes
|
@@ -1,127 +1,165 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
1
|
+
begin
|
2
|
+
require 'gosu'
|
3
|
+
rescue LoadError => e
|
4
|
+
require 'ostruct'
|
5
|
+
module Gosu
|
6
|
+
class Window
|
7
|
+
attr_accessor :caption, :grid
|
8
|
+
def initialize(one, two, three)
|
9
|
+
abort "Gosu not installed. Please use the --console option, or install gosu"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Image
|
14
|
+
def initialize(*args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ZIndex
|
21
|
+
LAYERS = [:world, :dead, :actor, :attacking, :overlay]
|
22
|
+
|
23
|
+
def self.for(type); LAYERS.index(type) end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Actor
|
27
|
+
|
28
|
+
def self.window=(window); @window = window end
|
29
|
+
def self.window; @window end
|
30
|
+
|
31
|
+
def self.sprites
|
32
|
+
@sprites ||= Dir[File.join(File.dirname(__FILE__),'sprites/*.png')].inject({}) do |sprites,f|
|
33
|
+
sprite = File.basename(f,'.*').split('-')
|
34
|
+
sprites[sprite.first] ||= {}
|
35
|
+
sprites[sprite.first][sprite.last] = Gosu::Image.new(window, f, false)
|
36
|
+
sprites
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def image
|
41
|
+
#self.class.sprites['robot']['idle'] #FIXME adjust this to indicate human versus zombie, and status of alive, dying or dead
|
42
|
+
self.class.sprites[actor_type][actor_state]
|
43
|
+
end
|
44
|
+
|
45
|
+
def actor_type
|
46
|
+
raise NotImplementedError
|
47
|
+
end
|
48
|
+
|
49
|
+
def draw
|
50
|
+
raise "actor is off the screen" unless (x > 0 and x < self.class.window.width and y > 0 and y < self.class.window.height)
|
51
|
+
image.draw_rot(x, y, z, actor_direction)
|
52
|
+
end
|
53
|
+
|
54
|
+
def calculate_x(successful_step_count)
|
55
|
+
preferred_step_size = 10
|
56
|
+
max_distance = ([Window.width, Window.height].min / 2) - preferred_step_size
|
57
|
+
(Window.width / 2) + Math::sin(@angle) * ((test_suite_size - successful_step_count) * preferred_step_size) * [1, max_distance * 1.0 / (test_suite_size * preferred_step_size)].min
|
58
|
+
end
|
59
|
+
|
60
|
+
def calculate_y(successful_step_count)
|
61
|
+
preferred_step_size = 10
|
62
|
+
max_distance = ([Window.width, Window.height].min / 2) - preferred_step_size
|
63
|
+
((Window.height / 2) + Math::cos(@angle) * ((test_suite_size - successful_step_count) * preferred_step_size) * [1, max_distance * 1.0 / (test_suite_size * preferred_step_size)].min)
|
64
|
+
end
|
65
|
+
|
66
|
+
def x
|
67
|
+
calculate_x(@successful_step_count)
|
68
|
+
end
|
69
|
+
|
70
|
+
def y
|
71
|
+
calculate_y(@successful_step_count)
|
72
|
+
end
|
73
|
+
|
74
|
+
def z
|
75
|
+
case actor_state
|
76
|
+
when "dead" then ZIndex.for(:dead)
|
77
|
+
when "attacking" then ZIndex.for(:attacking)
|
78
|
+
when "moving" then ZIndex.for(:actor)
|
79
|
+
when "dying" then ZIndex.for(:actor)
|
80
|
+
else raise "Unknown state"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def window
|
85
|
+
self.class.window
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class Window < Gosu::Window
|
90
|
+
@width = 400
|
91
|
+
@height = 300
|
92
|
+
|
93
|
+
def self.width=(width); @width = width end
|
94
|
+
def self.width; @width end
|
95
|
+
|
96
|
+
def self.height=(height); @height = height end
|
97
|
+
def self.height; @height end
|
98
|
+
|
99
|
+
attr_accessor :grid
|
100
|
+
attr_writer :human, :zombie_list
|
101
|
+
|
102
|
+
def initialize
|
103
|
+
super(self.class.width, self.class.height, false)
|
104
|
+
|
105
|
+
self.caption = 'Zombie-chaser'
|
106
|
+
self.grid = 1
|
107
|
+
|
108
|
+
@grass = Gosu::Image.new(self, File.join(File.dirname(__FILE__),'tiles/grass.png'), true)
|
109
|
+
@shrubbery = Gosu::Image.new(self, File.join(File.dirname(__FILE__),'tiles/shrubbery.png'), true)
|
110
|
+
end
|
111
|
+
|
112
|
+
def draw
|
113
|
+
draw_scenery
|
114
|
+
draw_human
|
115
|
+
draw_zombies
|
116
|
+
end
|
117
|
+
|
118
|
+
def button_down(id)
|
119
|
+
close if id == Gosu::Button::KbEscape
|
120
|
+
end
|
121
|
+
|
122
|
+
# private
|
123
|
+
|
124
|
+
def tile_positions
|
125
|
+
w, h = @grass.width, @grass.height
|
126
|
+
@tile_positions ||= {
|
127
|
+
:x => (0...width).to_a.inject([]) {|a,x| a << x if x % w == 0; a},
|
128
|
+
:y => (0...height).to_a.inject([]) {|a,y| a << y if y % h == 0; a}
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
def map
|
133
|
+
@map ||= tile_positions[:y].map do |y|
|
134
|
+
tile_positions[:x].map do |x|
|
135
|
+
{
|
136
|
+
:x => x,
|
137
|
+
:y => y,
|
138
|
+
:tile => (rand(32) % 32 == 0) ? @shrubbery : @grass
|
139
|
+
}
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def draw_scenery
|
145
|
+
map.each do |row|
|
146
|
+
row.each do |col|
|
147
|
+
col[:tile].draw(col[:x], col[:y], ZIndex.for(:world))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def draw_human
|
153
|
+
@human.draw if defined?(@human)
|
154
|
+
end
|
155
|
+
|
156
|
+
def draw_zombies
|
157
|
+
@zombie_list.draw_zombies if defined?(@zombie_list)
|
158
|
+
end
|
159
|
+
|
160
|
+
#No longer a major issue now zombies approach from all directions in GUI mode
|
161
|
+
def no_living_zombies_apart_from_me?(desired_step_count, actor)
|
162
|
+
true
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
@@ -1,98 +1,105 @@
|
|
1
|
-
require "human"
|
2
|
-
require "interface"
|
3
|
-
|
4
|
-
class World
|
5
|
-
@interface_type = :gui_interface
|
6
|
-
|
7
|
-
def self.interface_type=(interface_type); @interface_type = interface_type end
|
8
|
-
def self.interface_type; @interface_type end
|
9
|
-
|
10
|
-
attr_reader :interface
|
11
|
-
|
12
|
-
def self.new_using_results(human_results, zombies_results)
|
13
|
-
world = new(:no_interface)
|
14
|
-
human = MockHuman.new_using_results(human_results, world)
|
15
|
-
zombie_list = MockZombieList.new_using_results(zombies_results, world)
|
16
|
-
world.set_human(human)
|
17
|
-
world.set_zombie_list(zombie_list)
|
18
|
-
world
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.new_using_test_unit_handler(test_pattern)
|
22
|
-
world = new(self.interface_type)
|
23
|
-
human = Human.new_using_test_unit_handler(test_pattern, world)
|
24
|
-
zombie_list =
|
25
|
-
world.set_human(human)
|
26
|
-
world.set_zombie_list(zombie_list)
|
27
|
-
world
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@
|
33
|
-
@
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
@zombie_list = zombie_list
|
52
|
-
end
|
53
|
-
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
zombie
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
1
|
+
require "zombie-chaser/human"
|
2
|
+
require "zombie-chaser/interface"
|
3
|
+
|
4
|
+
class World
|
5
|
+
@interface_type = :gui_interface
|
6
|
+
|
7
|
+
def self.interface_type=(interface_type); @interface_type = interface_type end
|
8
|
+
def self.interface_type; @interface_type end
|
9
|
+
|
10
|
+
attr_reader :interface
|
11
|
+
|
12
|
+
def self.new_using_results(human_results, zombies_results)
|
13
|
+
world = new(:no_interface)
|
14
|
+
human = MockHuman.new_using_results(human_results, world)
|
15
|
+
zombie_list = MockZombieList.new_using_results(zombies_results, world)
|
16
|
+
world.set_human(human)
|
17
|
+
world.set_zombie_list(zombie_list)
|
18
|
+
world
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.new_using_test_unit_handler(test_pattern)
|
22
|
+
world = new(self.interface_type)
|
23
|
+
human = Human.new_using_test_unit_handler(test_pattern, world)
|
24
|
+
zombie_list = ZombieList.new_using_test_unit_handler(test_pattern, world)
|
25
|
+
world.set_human(human)
|
26
|
+
world.set_zombie_list(zombie_list)
|
27
|
+
world
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(interface_type)
|
31
|
+
@human = nil
|
32
|
+
@zombie_list = nil
|
33
|
+
@interface = case interface_type
|
34
|
+
when :console_interface then ConsoleInterface.new
|
35
|
+
when :no_interface then NoInterface.new
|
36
|
+
when :gui_interface then GuiInterface.new
|
37
|
+
end
|
38
|
+
@view_update_threads = nil
|
39
|
+
@collision_detection_lock = Monitor.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_human(human)
|
43
|
+
raise "Already set" unless @human.nil?
|
44
|
+
@human = human
|
45
|
+
interface.human = human
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_zombie_list(zombie_list)
|
49
|
+
raise "Already set" unless @zombie_list.nil?
|
50
|
+
@zombie_list = zombie_list
|
51
|
+
@interface.zombie_list = zombie_list
|
52
|
+
end
|
53
|
+
|
54
|
+
def while_world_running
|
55
|
+
@view_update_threads = Queue.new
|
56
|
+
yield
|
57
|
+
@view_update_threads.enq(:end_of_work)
|
58
|
+
thread = @view_update_threads.deq
|
59
|
+
until thread == :end_of_work
|
60
|
+
thread.join
|
61
|
+
thread = @view_update_threads.deq
|
62
|
+
end
|
63
|
+
@interface.finish_if_neccessary
|
64
|
+
end
|
65
|
+
|
66
|
+
def run_human
|
67
|
+
@human.run
|
68
|
+
! @human.dead?
|
69
|
+
end
|
70
|
+
|
71
|
+
def run_next_zombie
|
72
|
+
# Since zombies can come from multiple directions, rather than queueing against each other,
|
73
|
+
# separating out when they appear isn't required
|
74
|
+
zombie = @zombie_list.supply_next_zombie
|
75
|
+
@view_update_threads.enq(Thread.new{zombie.build_view_queue})
|
76
|
+
@view_update_threads.enq(Thread.new{zombie.update_view})
|
77
|
+
zombie.run_tests
|
78
|
+
end
|
79
|
+
|
80
|
+
def synchronize_for_collision_detection
|
81
|
+
@collision_detection_lock.synchronize {yield}
|
82
|
+
end
|
83
|
+
|
84
|
+
def something_happened
|
85
|
+
@interface.something_happened
|
86
|
+
end
|
87
|
+
|
88
|
+
def sleep(duration)
|
89
|
+
@interface.sleep(duration)
|
90
|
+
end
|
91
|
+
|
92
|
+
def notify_human_eaten
|
93
|
+
@human.get_eaten
|
94
|
+
end
|
95
|
+
|
96
|
+
#Assumption: this is called after human is in a valid state
|
97
|
+
def human_dead?
|
98
|
+
@human.dead?
|
99
|
+
end
|
100
|
+
|
101
|
+
def no_living_zombies_apart_from_me?(desired_step_count, actor)
|
102
|
+
@interface.no_living_zombies_apart_from_me?(desired_step_count, actor)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|