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