From 17620454f5f58b2f1b30063aa0920205a1c63eb6 Mon Sep 17 00:00:00 2001 From: Paolo Capriotti Date: Sun, 14 Jun 2009 16:13:17 +0200 Subject: [PATCH] Initial implementation of MatchHandler. --- lib/games/chess/move.rb | 11 ++++++++- lib/ics/icsplayer.rb | 22 +++++++++++++++++ lib/ics/match_handler.rb | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/ics/protocol.rb | 26 ++++++++++++++++---- lib/ics/style12.rb | 23 +++++++++++------ lib/interaction/match.rb | 16 ++++++++++-- 6 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 lib/ics/icsplayer.rb create mode 100644 lib/ics/match_handler.rb diff --git a/lib/games/chess/move.rb b/lib/games/chess/move.rb index 0442709..53f0563 100644 --- a/lib/games/chess/move.rb +++ b/lib/games/chess/move.rb @@ -12,7 +12,7 @@ module Chess @dst = dst @promotion = opts[:promotion] end - + def delta dst - src end @@ -24,5 +24,14 @@ module Chess def to_s "#{src} -> #{dst}" end + + # SAN + def self.from_san(san) + # TODO + end + + def to_san + "" # TODO + end end end diff --git a/lib/ics/icsplayer.rb b/lib/ics/icsplayer.rb new file mode 100644 index 0000000..316db34 --- /dev/null +++ b/lib/ics/icsplayer.rb @@ -0,0 +1,22 @@ +require 'interaction/match' + +module ICS + +class ICSPlayer + include Player + + attr_reader :color + + # create a new ICS player playing with + # the given color and using the given + # output channel to send moves + def initialize(out, color) + @color = color + @out = out + end + + def on_move(data) + move = data[:move] + out[move.to_san] + end +end \ No newline at end of file diff --git a/lib/ics/match_handler.rb b/lib/ics/match_handler.rb new file mode 100644 index 0000000..6a63655 --- /dev/null +++ b/lib/ics/match_handler.rb @@ -0,0 +1,64 @@ +require 'interaction/match' + +module ICS + +# Handler for ICS games +# +class MatchHandler + include Observer + + def initialize(user, protocol) + @protocol = protocol + @matches = { } + @user = user + + protocol.add_observer(self) + end + + def on_creating_game(data) + match = Match.new(data[:game]) + @matches[data[:number]] = [match, data[:icsapi]] + end + + def on_style12(style12) + match, icsapi = @matches[style12[:game_number]] + return if match == nil + + unless match.started? + rel = style12[:relation] + state = style12[:state] + turns = [state.turn, state.opposite_turn(state.turn)] + user_color, opponent_color = + if rel == Style12::Relation::MY_MOVE + turns + else + turns.reverse + end + user.reset(user_color, match) + opponent = ICSPlayer.new + lambda {|msg| @protocol.connection.send_text(msg), + opponent_color + + match.register(user) + match.register(opponent) + + match.start(user) + match.start(opponent) + end + + if style12[:move_index] > 0 + last_move = icsapi.parse_verbose(style12[:last_move], style12[:state]) + move = match.game.read_move(style12[:last_move_san], style12[:state]) + if last_move != move + warn "[server inconsistency] " + + "SAN for last move is different from verbose notation" + end + + match.move(nil, move) + end + + + end +end + +end \ No newline at end of file diff --git a/lib/ics/protocol.rb b/lib/ics/protocol.rb index a983cfd..f920145 100644 --- a/lib/ics/protocol.rb +++ b/lib/ics/protocol.rb @@ -12,6 +12,8 @@ class Protocol 'standard' => :chess, 'blitz' => :chess, 'lightning' => :chess } + + attr_reader :connection def self.on(regex, type = :full, &blk) # ugly hack to work around the missing @@ -30,6 +32,8 @@ class Protocol end def link_to(connection) + raise "protocol already linked" if @connection + @connection = connection connection.on(:received_line) do |line, offset| process line end @@ -49,8 +53,12 @@ class Protocol execute_action @@partial_actions, line end + # This is the first of two messages issued by the server when a new game + # is starting. Many of the information contained here is repeated in the + # second message, but some, like time information, is only present here on %r{^Creating:\s+(\S+)\s+\((\S*)\)\s+(\S+)\s+\((\S*)\) \s+(\S+)\s+(\S+)\s+(\d+)\s+(\d+)}x do |match| + game = game_from_type(match[6]) @incoming_game = { :white => { :name => match[1], @@ -60,17 +68,22 @@ class Protocol :score => match[4].to_i }, :rated => match[5], :type => match[6], - :game => game_from_type(match[6]), + :game => game, + :icsapi => ICSApi.new(game), :time => match[7].to_i, :increment => match[8].to_i } - fire :creating_game => @incoming_game + end - + + # This is the second message of a game creation. + # The game number is contained here. on /^\{Game\s+(\d+)\s+\((\S+)\s+vs\.\s+(\S+)\) \s+(\S+.*)\}(.*)/x do |match| if match[4] =~ /^(Creating)|(Continuing)/ if not @incoming_game - # this should not happen + # if this happens, it means that the first message has + # been somehow lost + # continue anyway, gathering as much information as possible info = match[4].split(/\s+/) if info.size >= 3 @incoming_game = { @@ -84,9 +97,13 @@ class Protocol end end if @incoming_game + # now we know the game number, so we can save + # all the game information in the @games hash num = match[1].to_i @incoming_game[:number] = num @games[num] = @incoming_game + fire :creating_game => @incoming_game + @incoming_game = nil end else if not @incoming_game @@ -116,7 +133,6 @@ class Protocol end on(Style12::PATTERN) do |match| - puts "matched style12" style12 = Style12.from_match(match, @games) fire :style12 => style12 end diff --git a/lib/ics/style12.rb b/lib/ics/style12.rb index bd94481..b59ff7d 100644 --- a/lib/ics/style12.rb +++ b/lib/ics/style12.rb @@ -60,17 +60,21 @@ class Style12 TIME_USED = 28 LAST_MOVE = 29 FLIP = 30 + + class Relation + MOVE_LIST_START = -4 + ISOLATED_POSITION = -3 + OBSERVING_EXAMINED = -2 + NOT_MY_MOVE = -1 + OBSERVING_PLAYED = 0 + MY_MOVE = 1 + EXAMINING = 2 + end def self.from_match(match, games) game_number = match[GAME_NUMBER].to_i current_game = games[game_number] - game = if current_game - current_game[:game] - else - Game.dummy - end - icsapi = ICSApi.new(game) - + icsapi = current_game[:icsapi] state = icsapi.new_state(:turn => match[TURN] == 'W' ? :white : :black, :en_passant => match[EN_PASSANT].to_i, @@ -89,7 +93,10 @@ class Style12 :game_number => match[GAME_NUMBER].to_i, :move_index => match[MOVE_ORDINAL].to_i, :white_player => match[WHITE_PLAYER], - :black_player => match[BLACK_PLAYER]) + :black_player => match[BLACK_PLAYER], + :relation => match[RELATION].to_i, + :last_move => match[LAST_MOVE_VERBOSE] + ) end attr_reader :state diff --git a/lib/interaction/match.rb b/lib/interaction/match.rb index eef430d..1fe8bd7 100644 --- a/lib/interaction/match.rb +++ b/lib/interaction/match.rb @@ -7,6 +7,9 @@ end class Match include Observable + attr_reader :game + attr_reader :state + def initialize(game) @game = game @players = { } # player => ready @@ -40,8 +43,13 @@ class Match def move(player, move) return false unless @state - return false unless @players.has_key?(player) - return false unless player.color == @state.turn + # if player is nil, assume the current player is moving + if player == nil + player = current_player + else + return false unless @players.has_key?(player) + return false unless player.color == @state.turn + end return false unless @validate[move] @state.perform!(move) @@ -69,4 +77,8 @@ class Match p.update any_to_event(event) unless p == player end end + + def current_player + @players.keys.find {|p| p.color == @state.color } + end end \ No newline at end of file -- 2.11.4.GIT