Add licence and installation instructions.
[kaya.git] / lib / games / chess / serializer.rb
blobbea3fe5e3b2052b44638d0e39a3426019107e160
1 # Copyright (c) 2009 Paolo Capriotti <p.capriotti@gmail.com>
2
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 'games/chess/san'
9 require 'point'
11 module Chess
13 class Serializer
14   def initialize(rep, validator_factory, 
15                  move_factory, piece_factory, 
16                  notation)
17     @rep = rep
18     @validator = validator_factory
19     @move = move_factory
20     @piece = piece_factory
21     @notation = notation
22   end
23   
24   def serialize(move, ref)
25     case @rep
26     when :simple
27       ysize = ref.board.size.y
28       result = move.src.to_coord(ysize) + move.dst.to_coord(ysize)
29       result += '=' + @piece.symbol(move.promotion) if move.promotion
30       result
31     when :compact
32       san move, ref, lambda{|t| @piece.symbol(t) }
33     when :decorated
34       san move, ref, lambda{|t| "{#{t.to_s}}" }
35     end
36   end
37   
38   def deserialize(s, ref)
39     notation = @notation.read(s)
40     read_san ref, notation if notation
41   end
42   
43   def read_san(ref, san)
44     candidate = nil
45     return candidate unless san[:dst] or san[:castling]
46     validate = @validator.new(ref)
47     
48     if san[:castling]
49       # find king starting position
50       src = Point.new(ref.board.size.x / 2, ref.row(0, ref.turn))
51       dst = src + (san[:castling] == :king ? Point.new(2, 0) : Point.new(-2, 0))
52       king = ref.board[src]
53       return candidate unless king.type == :king
54       candidate = @move.new(src, dst)
55       candidate if validate[candidate]
56     elsif san[:src] and san[:src].x and san[:src].y
57       mv = @move.new(san[:src], san[:dst], :promotion => san[:promotion])
58       mv if validate[mv]
59     else
60       ref.board.each_square do |p|
61         mv = @move.new(p, san[:dst], :promotion => san[:promotion])
62         piece = ref.board[p]
63         if p =~ san[:src] and piece and 
64            piece.type == san[:type] and
65            piece.color == ref.turn
66           if validate[mv]
67             if candidate
68               # ambiguous!
69               return nil
70             else
71               candidate = mv
72             end
73           end
74         end
75       end
76       candidate
77     end
78   end
79   
80   def san(move, ref, sym)
81     piece = ref.board[move.src]
82         
83     return "" unless piece
84     return "0-0" if move.type == :king_side_castling
85     return "0-0-0" if move.type == :queen_side_castling  
86     
87     capture_square = ref.capture_square(move)
88     captured = ref.board[capture_square]
89     
90     result = ""
91     ysize = ref.board.size.y
92     
93     if piece.type == :pawn
94       result = if captured
95         result = Point.new(move.src.x, nil).to_coord(ysize) + 'x'
96       else
97         ""
98       end
99       result += move.dst.to_coord(ysize)
100     else
101       result = sym[piece.type]
102       san = minimal_notation ref,
103         :src => move.src,
104         :dst => move.dst,
105         :type => piece.type
106       
107       result += san[:src].to_coord(ysize) if san[:src]
108       result += 'x' if captured
109       result += san[:dst].to_coord(ysize)
110     end
111     
112     if move.promotion
113       result += '=' + sym[move.promotion]
114     end
115     
116     result
117   end
118   
119   def minimal_notation(ref, san)
120     @notation.each_alternative(san) do |alternative|
121       return alternative if read_san(ref, alternative)
122     end
123     
124     san
125   end