1 # Copyright (c) 2010 Paolo Capriotti <p.capriotti@gmail.com>
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 require_bundle 'ics', 'style12'
14 # Helper mixin to create and start ICS matches.
18 # Create an appropriate MatchHelper instance for the given style12
20 def self.from_style12(style12)
21 @@helpers ||= {}.tap do |h|
22 h[Style12::Relation::EXAMINING] = ExaminingMatchHelper.instance
23 h[Style12::Relation::NOT_MY_MOVE] = DefaultMatchHelper.instance
24 h[Style12::Relation::MY_MOVE] = h[Style12::Relation::NOT_MY_MOVE]
25 h[Style12::Relation::OBSERVING_PLAYED] = ObservingMatchHelper.instance
28 @@helpers[style12.relation]
32 # Get the helper instance for this type.
34 # Currently supported types: :default, :examining, :observing.
37 ICS.const_get(type.to_s.capitalize + "MatchHelper").instance
41 # Create the opponent player. By default, create an ICSPlayer that
42 # will respond to future style12 events.
44 def create_opponent(protocol, color, match_info)
45 send = lambda {|msg| protocol.connection.send_text(msg) }
46 opponent = ICSPlayer.new(send, color, match_info[:match], match_info)
47 match_info[:icsplayer] = opponent
52 # Create a player for this match.
54 def create_player(user, color, match_info)
55 raise "not implemented"
59 # Create a new match instance.
61 def create_match(handler, match_info)
62 raise "not implemented"
68 def close_match(handler, match_info)
69 handler.matches.delete(match_info[:game_number])
73 # Get user from the view. The default implementation
74 # simply returns the controller of the main ics view.
76 def get_user(view, match_info)
81 # Perform post-creation initialization for players.
83 def setup_players(user, players)
87 # Return or create a match. Called on an existing match when a new style12
89 # If no match for that style12 event is found, the match argument
92 def get_match(protocol, match_info, style12)
97 # Return an array of colors to be assigned to the players.
99 def colors(state, rel)
100 turns = [state.turn, state.opposite_turn(state.turn)]
101 if rel == Style12::Relation::NOT_MY_MOVE
109 # Start an existing match. Called when the first style12
110 # for the given match is received.
112 def start(protocol, view, match_info, style12)
113 rel = style12.relation
114 state = style12.state
115 turns = [state.turn, state.opposite_turn(state.turn)]
117 user_color, opponent_color = colors(state, rel)
120 opponent = create_opponent(protocol, opponent_color, match_info)
121 user = get_user(view, match_info)
122 player = create_player(user, user_color, match_info)
123 setup_players(user, [player, opponent])
126 match = match_info[:match]
127 match.register(player)
128 match.register(opponent)
130 match.start(opponent)
131 raise "couldn't start match" unless match.started?
136 # set initial state and time
137 unless match_info[:icsapi].same_state(match.state, style12.state)
138 match.history.state = style12.state
140 match.update_time(style12.time)
148 # Helper class to setup normal ICS games.
150 # The first player is the default user, and the opponent is a default
151 # ICSPlayer instance created by create_opponent.
153 class DefaultMatchHelper
157 def create_player(user, color, match_info)
158 # do not create a new player, just return user
161 user.name = match_info[color][:name]
165 def create_match(handler, match_info)
166 match = Match.new(match_info[:game],
169 :time_running => true)
170 match.on(:close) { close_match(handler, match_info) }
171 match_info.merge(:match => match)
174 def close_match(handler, match_info)
175 super(handler, match_info)
176 handler.protocol.connection.send_text("resign")
181 # Helper class to setup matches in examination mode.
183 # The first player is dummy, and the user is set to control both
184 # the dummy player and the opponent. This allows the user to perform
185 # moves for both players.
187 class ExaminingMatchHelper
191 def create_player(user, color, match_info)
194 # create a controlled player
195 player = DummyPlayer.new(color)
196 player.name = match_info[color][:name]
198 match_info[:match].add_observer(user)
202 def setup_players(user, players)
203 players.each do |player|
204 user.add_controlled_player(player)
208 def get_match(handler, match_info, style12)
210 # Examined games on ics have no header, so we have to be prepared to
211 # create a new match on the fly at this point.
212 # Create an editable Game.dummy match for the moment.
213 match_info = create_match(handler, style12.match_info)
215 # We want to change the game type at some point, so request the game
216 # movelist to the server.
217 handler.protocol.connection.send_text('moves')
223 def create_match(handler, match_info)
224 match = Match.new(Game.dummy,
228 match.on(:close) { close_match(handler, match_info) }
229 match_info.merge(:match => match)
232 def close_match(handler, match_info)
233 super(handler, match_info)
234 handler.protocol.connection.send_text("unexamine")
239 # Helper class to setup matches in observation mode.
241 # Match is set to not editable and navigable, first player is dummy,
242 # no controlled player for the user.
244 class ObservingMatchHelper
248 def create_player(user, color, match_info)
249 player = DummyPlayer.new(color)
250 player.name = match_info[color][:name]
252 match_info[:match].add_observer(user)
256 def create_match(handler, match_info)
257 match = Match.new(Game.dummy,
261 match.on(:close) { close_match(handler, match_info) }
262 match_info.merge(:match => match)
265 def close_match(handler, match_info)
266 super(handler, match_info)
267 handler.protocol.connection.send_text("unobserve #{match_info[:number]}")
270 def get_user(view, match_info)
271 v = view.create(:name => match_name(match_info),
276 def match_name(match_info)
277 "#{match_info[:white][:name]} - #{match_info[:black][:name]}"