pirate_game 0.0.1

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.
@@ -0,0 +1,208 @@
1
+ require 'shuttlecraft/mothership'
2
+
3
+ class PirateGame::GameMaster < Shuttlecraft::Mothership
4
+
5
+
6
+ MIN_PLAYERS = 1 # for now
7
+ MAX_PLAYERS = 4
8
+
9
+ STATES = [:pending, :startable, :playing, :ended]
10
+
11
+ attr_accessor :stage, :stage_ary
12
+
13
+ ##
14
+ # The state of the game. See STATES.
15
+
16
+ attr_reader :state
17
+
18
+ ##
19
+ # Number of players in the game. Call #update to refresh
20
+
21
+ attr_reader :num_players
22
+
23
+ ##
24
+ # Names of players in the game. Call #update to refresh
25
+
26
+ attr_reader :player_names
27
+
28
+ def initialize(opts={})
29
+ opts[:protocol] ||= PirateGame::Protocol.default
30
+
31
+ super(opts.merge({:verbose => true}))
32
+
33
+ set_state :pending
34
+
35
+ @last_update = Time.at 0
36
+ @num_players = 0
37
+ @player_names = []
38
+ @stage = nil
39
+ @stage_ary = []
40
+
41
+ @action_watcher = create_action_watcher
42
+ end
43
+
44
+ def registrations_text
45
+ "Num Players: #{@num_players}\n#{@player_names.join(', ')}\n"
46
+ end
47
+
48
+ def stage_info
49
+ return unless @stage
50
+
51
+ info = "Stage #{@stage.level}: \n"
52
+ if @stage.in_progress?
53
+ info << "Actions: #{@stage.actions_completed}\n"
54
+ info << "Time Left: #{@stage.time_left.to_i} seconds\n"
55
+ else
56
+ info << "Status: #{@stage.status}\n"
57
+
58
+ rundown = @stage.rundown
59
+
60
+ info << "Actions: #{rundown[:total_actions]}\n"
61
+
62
+ rundown[:player_breakdown].each do |player_uri, actions|
63
+ info << "#{player_uri}: #{actions}\n"
64
+ end
65
+
66
+ end
67
+
68
+ info
69
+ end
70
+
71
+ def game_info
72
+ return if @stage_ary.empty?
73
+
74
+ info = "Game Rundown:\n"
75
+ gr = game_rundown
76
+
77
+ info << "Total Actions: #{gr[:total_actions]}\n"
78
+
79
+ gr[:player_breakdown].each do |player_uri, actions|
80
+ info << "#{player_uri}: #{actions}\n"
81
+ end
82
+
83
+ info
84
+ end
85
+
86
+ def game_rundown
87
+ return {} if @stage_ary.empty?
88
+
89
+ rundown = {
90
+ :total_stages => @stage_ary.length,
91
+ :total_actions => @stage_ary.inject(0) {|sum,stage| sum += stage.actions_completed},
92
+ :player_breakdown => {}}
93
+
94
+ for stage in @stage_ary
95
+ stage.player_stats.each_pair do |key, value|
96
+ rundown[:player_breakdown][key] ||= 0
97
+ rundown[:player_breakdown][key] += value.size
98
+ end
99
+ end
100
+
101
+ rundown
102
+ end
103
+
104
+ def allow_registration?
105
+ return (@stage.nil? && @num_players < MAX_PLAYERS)
106
+ end
107
+
108
+ def startable?
109
+ update!
110
+ return (@stage.nil? || @stage.success?) &&
111
+ @num_players >= MIN_PLAYERS &&
112
+ @num_players <= MAX_PLAYERS
113
+ end
114
+
115
+ def increment_stage
116
+ @stage =
117
+ if @stage
118
+ @stage.increment
119
+ else
120
+ PirateGame::Stage.new 1, @num_players
121
+ end
122
+
123
+ @stage_ary << @stage
124
+ @stage
125
+ end
126
+
127
+ def on_registration
128
+ set_state :startable if startable?
129
+ end
130
+
131
+ def start
132
+ return unless startable?
133
+
134
+ increment_stage
135
+
136
+ return true
137
+ end
138
+
139
+ def send_stage_info_to_clients
140
+ if @stage.in_progress?
141
+ set_state :playing
142
+ send_start_to_clients
143
+
144
+ elsif @stage.success?
145
+ set_state :startable
146
+ send_return_to_pub_to_clients
147
+
148
+ elsif @stage.failure?
149
+ set_state :ended
150
+ send_end_game_to_clients
151
+ end
152
+ end
153
+
154
+ def send_start_to_clients
155
+ each_client do |client|
156
+ bridge = @stage.bridge_for_player
157
+ client.start_stage(bridge, @stage.all_items)
158
+ end
159
+ end
160
+
161
+ def send_return_to_pub_to_clients
162
+ each_client do |client|
163
+ client.return_to_pub
164
+ end
165
+ end
166
+
167
+ def send_end_game_to_clients
168
+ each_client do |client|
169
+ client.end_game game_rundown
170
+ end
171
+ end
172
+
173
+ def create_action_watcher
174
+ Thread.new do
175
+ loop do
176
+ handle_action @ts.take([:action, nil, nil, nil])
177
+ end
178
+ end
179
+ end
180
+
181
+ def handle_action action_array
182
+ if @stage && @stage.in_progress?
183
+ @stage.complete action_array[1], action_array[3]
184
+ end
185
+ end
186
+
187
+ ##
188
+ # Retrieves the latest data from the TupleSpace.
189
+
190
+ def update
191
+ ret = super
192
+ return if ret.nil?
193
+
194
+ @num_players = @registered_services_ary.length
195
+
196
+ @player_names = @registered_services_ary.map { |name,| name }
197
+
198
+ ret
199
+ end
200
+
201
+ private
202
+
203
+ def set_state state
204
+ @state = state if STATES.include? state
205
+ end
206
+
207
+ end
208
+
@@ -0,0 +1,23 @@
1
+ class PirateGame::Image < PirateGame::WavingItem
2
+
3
+ def initialize shoes, image, top, left
4
+ super 0, 10, 4
5
+
6
+ @shoes = shoes
7
+ @image = image
8
+ @top = top
9
+ @left = left
10
+ end
11
+
12
+ def animate frame
13
+ top_offset, left_offset = waving_offset frame
14
+
15
+ @ship.move @top + top_offset, @left + left_offset
16
+ end
17
+
18
+ def draw
19
+ @ship = @shoes.image @image, top: @top, left: @left
20
+ end
21
+
22
+ end
23
+
@@ -0,0 +1,39 @@
1
+ require 'thread'
2
+
3
+ class PirateGame::LogBook
4
+
5
+ include Enumerable
6
+
7
+ def initialize size = 10
8
+ @mutex = Mutex.new
9
+ @log_book = []
10
+ @size = size
11
+ end
12
+
13
+ def add entry, author = 'unknown'
14
+ @mutex.synchronize do
15
+ @log_book << [entry, author]
16
+
17
+ @log_book.shift if @log_book.size > @size
18
+ end
19
+ end
20
+
21
+ def each
22
+ return enum_for __method__ unless block_given?
23
+
24
+ @log_book.each do |(entry, author)|
25
+ yield [entry, author]
26
+ end
27
+ end
28
+
29
+ def empty?
30
+ @log_book.empty?
31
+ end
32
+
33
+ def length
34
+ @log_book.length
35
+ end
36
+ alias_method :size, :length
37
+
38
+ end
39
+
@@ -0,0 +1,103 @@
1
+ module PirateGame
2
+ class MasterApp
3
+
4
+ def self.run
5
+ new.run
6
+ end
7
+
8
+ def initialize
9
+ @game_master = nil
10
+ end
11
+
12
+ def run
13
+ Shoes.app width: 360, height: 360, resizeable: false, title: 'Game Master' do
14
+
15
+ def launch_screen
16
+ clear do
17
+ background Boot::COLORS[:dark]
18
+ stack margin: 20 do
19
+ title "Start Game", stroke: Boot::COLORS[:light]
20
+ edit_line text: 'Name' do |s|
21
+ @name = s.text
22
+ end
23
+ button('launch') {
24
+ @game_master = GameMaster.new(name: @name)
25
+ display_screen
26
+ }
27
+ end
28
+ end
29
+ end
30
+
31
+ def display_screen
32
+ @game_master.update
33
+
34
+ clear do
35
+ background Boot::COLORS[:light]
36
+
37
+ stack :margin => 20 do
38
+ title "Game #{@game_master.name}", stroke: Boot::COLORS[:dark]
39
+
40
+ @button_stack = stack
41
+ @registrations = para stroke: Boot::COLORS[:dark]
42
+ @stage_info = para stroke: Boot::COLORS[:dark]
43
+ @game_info = para stroke: Boot::COLORS[:dark]
44
+ end
45
+
46
+ animate(5) {
47
+ detect_state_change {
48
+ update_button_stack
49
+ }
50
+
51
+ detect_stage_status_change {
52
+ @game_master.send_stage_info_to_clients
53
+ }
54
+
55
+ @registrations.replace @game_master.registrations_text
56
+ @stage_info.replace @game_master.stage_info
57
+ @game_info.replace @game_master.game_info
58
+ }
59
+ end
60
+ end
61
+
62
+ ##
63
+ # Responsible for updating the display of the START button
64
+
65
+ def update_button_stack
66
+ @button_stack.clear do
67
+
68
+ case @state
69
+ when :startable
70
+ button('START') {
71
+ @game_master.start
72
+ }
73
+ end
74
+ end
75
+ end
76
+
77
+ ##
78
+ # If the game_master.state has changed, yields to the block.
79
+
80
+ def detect_state_change
81
+ return if @state == @game_master.state
82
+
83
+ @state = @game_master.state
84
+
85
+ yield
86
+ end
87
+
88
+ ##
89
+ # If the stage status has changed, yields to the block
90
+ def detect_stage_status_change
91
+ return unless @game_master.stage
92
+ return if @stage_status == @game_master.stage.status
93
+
94
+ @stage_status = @game_master.stage.status
95
+
96
+ yield
97
+ end
98
+
99
+ launch_screen
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,10 @@
1
+ require 'shuttlecraft/protocol'
2
+
3
+ class PirateGame::Protocol < Shuttlecraft::Protocol
4
+
5
+ def self.default
6
+ @@default ||= self.new :PirateGame, "Pirate Game"
7
+ @@default
8
+ end
9
+
10
+ end
@@ -0,0 +1,30 @@
1
+ class Shoes
2
+ module Swt
3
+ class Progress
4
+ def fraction=(value)
5
+ @real.selection = (value*100).to_i unless @real.disposed?
6
+ end
7
+ end
8
+
9
+ class TextBlockPainter
10
+
11
+ # added the return statement on line 8
12
+ def paintControl(paint_event)
13
+ graphics_context = paint_event.gc
14
+ gcs_reset graphics_context
15
+ @text_layout.setText @dsl.text
16
+ set_styles
17
+ if @dsl.width
18
+ @text_layout.setWidth @dsl.width
19
+ return if @dsl.absolute_left.nil? || @dsl.margin_left.nil? || @dsl.absolute_top.nil? || @dsl.margin_top.nil?
20
+ @text_layout.draw graphics_context, @dsl.absolute_left + @dsl.margin_left, @dsl.absolute_top + @dsl.margin_top
21
+ if @dsl.cursor
22
+ move_text_cursor
23
+ else
24
+ (@dsl.textcursor.remove; @dsl.textcursor = nil) if @dsl.textcursor
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,96 @@
1
+ require 'pirate_command'
2
+
3
+ class PirateGame::Stage
4
+
5
+ attr_accessor :actions_completed
6
+ attr_accessor :player_stats
7
+ attr_accessor :all_items
8
+ attr_accessor :begin_time
9
+ attr_accessor :level
10
+ attr_accessor :players
11
+
12
+ ITEMS_PER_BRIDGE = 6
13
+ DURATION = PirateGame::Boot.config["stage_duration"]
14
+
15
+ IN_PROGRESS = 'In Progress'
16
+ SUCCESS = 'Success'
17
+ FAILURE = 'Failure'
18
+
19
+ def initialize(level, players)
20
+ @level = level
21
+ @players = players
22
+ @actions_completed = 0
23
+ @player_stats = {}
24
+ generate_all_items
25
+
26
+ @begin_time = Time.now
27
+ end
28
+
29
+ def increment
30
+ PirateGame::Stage.new self.level + 1, self.players
31
+ end
32
+
33
+ def time_left
34
+ [0, (begin_time + DURATION) - Time.now].max
35
+ end
36
+
37
+ def status
38
+ if time_left > 0
39
+ IN_PROGRESS
40
+ else
41
+ passed? ? SUCCESS : FAILURE
42
+ end
43
+ end
44
+
45
+ def in_progress?
46
+ status == IN_PROGRESS
47
+ end
48
+
49
+ def success?
50
+ status == SUCCESS
51
+ end
52
+
53
+ def failure?
54
+ status == FAILURE
55
+ end
56
+
57
+ def generate_all_items
58
+ @all_items = []
59
+
60
+ while @all_items.length < @players*ITEMS_PER_BRIDGE
61
+ thing = PirateCommand.thing
62
+ @all_items << thing unless @all_items.include?(thing)
63
+ end
64
+ @boards = @all_items.each_slice(ITEMS_PER_BRIDGE).to_a
65
+ end
66
+
67
+ def bridge_for_player
68
+ @boards.shift
69
+ end
70
+
71
+ def complete action, from
72
+ @actions_completed += 1
73
+ @player_stats[from] ||= []
74
+ @player_stats[from] << action
75
+ end
76
+
77
+ def required_actions
78
+ @level * 2 + 1
79
+ end
80
+
81
+ def passed?
82
+ @actions_completed >= required_actions
83
+ end
84
+
85
+ def rundown
86
+ return if status == IN_PROGRESS
87
+
88
+ rundown = {stage: @level, total_actions: @actions_completed}
89
+ rundown[:player_breakdown] = {}
90
+
91
+ @player_stats.each {|p,v| rundown[:player_breakdown][p] = v.size}
92
+
93
+ rundown
94
+ end
95
+
96
+ end