fen_to_ascii.c - chess-puzzles - chess puzzle book generator | |
git clone git://git.codemadness.org/chess-puzzles | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
fen_to_ascii.c (5802B) | |
--- | |
1 /* TODO: option to flip board? */ | |
2 | |
3 #include <ctype.h> | |
4 #include <stdio.h> | |
5 #include <string.h> | |
6 | |
7 static char board[8][8]; | |
8 static char highlight[8][8]; | |
9 | |
10 static int side_to_move = 'w'; /* default: white to move */ | |
11 static int white_can_castle[2] = { 0, 0 }; /* allow king side, allow que… | |
12 static int black_can_castle[2] = { 0, 0 }; /* allow king side, allow que… | |
13 | |
14 static const int showcoords = 1; /* config: show board coordinates? */ | |
15 | |
16 int | |
17 isvalidsquare(int x, int y) | |
18 { | |
19 return !(x < 0 || x >= 8 || y < 0 || y >= 8); | |
20 } | |
21 | |
22 /* place a piece, if possible */ | |
23 void | |
24 place(int piece, int x, int y) | |
25 { | |
26 if (!isvalidsquare(x, y)) | |
27 return; | |
28 | |
29 board[y][x] = piece; | |
30 } | |
31 | |
32 /* get piece, if possible */ | |
33 int | |
34 getpiece(int x, int y) | |
35 { | |
36 if (!isvalidsquare(x, y)) | |
37 return 0; | |
38 return board[y][x]; | |
39 } | |
40 | |
41 int | |
42 squaretoxy(const char *s, int *x, int *y) | |
43 { | |
44 if (*s >= 'a' && *s <= 'h' && | |
45 *(s + 1) >= '1' && *(s + 1) <= '8') { | |
46 *x = *s - 'a'; | |
47 *y = '8' - *(s + 1); | |
48 return 1; | |
49 } | |
50 return 0; | |
51 } | |
52 | |
53 void | |
54 highlightmove(int x1, int y1, int x2, int y2) | |
55 { | |
56 if (isvalidsquare(x1, y1)) | |
57 highlight[y1][x1] = 1; | |
58 | |
59 if (isvalidsquare(x2, y2)) | |
60 highlight[y2][x2] = 1; | |
61 } | |
62 | |
63 void | |
64 showpiece(int c) | |
65 { | |
66 const char *s = ""; | |
67 | |
68 /* simple or use unicode character */ | |
69 #if 1 | |
70 putchar(c); | |
71 return; | |
72 #endif | |
73 | |
74 switch (c) { | |
75 case 'K': s = "♔"; break; | |
76 case 'Q': s = "♕"; break; | |
77 case 'R': s = "♖"; break; | |
78 case 'B': s = "♗"; break; | |
79 case 'N': s = "♘"; break; | |
80 case 'P': s = "♙"; break; | |
81 case 'k': s = "♚"; break; | |
82 case 'q': s = "♛"; break; | |
83 case 'r': s = "♜"; break; | |
84 case 'b': s = "♝"; break; | |
85 case 'n': s = "♞"; break; | |
86 case 'p': s = "♟"; break; | |
87 } | |
88 | |
89 if (*s) | |
90 fputs(s, stdout); | |
91 } | |
92 | |
93 void | |
94 showboardfen(void) | |
95 { | |
96 int x, y, piece, skip = 0; | |
97 | |
98 for (y = 0; y < 8; y++) { | |
99 if (y > 0) | |
100 putchar('/'); | |
101 skip = 0; | |
102 for (x = 0; x < 8; x++) { | |
103 piece = getpiece(x, y); | |
104 if (piece) { | |
105 if (skip) | |
106 putchar(skip + '0'); | |
107 putchar(piece); | |
108 skip = 0; | |
109 } else { | |
110 skip++; | |
111 } | |
112 } | |
113 if (skip) | |
114 putchar(skip + '0'); | |
115 } | |
116 | |
117 /* ? TODO: detect en passant, invalid castling etc? */ | |
118 } | |
119 | |
120 /* show board */ | |
121 /* TODO: show fancier, unicode and background square color */ | |
122 /* TODO: use the output format similar to stockfish "d" command */ | |
123 void | |
124 showboard(void) | |
125 { | |
126 int x, y, piece; | |
127 | |
128 printf("Board FEN:\n"); | |
129 showboardfen(); | |
130 printf("\n\n"); | |
131 | |
132 for (y = 0; y < 8; y++) { | |
133 printf("+---+---+---+---+---+---+---+---+\n"); | |
134 for (x = 0; x < 8; x++) { | |
135 if (x == 0) | |
136 putchar('|'); | |
137 fputs(" ", stdout); | |
138 piece = getpiece(x, y); | |
139 if (piece) | |
140 showpiece(piece); | |
141 else | |
142 fputs(" ", stdout); | |
143 fputs(" ", stdout); | |
144 putchar('|'); | |
145 } | |
146 if (showcoords) { | |
147 putchar(' '); | |
148 putchar('8' - y); | |
149 } | |
150 putchar('\n'); | |
151 } | |
152 printf("+---+---+---+---+---+---+---+---+\n"); | |
153 if (showcoords) | |
154 printf(" a | b | c | d | e | f | g | h |\n"); | |
155 | |
156 fputs("\n", stdout); | |
157 | |
158 #if 0 | |
159 if (side_to_move == 'w') { | |
160 fputs("White to move\n", stdout); | |
161 } else if (side_to_move == 'b') | |
162 fputs("Black to move\n", stdout); | |
163 | |
164 if (white_can_castle[0]) | |
165 fputs("White can castle king side\n", stdout); | |
166 if (white_can_castle[1]) | |
167 fputs("White can castle queen side\n", stdout); | |
168 if (black_can_castle[0]) | |
169 fputs("Black can castle king side\n", stdout); | |
170 if (black_can_castle[1]) | |
171 fputs("Black can castle queen side\n", stdout); | |
172 #endif | |
173 } | |
174 | |
175 int | |
176 main(int argc, char *argv[]) | |
177 { | |
178 const char *fen, *moves, *s; | |
179 int x, y, x2, y2, field, piece; | |
180 char pieces[] = "PNBRQKpnbrqk", square[3]; | |
181 | |
182 if (argc != 3) { | |
183 fprintf(stderr, "usage: %s <FEN> <moves>\n", argv[0]); | |
184 return 1; | |
185 } | |
186 | |
187 fen = argv[1]; | |
188 moves = argv[2]; | |
189 | |
190 /* initial board state, FEN format */ | |
191 x = y = field = 0; | |
192 for (s = fen; *s; s++) { | |
193 /* next field, fields are: piece placement data, active … | |
194 Castling availability, En passant target square, | |
195 Halfmove clock, Fullmove number */ | |
196 if (*s == ' ') { | |
197 field++; | |
198 continue; | |
199 } | |
200 | |
201 switch (field) { | |
202 case 0: /* piece placement data */ | |
203 /* skip square */ | |
204 if (*s >= '1' && *s <= '9') { | |
205 x += (*s - '0'); | |
206 continue; | |
207 } | |
208 /* next rank */ | |
209 if (*s == '/') { | |
210 x = 0; | |
211 y++; | |
212 continue; | |
213 } | |
214 /* is piece? place it */ | |
215 if (strchr(pieces, *s)) | |
216 place(*s, x++, y); | |
217 break; | |
218 case 1: /* active color */ | |
219 if (*s == 'w' || *s == 'b') | |
220 side_to_move = *s; | |
221 break; | |
222 case 2: /* castling availability */ | |
223 if (*s == '-') { | |
224 white_can_castle[0] = 0; | |
225 white_can_castle[1] = 0; | |
226 black_can_castle[0] = 0; | |
227 black_can_castle[1] = 0; | |
228 } else if (*s == 'K') { | |
229 white_can_castle[0] = 1; | |
230 } else if (*s == 'Q') { | |
231 white_can_castle[1] = 1; | |
232 } else if (*s == 'k') { | |
233 black_can_castle[0] = 1; | |
234 } else if (*s == 'q') { | |
235 black_can_castle[1] = 1; | |
236 } | |
237 break; | |
238 case 3: /* TODO: en-passant square, rest of the fields */ | |
239 break; | |
240 } | |
241 /* TODO: parse which side to move, en-passant, etc */ | |
242 } | |
243 | |
244 /* process moves */ | |
245 square[2] = '\0'; | |
246 x = y = x2 = y2 = -1; | |
247 for (s = moves; *s; s++) { | |
248 if (*s == ' ') | |
249 continue; | |
250 if ((*s >= 'a' && *s <= 'h') && | |
251 (*(s + 1) >= '1' && *(s + 1) <= '8') && | |
252 (*(s + 2) >= 'a' && *(s + 2) <= 'h') && | |
253 (*(s + 3) >= '1' && *(s + 3) <= '8')) { | |
254 square[0] = *s; | |
255 square[1] = *(s + 1); | |
256 | |
257 s += 2; | |
258 squaretoxy(square, &x, &y); | |
259 piece = getpiece(x, y); | |
260 | |
261 place(0, x, y); /* clear square */ | |
262 | |
263 /* place piece at new location */ | |
264 square[0] = *s; | |
265 square[1] = *(s + 1); | |
266 squaretoxy(square, &x2, &y2); | |
267 place(piece, x2, y2); | |
268 s += 2; | |
269 | |
270 /* possible promotion? (queen, knight, bishop) */ | |
271 if (*s == 'q' || *s == 'n' || *s == 'b') { | |
272 if (side_to_move == 'w') | |
273 piece = toupper(*s); | |
274 else | |
275 piece = *s; | |
276 place(piece, x2, y2); | |
277 s++; | |
278 } | |
279 | |
280 /* switch which side it is to move */ | |
281 side_to_move = side_to_move == 'b' ? 'w' : 'b'; | |
282 } | |
283 } | |
284 /* highlight last move */ | |
285 highlightmove(x, y, x2, y2); | |
286 | |
287 showboard(); | |
288 | |
289 return 0; | |
290 } |