Tweaks for Android
[chess] / src / chessutils.ec
1 /****************************************************************************
2    CHESS Game
3
4    Copyright (c) 2001 Jerome Jacovella-St-Louis
5    All Rights Reserved.
6    
7    chessutils.ec - Utilities to validate moves
8 ****************************************************************************/
9 import "chess.ec"
10
11 enum PieceType : byte { Empty, Pawn, Knight, Bishop, Rook, Queen, King };
12 enum Player : byte { White, Black };
13
14 int materialValues[PieceType] = { 0, 10, 30, 35, 50, 90, 10000 };
15
16 class Piece : byte
17 {
18 public:
19    Player player : 1 : 3;
20    PieceType type : 3 : 0;
21 };
22
23 struct ChessMove
24 {
25    int x1, y1;
26    int x2, y2;
27    Player player;
28    PieceType type;
29    int rating;
30    PieceType promotion;
31    int count;
32 };
33
34 enum State { Normal, Check, CheckMate, StaleMate };
35
36 struct ChessState
37 {
38    bool gameRunning;
39    bool isLocalPlayer[Player];
40
41    Point enPassant;
42    bool kingMoved[Player], qRookMoved[Player], kRookMoved[Player];
43    Piece board[8][8];
44    Player turn;
45    int numMoves;
46    Point kings[Player];
47    int pieceCount[Player][PieceType];
48    int materialValue[Player];
49    bool castled[Player];
50
51    State state;
52 };
53
54 static bool FreeWay(Piece board[8][8], int x1, int y1, int x2, int y2)
55 {
56    int x,y;
57    int dx = Sgn(x2-x1), dy = Sgn(y2-y1);
58
59    for(x = x1+dx, y = y1+dy; x != x2 || y != y2; x+=dx, y+=dy)
60       if(board[y][x])
61          return false;
62    return true;
63 }
64
65 static bool BasicMove(Piece board[8][8], int x1, int y1, int x2, int y2)
66 {
67    bool valid = false;
68    Piece source = board[y1][x1];
69    Player player = source.player;
70    Piece dest = board[y2][x2];
71
72    if(!dest || player != dest.player)
73    {
74       PieceType piece = source.type;
75       int dx = x2 - x1;
76       int dy = y2 - y1;
77
78       switch(piece)
79       {
80          case Pawn:
81          {
82             int direction = (player == White) ? 1 : -1;
83             if(dy == direction && Abs(dx) == 1 && dest)
84                valid = true;
85             break;
86          }
87          case Knight:
88             if( (Abs(dx) == 1 && Abs(dy) == 2) ||
89                 (Abs(dy) == 1 && Abs(dx) == 2))
90                 valid = true;
91             break;
92          case Bishop:
93             if(Abs(dx) == Abs(dy) && FreeWay(board, x1,y1,x2,y2))
94                valid = true;
95             break;
96          case Rook:
97             if((!Abs(dx) || !Abs(dy)) && FreeWay(board, x1,y1,x2,y2))
98                valid = true;
99             break;
100          case Queen:
101             if((!Abs(dx) || !Abs(dy) || Abs(dx) == Abs(dy)) && 
102                FreeWay(board, x1,y1,x2,y2))
103                valid = true;
104             break;      
105          case King:
106             if(Abs(dx) <= 1 && Abs(dy) <= 1)
107                valid = true;
108             break;
109       }
110    }
111    return valid;
112 }
113
114 bool Check(ChessState state, Player player, int kx, int ky)
115 {
116    int x,y;
117    Piece old1 = 0xFF, old2;
118    bool checked = false;
119
120    if(kx == -1)
121    {
122       kx = state.kings[player].x;
123       ky = state.kings[player].y;
124    }
125    else
126    {
127       old1 = state.board[state.kings[player].y][state.kings[player].x];
128       state.board[state.kings[player].y][state.kings[player].x] = Piece { };
129
130       old2 = state.board[ky][kx];
131       state.board[ky][kx] = Piece { player, King };
132    }
133
134    for(y = 0; y<8; y++)
135    {
136       for(x = 0; x<8; x++)
137       {
138          Piece atBoard = state.board[y][x];
139          if(atBoard && atBoard.player != player)
140          {
141             if(BasicMove(state.board, x, y, kx, ky))
142             {
143                checked = true;
144                break;
145             }
146          }
147       }
148       if(checked)
149          break;
150    }
151
152    if(old1 != 0xFF)
153    {
154       state.board[ky][kx] = old2;
155       state.board[state.kings[player].y][state.kings[player].x] = old1;
156    }
157    return checked;
158 }
159
160 bool IsMoveValid(int x1, int y1, int x2, int y2, ChessState state, Piece endBoard[8][8], bool validate)
161 {
162    bool valid = false;
163    Piece source = state.board[y1][x1];
164    PieceType piece = source.type;
165    Player player = source.player;
166
167    int dx = x2 - x1;
168    int dy = y2 - y1;
169
170    if(!validate || (x2 >= 0 && y2 >= 0 && x2<8 && y2<8))
171    {
172       if(!(valid = (validate ? BasicMove(state.board, x1,y1,x2,y2) : true)))
173       {
174          // Handle special moves (pawn & castle)
175          switch(piece)
176          {
177             case Pawn:
178             {
179                int direction = (player == White) ? 1 : -1;
180                int start = (player == White) ? 1 : 6;
181                if(dy == direction)
182                {
183                   // Normal Move
184                   if(x1 == x2 && !state.board[y2][x2])
185                      valid = true;
186                   // En Passant
187                   else if(Abs(dx) == 1 && 
188                           x2 == state.enPassant.x && y2 == state.enPassant.y + direction)
189                      valid = true;
190                }
191                // First 2 Squares Move
192                else if(y2 - y1 == direction * 2 && y1 == start && x1 == x2 
193                   && !state.board[y1+direction][x1] && !state.board[y2][x1])
194                   valid = true;
195                break;
196             }
197             case King:
198                // Castle
199                if(!dy && Abs(dx) == 2)
200                {
201                   if(!state.kingMoved[player] && !Check(state, player, x1, y1))
202                   {
203                      // King Side
204                      if(dx == 2 && !state.kRookMoved[player] && 
205                         !state.board[y1][5] && !state.board[y1][6] &&
206                         !Check(state, player, 5, y1))
207                         valid = true;
208                      // Queen Side
209                      else if(dx == -2 && !state.qRookMoved[player] &&
210                         !state.board[y1][3] && !state.board[y1][2] && !state.board[y1][1] &&
211                         !Check(state, player, 3, y1))
212                         valid = true;
213                   }
214                }
215                break;
216          }
217       }
218    }
219
220    if(valid)
221    {
222       if(validate || endBoard)
223       {
224          ChessState tmpState = state;
225          tmpState = state;
226          tmpState.board[y1][x1] = Piece {};
227
228          // Handle special moves (pawn & castle)
229          switch(piece)
230          {
231             case Pawn:
232             {
233                int direction = (player == White) ? 1 : -1;
234                int start = (player == White) ? 1 : 6;
235                if(dy == direction)
236                {
237                   // Normal Move
238                   if(Abs(dx) == 1 && x2 == state.enPassant.x && y2 == state.enPassant.y + direction)
239                      tmpState.board[state.enPassant.y][state.enPassant.x] = Piece {};
240                }
241                break;
242             }
243             case King:
244                // *** Castle ***
245
246                // King Side
247                if(dx == 2)
248                {
249                   tmpState.board[y1][7] = Piece { };
250                   tmpState.board[y1][5] = Piece { player, Rook };
251                }
252                // Queen Side
253                else if(dx == -2)
254                {
255                   tmpState.board[y1][0] = Piece { };
256                   tmpState.board[y1][3] = Piece { player, Rook };
257                }
258
259                // Keep Track of King
260                tmpState.kings[player].x = x2;
261                tmpState.kings[player].y = y2;
262                break;
263          }
264
265          tmpState.board[y2][x2] = state.board[y1][x1];
266
267          valid = validate ? !Check(tmpState, player, -1, -1) : true;
268          if(valid && endBoard)
269             CopyBytes(endBoard, tmpState.board, 64);
270       }
271    }
272    return valid;
273 }
274
275 bool StateMakeMove(ChessState state, int x1, int y1, int x2, int y2, PieceType promotion, bool validate, int * delta)
276 {
277    bool valid = false;
278    PieceType type = state.board[y1][x1].type;
279    Player player = state.board[y1][x1].player;
280
281    PieceType captured = state.board[y2][x2].type;
282    if(captured)
283    {
284       if(x2 == 7 && y2 == ((player == White) ? 7 : 0) && !state.kRookMoved[(Player)!player])
285          state.kRookMoved[(Player)!player] = true;
286       else if(x2 == 0 && y2 == ((player == White) ? 7 : 0) && !state.qRookMoved[(Player)!player])
287          state.qRookMoved[(Player)!player] = true;
288
289       state.pieceCount[(Player)!player][captured]--;
290       state.materialValue[(Player)!player] -= materialValues[captured];
291       if(delta)
292          *delta = materialValues[captured];
293    }
294    // En Passant
295    else if(type == Pawn && Abs(x2-x1) == 1)
296    {
297       state.pieceCount[(Player)!player][Pawn]--;
298       state.materialValue[(Player)!player] -= materialValues[Pawn];
299       if(delta)
300          *delta = materialValues[Pawn];
301    }
302    
303    if(IsMoveValid(x1,y1,x2,y2, state, state.board, validate))
304    {
305       valid = true;
306
307       // En Passant
308       if(type == Pawn && Abs(y2-y1) == 2)
309       {
310          state.enPassant.x = x2;
311          state.enPassant.y = y2;
312       }
313       else
314       {
315          state.enPassant.x = -1;
316          state.enPassant.y = -1;
317       }
318
319       // Castle
320       if(type == King)
321       {
322          switch(x2-x1)
323          {
324             // King Side Castle
325             case 2:
326                state.kRookMoved[player] = true;
327                state.castled[player] = true;
328                break;
329             // Queen Side Castle
330             case -2:
331                state.qRookMoved[player] = true;
332                state.castled[player] = true;
333                break;
334          }
335       }
336
337       // Rook moved (can't castle with it)
338       if(type == Rook && y1 == ((player == White) ? 0 : 7))
339       {
340          if(!state.qRookMoved[player] && x1 == 0)
341             state.qRookMoved[player] = true;
342          else if(!state.kRookMoved[player] && x1 == 7)
343             state.kRookMoved[player] = true;
344       }
345
346       // Pawn Promotion
347       if(type == Pawn && y2 == ((player == White) ? 7 : 0))
348       {
349          state.board[y2][x2] = Piece { player, promotion };
350
351          state.pieceCount[player][Pawn]--;
352          state.pieceCount[player][promotion]++;
353          state.materialValue[player] += materialValues[promotion] - materialValues[Pawn];
354          if(delta)
355             *delta += materialValues[promotion] - materialValues[Pawn];
356       }
357
358       // Keep track of the kings
359       if(type == King)
360       {
361          state.kings[player].x = x2;
362          state.kings[player].y = y2;
363
364          state.kingMoved[player] = true;
365       }
366
367       state.turn ^= 1;
368       state.numMoves ++;
369    }
370    return valid;   
371 }