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