zombie-chaser 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|