Fix bug in silver and gold validation.
[kaya.git] / lib / games / shogi / validator.rb
bloba385eaf0344908ee5cbe8aa8d6f6e6ede61d0c91
1 require 'games/validator_base'
3 module Shogi
4   class Validator < ValidatorBase
5     def initialize(state)
6       super
7     end
8     
9     def [](move)
10       move.validate do |m|
11         validate(m)
12       end
13     end
14     
15     def validate(move, target = nil)
16       return false unless move.dropped || @state.board.valid?(move.src)
17       return false unless @state.board.valid? move.dst
18       
19       piece = move.dropped
20       if piece
21         return false unless piece.color == @state.turn
22         return false unless @state.pool(piece.color).has_piece?(piece)
23         return false if @state.board[move.dst]
24         if piece.type == :pawn
25           # pawns cannot be dropped on the last rank
26           return false if 
27             move.dst.y == @state.row(@state.board.size.y - 1, piece.color)
28           # do not allow two pawns on the same column
29           return false if (0..@state.board.size.y).
30                           map{|y| Point.new(move.dst.x, y) }.
31                           any?{|p| @state.board[p]. == piece }
32         elsif piece.type == :horse
33           # horses cannot be dropped on the last or last-but-one rank
34           return false if 
35             move.dst.y == @state.row(@state.board.size.y - 1, piece.color) ||
36             move.dst.y == @state.row(@state.board.size.y - 2, piece.color)
37         end
38       else
39         piece = @state.board[move.src]
40         return false unless piece and piece.color == @state.turn
41         return false unless check_pseudolegality(piece, target, move)
42       end
43       
44       
45       @state.try(move) do |tmp|
46         validator = self.class.new(tmp)
47         legal = validator.check_legality(piece, target, move)
48         return false unless legal
49       end
50       
51       true
52     end
53     
54     def validator_method(type)
55       m = super(type)
56       if @state.promoted_type?(type)
57         m = super(:gold) unless respond_to?(m)
58         m
59       else
60         m
61       end
62     end
63     
64     def check_pseudolegality(piece, target, move)
65       if move.promote?
66         return false if piece.type == :king or piece.type == :gold
67         return false unless 
68           @state.in_promotion_zone?(move.src, piece.color) ||
69           @state.in_promotion_zone?(move.dst, piece.color)
70           
71         return false if @state.promoted?(piece)
72       else
73         # check for cases when it is mandatory to promote
74         case piece.type
75         when :pawn
76           return false if move.dst.y == @state.row(@state.board.size.y - 1, piece.color)
77         when :horse
78           return false if 
79             move.dst.y == @state.row(@state.board.size.y - 1, piece.color) ||
80             move.dst.y == @state.row(@state.board.size.y - 2, piece.color)
81         end
82       end
83       super(piece, target, move)
84     end
85     
86     def validate_pawn(piece, target, move)
87       move.delta == @state.direction(piece.color)
88     end
89     
90     def validate_lance(piece, target, move)
91       move.delta.x == 0 and
92       move.delta.y.unit == @state.direction(piece.color).y and
93       @state.board.clear_path? move.range
94     end
95     
96     def validate_horse(piece, target, move)
97       move.delta.x.abs == 1 and
98       move.delta.y == 2 * @state.direction(piece.color).y
99     end
100     
101     def validate_silver(piece, target, move)
102       dir = @state.direction(piece.color).y
103       (move.delta.y == dir and move.delta.x.abs <= 1) or
104       (move.delta.x.abs == 1 and move.delta.y == -dir)
105     end
106     
107     def validate_gold(piece, target, move)
108       dir = @state.direction(piece.color).y
109       (move.delta.y == dir and move.delta.x.abs <= 1) or
110       move.delta.x.abs + move.delta.y.abs == 1
111     end
112     
113     def validate_king(piece, target, move)
114       move.delta.x.abs <= 1 and
115       move.delta.y.abs <= 1
116     end
117     
118     def validate_bishop(piece, target, move)
119       range = move.range
120       range.diagonal? and
121       @state.board.clear_path? range
122     end
123     
124     def validate_rook(piece, target, move)
125       range = move.range
126       range.parallel? and
127       @state.board.clear_path? range
128     end
129     
130     def validate_promoted_rook(piece, target, move)
131       validate_king(piece, target, move) ||
132       validate_rook(piece, target, move)
133     end
134     
135     def validate_promoted_bishop(piece, target, move)
136       validate_king(piece, target, move) ||
137       validate_bishop(piece, target, move)
138     end
139     
140     def each_move(src, dst, target)
141       piece = @state.board[src]
142       if piece
143         moves = [@state.move_factory.new(src, dst)]
144         if @state.in_promotion_zone?(dst, piece.color)
145           moves << @state.move_factory.new(src, dst, :promotion => true)
146         end
147         moves.each do |m|
148           yield m if check_pseudolegality(piece, target, m)
149         end
150       end
151     end
152   end