public
Fork of scrooloose/chesticles
Description: A chess hax in ruby
Clone URL: git://github.com/halorgium/chesticles.git
chesticles / models / move.rb
100644 183 lines (141 sloc) 3.881 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
class Move
  class Error < StandardError; end
  class InvalidOperationError < Error; end
 
  attr_reader :piece, :square
 
  delegate :board, :player, :to => :piece
 
  def initialize(piece, square)
    @piece = piece
    @square = square
  end
 
  def checks_enemy?
    enemy_king = board.king_for(player.enemy)
    piece.temporarily_move_to(square) do |p|
      board.threatened?(enemy_king.square, player)
    end
  end
 
  def checks_self?
    our_king = board.king_for(player)
    piece.temporarily_move_to(square) do |p|
      board.threatened?(our_king.square, player.enemy)
    end
  end
 
  def to_occupied_square?
    !to_empty_square?
  end
 
  def to_enemy_occupied_square?
    p = board.piece_for(square)
    p && p.player != player
  end
 
  def to_empty_square?
    board.piece_for(square).nil?
  end
 
  def horizontal?
    dy_for_player == 0 && dx_for_player != 0
  end
 
  def vertical?
    dy_for_player != 0 && dx_for_player == 0
  end
 
  def diagonal?
    dy_for_player.abs == dx_for_player.abs
  end
 
  def straight?
    horizontal? || vertical? || diagonal?
  end
 
 
  def direction?(heading, dist = nil)
    return false if direction != heading
    return false if dist && distance != dist
    true
  end
 
  def direction
    dx = dx_for_player
    dy = dy_for_player
 
    return :left if(dx < 0 && horizontal?)
    return :right if(dx > 0 && horizontal?)
    return :forward if(dy > 0 && vertical?)
    return :back if(dx < 0 && vertical?)
    return :back_left if(dx < 0 && dy < 0 && diagonal?)
    return :back_right if(dx < 0 && dy < 0 && diagonal?)
    return :forward_left if(dx < 0 && dy > 0 && diagonal?)
    return :forward_right if(dx > 0 && dy > 0 && diagonal?)
  end
 
 
 
  def dy_for_player
    @dy_for_player ||= begin
      ydelta = start_square.y_delta_to(square)
      ydelta = -ydelta if player.white?
      ydelta
    end
  end
 
  def dx_for_player
    @dx_for_player ||= begin
      xdelta = start_square.x_delta_to(square)
      xdelta = -xdelta if player.black?
      xdelta
    end
  end
 
  def dx
    @dx ||= start_square.x_delta_to(square)
  end
 
  def dy
    @dy ||= start_square.y_delta_to(square)
  end
 
 
  def distance
    @distance ||= if vertical?
      dy_for_player.abs
    elsif horizontal?
      dx_for_player.abs
    elsif diagonal?
      dx_for_player.abs
    else
      raise(InvalidOperationError, "Cannot compute distance for a non-straight move")
    end
  end
 
  def start_square
    piece.square
  end
 
  def squares_between
    square.squares_between piece.square
  end
 
  def clear_path?
    raise(InvalidOperationError, "move must be straight") unless straight?
    squares_between.all? do |s|
      board.piece_for(s).nil?
    end
  end
 
  def min_x
    square.x < start_square.x ? square.x : start_square.x
  end
 
  def max_x
    square.x > start_square.x ? square.x : start_square.x
  end
 
  def min_y
    square.y < start_square.y ? square.y : start_square.y
  end
 
  def max_y
    square.y > start_square.y ? square.y : start_square.y
  end
 
  def execute
    piece.move(self)
  end
 
  def trying_to_capture_own_piece?
    p = board.piece_for(square)
    p && p.player == player
  end
 
  def moving_to_same_square?
    square == @piece.square
  end
 
  def castle?
    return false unless piece.is_a?(King) && !piece.moved? && dx.abs == 2 && horizontal?
    
    rook_square = if direction?(:left)
      player.white? ? Square.new(0,7) : Square.new(7,0)
    elsif direction?(:right)
      player.white? ? Square.new(7,7) : Square.new(0,0)
    end
 
    #return false if there are any pieces between the king and rook
    return false if piece.square.squares_between(rook_square).map{|s| board.piece_for(s)}.compact.any?
 
    rook = board.piece_for(rook_square)
    rook.is_a?(Rook) && !rook.moved?
  end
 
  def moving_to_end_of_board?
    square.y == 0 && player.white? || square.y == 7 && player.black?
  end
 
 
end