Krolyxon
Commit 3120783

Branches

Tags

Refs →

orion-rf.piolibdepsesp32-s3-n16r8U8g2examplesgamesLittleRookChessLittleRookChess.ino

2347 lines · 63.5 KB

1/*
2
3 LittleRookChess.ino
4
5 A Simple Chess Engine (ported from U8glib)
6
7 Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)
8
9 Copyright (c) 2016, olikraus@gmail.com
10 All rights reserved.
11
12 Redistribution and use in source and binary forms, with or without modification,
13 are permitted provided that the following conditions are met:
14
15 * Redistributions of source code must retain the above copyright notice, this list
16 of conditions and the following disclaimer.
17
18 * Redistributions in binary form must reproduce the above copyright notice, this
19 list of conditions and the following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
27 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
34 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
36 Current Rule Limitation
37 - no minor promotion, only "Queening" of the pawn
38 - threefold repetition is not detected (same board situation appears three times)
39 Note: Could be implemented, but requires tracking of the complete game
40 - Fifty-move rule is not checked (no pawn move, no capture within last 50 moves)
41
42 Words
43 Ply a half move
44
45 General Links
46 http://chessprogramming.wikispaces.com/
47
48 Arduino specific
49 http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1260055596
50
51 Prefixes
52 chess_ Generic Chess Application Interface
53 ce_ Chess engine, used internally, these function should not be called directly
54 cu_ Chess utility function
55 stack_ Internal function for stack handling
56
57 Issues
58 10.01.2011
59 - castling to the right does not move the rook
60 --> done
61 - castling to the left: King can only move two squares
62 --> done
63
64 11.01.2011
65 Next Steps:
66 - replace stack_NextCurrentPos with cu_NextPos, cleanup code according to the loop variable
67 --> done
68 - Castling: Need to check for fields under attack
69 --> done
70
71 - Check for WIN / LOOSE situation, perhaps call ce_Eval() once on the top-level board setup
72 just after the real move
73 - cleanup cu_Move
74 --> almost done
75 - add some heuristics to the eval procedure
76 - add right side menu
77 --> done
78 - clean up chess_ManualMove
79 --> done
80 - finish menu (consider is_game_end, undo move)
81 - end condition: if KING is under attack and if KING can not move to a field which is under attack...
82 then the game is lost. What will be returned by the Eval procedure? is it -INF?
83 --> finished
84
85 - reduce the use of variable color, all should be reduced to board_orientation and ply&1
86
87 - chess_GetNextMarked shoud make use of cu_NextPos
88 --> done
89 - chess_ManualMove: again cleanup, solve draw issue (KING is not in check and no legal moves are available)
90 --> done
91 22.01.2011
92 - simplify eval_t ce_Eval(void)
93 - position eval does not work, still moves side pawn :-(
94 maybe because all pieces are considered
95 --> done
96
97 17. June 2016
98 U8g2/Arduboy Port
99*/
100
101#include <Arduino.h>
102#include <U8g2lib.h>
103
104#ifdef U8X8_HAVE_HW_SPI
105#include <SPI.h>
106#endif
107#ifdef U8X8_HAVE_HW_I2C
108#include <Wire.h>
109#endif
110
111
112/*
113 U8glib Example Overview:
114 Frame Buffer Examples: clearBuffer/sendBuffer. Fast, but may not work with all Arduino boards because of RAM consumption
115 Page Buffer Examples: firstPage/nextPage. Less RAM usage, should work with all Arduino boards.
116 U8x8 Text Only Example: No RAM usage, direct communication with display controller. No graphics, 8x8 Text only.
117
118 This is a page buffer example.
119*/
120
121void chess_Init(u8g2_t *u8g, uint8_t body_color);
122
123#define ARDUBOY
124//#define PI_SHIELD
125
126#ifdef ARDUBOY
127/*=== ARDUBOY Production, Kickstarter Edition ===*/
128U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 12, /* dc=*/ 4, /* reset=*/ 6); // Arduboy (Production, Kickstarter Edition)
129void setup(void) {
130 //u8g2.begin(/*Select=*/ A0, /*Right/Next=*/ 5, /*Left/Prev=*/ 9, /*Up=*/ 8, /*Down=*/ 10, /*Home/Cancel=*/ A1); // Arduboy DevKit
131 u8g2.begin(/*Select=*/ 7, /*Right/Next=*/ A1, /*Left/Prev=*/ A2, /*Up=*/ A0, /*Down=*/ A3, /*Home/Cancel=*/ 8); // Arduboy 10 (Production)
132 chess_Init(u8g2.getU8g2(), 1); /* assuming Arduboy OLED here, so make the body_color be 1 for the white OLED pixel */
133}
134#endif
135
136#ifdef PI_SHIELD
137/*=== Pax Instruments Shield ===*/
138U8G2_ST7567_PI_132X64_1_4W_HW_SPI u8g2(U8G2_R2, /* cs=*/ 7, /* dc=*/ 9, /* reset=*/ 8); // Pax Instruments Shield, LCD_BL=6
139void setup(void) {
140 u8g2.begin(/*Select=*/ 4, /*Right/Next=*/ 5, /*Left/Prev=*/ A1, /*Up=*/ U8X8_PIN_NONE, /*Down=*/ U8X8_PIN_NONE, /*Home/Cancel=*/ A2); // Pax Instrument Shield
141
142 /* U8g2 Project: Pax Instruments Shield: Enable Backlight */
143 pinMode(6, OUTPUT);
144 digitalWrite(6, 0);
145
146 chess_Init(u8g2.getU8g2(), 0); /* assuming Arduboy OLED here, so make the body_color be 1 for the white OLED pixel */
147}
148#endif
149
150/* Ignore PROGMEM for now */
151#define CHESS_PROGMEM
152#define chess_pgm_read(p) (*(p))
153#define CHESS_PSTR(s) (s)
154#define u8g2_DrawStrP u8g2_DrawStr
155
156/* backward compatibility */
157#define u8g2_SetDefaultBackgroundColor(u8g2) \
158 u8g2_SetDrawColor(u8g2, 0)
159#define u8g2_SetDefaultForegroundColor(u8g2) \
160 u8g2_SetDrawColor(u8g2, 1)
161
162
163/* menu button definitions: mapped to the u8g2 menu events */
164
165#define CHESS_KEY_NONE 0
166#define CHESS_KEY_NEXT U8X8_MSG_GPIO_MENU_NEXT
167#define CHESS_KEY_PREV U8X8_MSG_GPIO_MENU_PREV
168#define CHESS_KEY_SELECT U8X8_MSG_GPIO_MENU_SELECT
169#define CHESS_KEY_BACK U8X8_MSG_GPIO_MENU_HOME
170
171
172/*
173SAN identifies each piece by a single upper case letter. The standard English
174values: pawn = "P", knight = "N", bishop = "B", rook = "R", queen = "Q", and
175king = "K".
176*/
177
178/* numbers for the various pieces */
179#define PIECE_NONE 0
180#define PIECE_PAWN 1
181#define PIECE_KNIGHT 2
182#define PIECE_BISHOP 3
183#define PIECE_ROOK 4
184#define PIECE_QUEEN 5
185#define PIECE_KING 6
186
187/* color definitions */
188#define COLOR_WHITE 0
189#define COLOR_BLACK 1
190
191/* a mask, which includes COLOR and PIECE number */
192#define COLOR_PIECE_MASK 0x01f
193
194#define CP_MARK_MASK 0x20
195
196#define ILLEGAL_POSITION 255
197
198/* This is the build in upper limit of the search stack */
199/* This value defines the amount of memory allocated for the search stack */
200/* The search depth of this chess engine can never exceed this value */
201#define STACK_MAX_SIZE 5
202
203/* chess half move stack: twice the number of undo's, a user can do */
204#define CHM_USER_SIZE 6
205
206/* the CHM_LIST_SIZE must be larger than the maximum search depth */
207/* the overall size of ste half move stack */
208#define CHM_LIST_SIZE (STACK_MAX_SIZE+CHM_USER_SIZE+2)
209
210typedef int16_t eval_t; /* a variable type to store results from the evaluation */
211//#define EVAL_T_LOST -32768
212#define EVAL_T_MIN -32767
213#define EVAL_T_MAX 32767
214//#define EVAL_T_WIN 32767
215
216/* for maintainance of our own stack: this is the definition of one element on the stack */
217struct _stack_element_struct
218{
219 /* the current source position which is investigated */
220 uint8_t current_pos;
221 uint8_t current_cp;
222 uint8_t current_color; /* COLOR_WHITE or COLOR_BLACK: must be predefines */
223
224 /* the move which belongs to that value, both values are game positions */
225 uint8_t best_from_pos;
226 uint8_t best_to_pos;
227 /* the best value, which has been dicovered so far */
228 eval_t best_eval;
229};
230typedef struct _stack_element_struct stack_element_t;
231typedef struct _stack_element_struct *stack_element_p;
232
233/* chess half move history */
234struct _chm_struct
235{
236 uint8_t main_cp; /* the main piece, which is moved */
237 uint8_t main_src; /* the source position of the main piece */
238 uint8_t main_dest; /* the destination of the main piece */
239
240 uint8_t other_cp; /* another piece: the captured one, the ROOK in case of castling or PIECE_NONE */
241 uint8_t other_src; /* the delete position of other_cp. Often identical to main_dest except for e.p. and castling */
242 uint8_t other_dest; /* only used for castling: ROOK destination pos */
243
244 /* the position of the last pawn, which did a double move forward */
245 /* this is required to check en passant conditions */
246 /* this array can be indexed by the color of the current player */
247 /* this is the condition BEFORE the move was done */
248 uint8_t pawn_dbl_move[2];
249
250 /* flags for the movement of rook and king; required for castling */
251 /* a 1 means: castling is (still) possible */
252 /* a 0 means: castling not possible */
253 /* bit 0 left side white */
254 /* bit 1 right side white */
255 /* bit 2 left side black */
256 /* bit 3 right side black */
257 /* this is the condition BEFORE the move was done */
258 uint8_t castling_possible;
259};
260
261typedef struct _chm_struct chm_t;
262typedef struct _chm_struct *chm_p;
263
264/* little rook chess, main structure */
265struct _lrc_struct
266{
267 /* half-move (ply) counter: Counts the number of half-moves so far. Starts with 0 */
268 /* the lowest bit is used to derive the color of the current player */
269 /* will be set to zero in chess_SetupBoard() */
270 uint8_t ply_count;
271
272 /* the half move stack position counter, counts the number of elements in chm_list */
273 uint8_t chm_pos;
274
275 /* each element contains a colored piece, empty fields have value 0 */
276 /* the field with index 0 is black (lower left) */
277 uint8_t board[64];
278 /* the position of the last pawn, which did a double move forward */
279 /* this is required to check en passant conditions */
280 /* this array can be indexed by the color of the current player */
281 uint8_t pawn_dbl_move[2];
282
283 /* flags for the movement of rook and king; required for castling */
284 /* a 1 means: castling is (still) possible */
285 /* a 0 means: castling not possible */
286 /* bit 0 left side white */
287 /* bit 1 right side white */
288 /* bit 2 left side black */
289 /* bit 3 right side black */
290 uint8_t castling_possible;
291
292 /* board orientation */
293 /* 0: white is below COLOR_WHITE */
294 /* 1: black is below COLOR_BLACK */
295 /* bascially, this can be used as a color */
296 uint8_t orientation;
297
298 /* exchange colors of the pieces */
299 /* 0: white has an empty body, use this for bright background color */
300 /* 1: black has an empty body, use this for dark backround color */
301 uint8_t strike_out_color;
302
303 /* 0, when the game is ongoing */
304 /* 1, when the game is stopped (lost or draw) */
305 uint8_t is_game_end;
306 /* the color of the side which lost the game */
307 /* this value is only valid, when is_game_end is not 0 */
308 /* values 0 and 1 represent WHITE and BLACK, 2 means a draw */
309 uint8_t lost_side_color;
310
311
312
313 /* checks are executed in ce_LoopRecur */
314 /* these checks will put some marks on the board */
315 /* this will be used by the interface to find out */
316 /* legal moves */
317 uint8_t check_src_pos;
318 uint8_t check_mode; /* CHECK_MODE_NONE, CHECK_MODE_MOVEABLE, CHECK_MODE_TARGET_MOVE */
319
320
321 /* count of the attacking pieces, indexed by color */
322 uint8_t find_piece_cnt[2];
323
324 /* sum of the attacking pieces, indexed by color */
325 uint8_t find_piece_weight[2];
326
327 /* points to the current element of the search stack */
328 /* this stack is NEVER empty. The value 0 points to the first element of the stack */
329 /* actually "curr_depth" represent half-moves (plies) */
330 uint8_t curr_depth;
331 uint8_t max_depth;
332 stack_element_p curr_element;
333
334 /* allocated memory for the search stack */
335 stack_element_t stack_memory[STACK_MAX_SIZE];
336
337 /* the half move stack, used for move undo and depth search, size is stored in chm_pos */
338 chm_t chm_list[CHM_LIST_SIZE];
339};
340typedef struct _lrc_struct lrc_t;
341
342#define CHECK_MODE_NONE 0
343#define CHECK_MODE_MOVEABLE 1
344#define CHECK_MODE_TARGET_MOVE 2
345
346
347
348/*==============================================================*/
349/* global variables */
350/*==============================================================*/
351
352lrc_t lrc_obj;
353u8g2_t *lrc_u8g; /* pointer to the C object of u8g2 lib, this is used by the chess engine */
354
355
356/*==============================================================*/
357/* forward declarations */
358/*==============================================================*/
359
360/*
361 apply no inline to some of the functions:
362 avr-gcc very often inlines functions, however not inline saves a lot of program memory!
363 On the other hand there are some really short procedures which should be inlined (like cp_GetColor)
364 These procedures are marked static to prevent the generation of the expanded procedure, which
365 also saves space.
366*/
367
368uint8_t stack_Push(uint8_t color) U8G2_NOINLINE;
369void stack_Pop(void) U8G2_NOINLINE;
370void stack_InitCurrElement(void) U8G2_NOINLINE;
371void stack_Init(uint8_t max) U8G2_NOINLINE;
372void stack_SetMove(eval_t val, uint8_t to_pos) U8G2_NOINLINE;
373uint8_t cu_NextPos(uint8_t pos) U8G2_NOINLINE;
374static uint8_t cu_gpos2bpos(uint8_t gpos);
375static uint8_t cp_Construct(uint8_t color, uint8_t piece);
376static uint8_t cp_GetPiece(uint8_t cp);
377static uint8_t cp_GetColor(uint8_t cp);
378uint8_t cp_GetFromBoard(uint8_t pos) U8G2_NOINLINE;
379void cp_SetOnBoard(uint8_t pos, uint8_t cp) U8G2_NOINLINE;
380
381void cu_ClearBoard(void) U8G2_NOINLINE;
382void chess_SetupBoard(void) U8G2_NOINLINE;
383eval_t ce_Eval(void);
384
385void cu_ClearMoveHistory(void) U8G2_NOINLINE;
386void cu_ReduceHistoryByFullMove(void) U8G2_NOINLINE;
387void cu_UndoHalfMove(void) U8G2_NOINLINE;
388chm_p cu_PushHalfMove(void) U8G2_NOINLINE;
389
390
391void ce_CalculatePositionWeight(uint8_t pos);
392uint8_t ce_GetPositionAttackWeight(uint8_t pos, uint8_t color);
393
394void chess_Thinking(void);
395void ce_LoopPieces(void);
396
397
398/*==============================================================*/
399/* search stack */
400/*==============================================================*/
401
402/* get current element from stack */
403struct _stack_element_struct *stack_GetCurrElement(void)
404{
405 return lrc_obj.curr_element;
406}
407
408uint8_t stack_Push(uint8_t color)
409{
410 if ( lrc_obj.curr_depth == lrc_obj.max_depth )
411 return 0;
412 lrc_obj.curr_depth++;
413 lrc_obj.curr_element = lrc_obj.stack_memory+lrc_obj.curr_depth;
414
415 /* change view for the evaluation */
416 color ^= 1;
417 stack_GetCurrElement()->current_color = color;
418
419 return 1;
420}
421
422void stack_Pop(void)
423{
424 lrc_obj.curr_depth--;
425 lrc_obj.curr_element = lrc_obj.stack_memory+lrc_obj.curr_depth;
426}
427
428/* reset the current element on the stack */
429void stack_InitCurrElement(void)
430{
431 stack_element_p e = stack_GetCurrElement();
432 e->best_eval = EVAL_T_MIN;
433 e->best_from_pos = ILLEGAL_POSITION;
434 e->best_to_pos = ILLEGAL_POSITION;
435}
436
437/* resets the search stack (and the check mode) */
438void stack_Init(uint8_t max)
439{
440 lrc_obj.curr_depth = 0;
441 lrc_obj.curr_element = lrc_obj.stack_memory;
442 lrc_obj.max_depth = max;
443 lrc_obj.check_mode = CHECK_MODE_NONE;
444 stack_InitCurrElement();
445 stack_GetCurrElement()->current_color = lrc_obj.ply_count;
446 stack_GetCurrElement()->current_color &= 1;
447}
448
449/* assign evaluation value and store the move, if this is the best move */
450/* assumes, that current_pos contains the source position */
451void stack_SetMove(eval_t val, uint8_t to_pos)
452{
453 stack_element_p e = stack_GetCurrElement();
454 if ( e->best_eval < val )
455 {
456 e->best_eval = val;
457 e->best_from_pos = e->current_pos;
458 e->best_to_pos = to_pos;
459 }
460}
461
462/*
463 calculate next position on a 0x88 board
464 loop is constructed in this way:
465 i = 0;
466 do
467 {
468 ...
469 i = cu_NextPos(i);
470 } while( i != 0 );
471
472 next pos might be started with an illegal position like 255
473*/
474uint8_t cu_NextPos(uint8_t pos)
475{
476 /* calculate next gpos */
477 pos++;
478 if ( ( pos & 0x08 ) != 0 )
479 {
480 pos+= 0x10;
481 pos&= 0xf0;
482 }
483 if ( ( pos & 0x80 ) != 0 )
484 pos = 0;
485 return pos;
486}
487
488uint8_t cu_PrevPos(uint8_t pos)
489{
490 /* calculate prev gpos */
491 pos--;
492 if ( ( pos & 0x80 ) != 0 )
493 pos = 0x077;
494 else if ( ( pos & 0x08 ) != 0 )
495 {
496 pos &= 0xf0;
497 pos |= 0x07;
498 }
499 return pos;
500}
501
502
503/*==============================================================*/
504/* position transltion */
505/*==============================================================*/
506/*
507 there are two positions
508 1. game position (gpos): BCD encoded x-y values
509 2. board position (bpos): a number between 0 and 63, only used to access the board.
510*/
511/*
512 gpos: game position value
513 returns: board position
514 note: does not do any checks
515*/
516static uint8_t cu_gpos2bpos(uint8_t gpos)
517{
518 uint8_t bpos = gpos;
519 bpos &= 0xf0;
520 bpos >>= 1;
521 gpos &= 0x0f;
522 bpos |= gpos;
523 return bpos;
524}
525
526#define gpos_IsIllegal(gpos) ((gpos) & 0x088)
527
528
529/*==============================================================*/
530/* colored piece handling */
531/*==============================================================*/
532
533#define cp_IsMarked(cp) ((cp) & CP_MARK_MASK)
534
535
536/*
537 piece: one of PIECE_xxx
538 color: COLOR_WHITE or COLOR_BLACK
539
540 returns: A colored piece
541*/
542static uint8_t cp_Construct(uint8_t color, uint8_t piece)
543{
544 color <<= 4;
545 color |= piece;
546 return color;
547}
548
549/* inline is better than a macro */
550static uint8_t cp_GetPiece(uint8_t cp)
551{
552 cp &= 0x0f;
553 return cp;
554}
555
556/*
557 we could use a macro:
558 #define cp_GetColor(cp) (((cp) >> 4)&1)
559 however, inlined functions are sometimes much better
560*/
561static uint8_t cp_GetColor(uint8_t cp)
562{
563 cp >>= 4;
564 cp &= 1;
565 return cp;
566}
567
568/*
569 pos: game position
570 returns the colored piece at the given position
571*/
572uint8_t cp_GetFromBoard(uint8_t pos)
573{
574 return lrc_obj.board[cu_gpos2bpos(pos)];
575}
576
577/*
578 pos: game position
579 cp: colored piece
580*/
581void cp_SetOnBoard(uint8_t pos, uint8_t cp)
582{
583 /*printf("cp_SetOnBoard gpos:%02x cp:%02x\n", pos, cp);*/
584 lrc_obj.board[cu_gpos2bpos(pos)] = cp;
585}
586
587/*==============================================================*/
588/* global board access */
589/*==============================================================*/
590
591void cu_ClearBoard(void)
592{
593 uint8_t i;
594 /* clear the board */
595 for( i = 0; i < 64; i++ )
596 lrc_obj.board[i] = PIECE_NONE;
597
598 lrc_obj.ply_count = 0;
599 lrc_obj.orientation = COLOR_WHITE;
600
601 lrc_obj.pawn_dbl_move[0] = ILLEGAL_POSITION;
602 lrc_obj.pawn_dbl_move[1] = ILLEGAL_POSITION;
603
604 lrc_obj.castling_possible = 0x0f;
605
606 lrc_obj.is_game_end = 0;
607 lrc_obj.lost_side_color = 0;
608
609 /* clear half move history */
610 cu_ClearMoveHistory();
611
612}
613
614/*
615 test setup
616 white wins in one move
617*/
618void chess_SetupBoardTest01(void)
619{
620 cu_ClearBoard();
621 lrc_obj.board[7+7*8] = cp_Construct(COLOR_BLACK, PIECE_KING);
622 lrc_obj.board[7+5*8] = cp_Construct(COLOR_WHITE, PIECE_PAWN);
623 lrc_obj.board[3] = cp_Construct(COLOR_WHITE, PIECE_KING);
624 lrc_obj.board[0+7*8] = cp_Construct(COLOR_BLACK, PIECE_ROOK);
625 lrc_obj.board[6] = cp_Construct(COLOR_WHITE, PIECE_QUEEN);
626}
627
628/* setup the global board */
629void chess_SetupBoard(void)
630{
631 uint8_t i;
632 register uint8_t bp, wp;
633
634 /* clear the board */
635 cu_ClearBoard();
636
637 /* precronstruct pawns */
638 wp = cp_Construct(COLOR_WHITE, PIECE_PAWN);
639 bp = cp_Construct(COLOR_BLACK, PIECE_PAWN);
640
641 /* setup pawn */
642 for( i = 0; i < 8; i++ )
643 {
644 lrc_obj.board[i+8] = wp;
645 lrc_obj.board[i+6*8] = bp;
646 }
647
648 /* assign remaining pieces */
649
650 lrc_obj.board[0] = cp_Construct(COLOR_WHITE, PIECE_ROOK);
651 lrc_obj.board[1] = cp_Construct(COLOR_WHITE, PIECE_KNIGHT);
652 lrc_obj.board[2] = cp_Construct(COLOR_WHITE, PIECE_BISHOP);
653 lrc_obj.board[3] = cp_Construct(COLOR_WHITE, PIECE_QUEEN);
654 lrc_obj.board[4] = cp_Construct(COLOR_WHITE, PIECE_KING);
655 lrc_obj.board[5] = cp_Construct(COLOR_WHITE, PIECE_BISHOP);
656 lrc_obj.board[6] = cp_Construct(COLOR_WHITE, PIECE_KNIGHT);
657 lrc_obj.board[7] = cp_Construct(COLOR_WHITE, PIECE_ROOK);
658
659 lrc_obj.board[0+7*8] = cp_Construct(COLOR_BLACK, PIECE_ROOK);
660 lrc_obj.board[1+7*8] = cp_Construct(COLOR_BLACK, PIECE_KNIGHT);
661 lrc_obj.board[2+7*8] = cp_Construct(COLOR_BLACK, PIECE_BISHOP);
662 lrc_obj.board[3+7*8] = cp_Construct(COLOR_BLACK, PIECE_QUEEN);
663 lrc_obj.board[4+7*8] = cp_Construct(COLOR_BLACK, PIECE_KING);
664 lrc_obj.board[5+7*8] = cp_Construct(COLOR_BLACK, PIECE_BISHOP);
665 lrc_obj.board[6+7*8] = cp_Construct(COLOR_BLACK, PIECE_KNIGHT);
666 lrc_obj.board[7+7*8] = cp_Construct(COLOR_BLACK, PIECE_ROOK);
667
668 //chess_SetupBoardTest01();
669
670}
671
672
673
674/*==============================================================*/
675/* checks */
676/*==============================================================*/
677
678/*
679 checks if the position is somehow illegal
680*/
681uint8_t cu_IsIllegalPosition(uint8_t pos, uint8_t my_color)
682{
683 uint8_t board_cp;
684 /* check, if the position is offboard */
685 if ( gpos_IsIllegal(pos) != 0 )
686 return 1;
687 /* get the piece from the board */
688 board_cp = cp_GetFromBoard(pos);
689 /* check if hit our own pieces */
690 if ( board_cp != 0 )
691 if ( cp_GetColor(board_cp) == my_color )
692 return 1;
693 /* all ok, we could go to this position */
694 return 0;
695}
696
697/*==============================================================*/
698/* evaluation procedure */
699/*==============================================================*/
700
701/*
702 basic idea is to return a value between EVAL_T_MIN and EVAL_T_MAX
703*/
704
705/*
706 the weight table uses the PIECE number as index:
707 #define PIECE_NONE 0
708 #define PIECE_PAWN 1
709 #define PIECE_KNIGHT 2
710 #define PIECE_BISHOP 3
711 #define PIECE_ROOK 4
712 #define PIECE_QUEEN 5
713 #define PIECE_KING 6
714 the king itself is not counted
715*/
716uint8_t ce_piece_weight[] = { 0, 1, 3, 3, 5, 9, 0 };
717uint8_t ce_pos_weight[] = { 0, 1, 1, 2, 2, 1, 1, 0};
718/*
719 evaluate the current situation on the global board
720*/
721eval_t ce_Eval(void)
722{
723 uint8_t cp;
724 uint8_t is_my_king_present = 0;
725 uint8_t is_opposit_king_present = 0;
726 eval_t material_my_color = 0;
727 eval_t material_opposit_color = 0;
728 eval_t position_my_color = 0;
729 eval_t position_opposit_color = 0;
730 eval_t result;
731 uint8_t pos;
732
733 pos = 0;
734 do
735 {
736 /* get colored piece from the board */
737 cp = cp_GetFromBoard(pos);
738
739 if ( cp_GetPiece(cp) != PIECE_NONE )
740 {
741 if ( stack_GetCurrElement()->current_color == cp_GetColor(cp) )
742 {
743 /* this is our color */
744 /* check if we found our king */
745 if ( cp_GetPiece(cp) == PIECE_KING )
746 is_my_king_present = 1;
747 material_my_color += ce_piece_weight[cp_GetPiece(cp)];
748 if ( cp_GetPiece(cp) == PIECE_PAWN || cp_GetPiece(cp) == PIECE_KNIGHT )
749 {
750 position_my_color += ce_pos_weight[pos&7]*ce_pos_weight[(pos>>4)&7];
751 }
752 }
753 else
754 {
755 /* this is the opposit color */
756 if ( cp_GetPiece(cp) == PIECE_KING )
757 is_opposit_king_present = 1;
758 material_opposit_color += ce_piece_weight[cp_GetPiece(cp)];
759 if ( cp_GetPiece(cp) == PIECE_PAWN || cp_GetPiece(cp) == PIECE_KNIGHT )
760 {
761 position_opposit_color += ce_pos_weight[pos&7]*ce_pos_weight[(pos>>4)&7];
762 }
763 }
764 }
765 pos = cu_NextPos(pos);
766 } while( pos != 0 );
767
768
769 /* decide if we lost or won the game */
770 if ( is_my_king_present == 0 )
771 return EVAL_T_MIN; /*_LOST*/
772 if ( is_opposit_king_present == 0 )
773 return EVAL_T_MAX; /*_WIN*/
774
775 /* here is the evaluation function */
776
777 result = material_my_color - material_opposit_color;
778 result <<= 3;
779 result += position_my_color - position_opposit_color;
780 return result;
781}
782
783/*==============================================================*/
784/* move backup and restore */
785/*==============================================================*/
786
787
788/* this procedure must be called to keep the size as low as possible */
789/* if the chm_list is large enough, it could hold the complete history */
790/* but for an embedded controler... it is deleted for every engine search */
791void cu_ClearMoveHistory(void)
792{
793 lrc_obj.chm_pos = 0;
794}
795
796void cu_ReduceHistoryByFullMove(void)
797{
798 uint8_t i;
799 while( lrc_obj.chm_pos > CHM_USER_SIZE )
800 {
801 i = 0;
802 for(;;)
803 {
804 if ( i+2 >= lrc_obj.chm_pos )
805 break;
806 lrc_obj.chm_list[i] = lrc_obj.chm_list[i+2];
807 i++;
808 }
809 lrc_obj.chm_pos -= 2;
810 }
811}
812
813void cu_UndoHalfMove(void)
814{
815 chm_p chm;
816
817 if ( lrc_obj.chm_pos == 0 )
818 return;
819
820 lrc_obj.chm_pos--;
821
822 chm = lrc_obj.chm_list+lrc_obj.chm_pos;
823
824 lrc_obj.pawn_dbl_move[0] = chm->pawn_dbl_move[0];
825 lrc_obj.pawn_dbl_move[1] = chm->pawn_dbl_move[1];
826 lrc_obj.castling_possible = chm->castling_possible;
827
828 cp_SetOnBoard(chm->main_src, chm->main_cp);
829 cp_SetOnBoard(chm->main_dest, PIECE_NONE);
830
831 if ( chm->other_src != ILLEGAL_POSITION )
832 cp_SetOnBoard(chm->other_src, chm->other_cp);
833 if ( chm->other_dest != ILLEGAL_POSITION )
834 cp_SetOnBoard(chm->other_dest, PIECE_NONE);
835
836}
837
838/*
839 assumes, that the following members of the returned chm structure are filled
840 uint8_t main_cp; the main piece, which is moved
841 uint8_t main_src; the source position of the main piece
842 uint8_t main_dest; the destination of the main piece
843
844 uint8_t other_cp; another piece: the captured one, the ROOK in case of castling or PIECE_NONE
845 uint8_t other_src; the delete position of other_cp. Often identical to main_dest except for e.p. and castling
846 uint8_t other_dest; only used for castling: ROOK destination pos
847
848*/
849chm_p cu_PushHalfMove(void)
850{
851 chm_p chm;
852
853 chm = lrc_obj.chm_list+lrc_obj.chm_pos;
854 if ( lrc_obj.chm_pos < CHM_LIST_SIZE-1)
855 lrc_obj.chm_pos++;
856
857 chm->pawn_dbl_move[0] = lrc_obj.pawn_dbl_move[0];
858 chm->pawn_dbl_move[1] = lrc_obj.pawn_dbl_move[1];
859 chm->castling_possible = lrc_obj.castling_possible;
860 return chm;
861}
862
863
864char chess_piece_to_char[] = "NBRQK";
865
866/*
867 simple moves on empty field: Ka1-b2
868 capture moves: Ka1xb2
869 castling: 0-0 or 0-0-0
870*/
871
872static void cu_add_pos(char *s, uint8_t pos) U8G2_NOINLINE;
873
874static void cu_add_pos(char *s, uint8_t pos)
875{
876 *s = pos;
877 *s &= 15;
878 *s += 'a';
879 s++;
880 *s = pos;
881 *s >>= 4;
882 *s += '1';
883}
884
885const char *cu_GetHalfMoveStr(uint8_t idx)
886{
887 chm_p chm;
888 static char buf[7]; /*Ka1-b2*/
889 char *p = buf;
890 chm = lrc_obj.chm_list+idx;
891
892 if ( cp_GetPiece(chm->main_cp) != PIECE_NONE )
893 {
894 if ( cp_GetPiece(chm->main_cp) > PIECE_PAWN )
895 {
896 *p++ = chess_piece_to_char[cp_GetPiece(chm->main_cp)-2];
897 }
898 cu_add_pos(p, chm->main_src);
899 p+=2;
900 if ( cp_GetPiece(chm->other_cp) == PIECE_NONE )
901 *p++ = '-';
902 else
903 *p++ = 'x';
904 cu_add_pos(p, chm->main_dest);
905 p+=2;
906 }
907 *p = '\0';
908 return buf;
909}
910
911
912
913
914
915/*==============================================================*/
916/* move */
917/*==============================================================*/
918
919/*
920 Move a piece from source position to a destination on the board
921 This function
922 - does not perform any checking
923 - however it processes "en passant" and casteling
924 - backup the move and allow 1x undo
925
926 2011-02-05:
927 - fill pawn_dbl_move[] for double pawn moves
928 --> done
929 - Implement casteling
930 --> done
931 - en passant
932 --> done
933 - pawn conversion/promotion
934 --> done
935 - half-move backup
936 --> done
937 - cleanup everything, minimize variables
938 --> done
939*/
940
941void cu_Move(uint8_t src, uint8_t dest)
942{
943 /* start backup structure */
944 chm_p chm = cu_PushHalfMove();
945
946 /* these are the values from the board at the positions, provided as arguments to this function */
947 uint8_t cp_src, cp_dest;
948
949 /* Maybe a second position is cleared and one additional location is set */
950 uint8_t clr_pos2;
951 uint8_t set_pos2;
952 uint8_t set_cp2;
953
954 /* get values from board */
955 cp_src = cp_GetFromBoard(src);
956 cp_dest = cp_GetFromBoard(dest);
957
958 /* fill backup structure */
959
960 chm->main_cp = cp_src;
961 chm->main_src = src;
962 chm->main_dest = dest;
963
964 chm->other_cp = cp_dest; /* prepace capture backup */
965 chm->other_src = dest;
966 chm->other_dest = ILLEGAL_POSITION;
967
968 /* setup results as far as possible with some suitable values */
969
970 clr_pos2 = ILLEGAL_POSITION; /* for en passant and castling, two positions might be cleared */
971 set_pos2 = ILLEGAL_POSITION; /* only used for castling */
972 set_cp2 = PIECE_NONE; /* ROOK for castling */
973
974 /* check for PAWN */
975 if ( cp_GetPiece(cp_src) == PIECE_PAWN )
976 {
977
978 /* double step: is the distance 2 rows */
979 if ( (src - dest == 32) || ( dest - src == 32 ) )
980 {
981 /* remember the destination position */
982 lrc_obj.pawn_dbl_move[cp_GetColor(cp_src)] = dest;
983 }
984
985 /* check if the PAWN is able to promote */
986 else if ( (dest>>4) == 0 || (dest>>4) == 7 )
987 {
988 /* do simple "queening" */
989 cp_src &= ~PIECE_PAWN;
990 cp_src |= PIECE_QUEEN;
991 }
992
993 /* is it en passant capture? */
994 /* check for side move */
995 else if ( ((src + dest) & 1) != 0 )
996 {
997 /* check, if target field is empty */
998 if ( cp_GetPiece(cp_dest) == PIECE_NONE )
999 {
1000 /* this is en passant */
1001 /* no further checking required, because legal moves are assumed here */
1002 /* however... the captured pawn position must be valid */
1003 clr_pos2 = lrc_obj.pawn_dbl_move[cp_GetColor(cp_src) ^ 1];
1004 chm->other_src = clr_pos2;
1005 chm->other_cp = cp_GetFromBoard(clr_pos2);
1006 }
1007 }
1008 }
1009
1010 /* check for the KING */
1011 else if ( cp_GetPiece(cp_src) == PIECE_KING )
1012 {
1013 /* disallow castling, if the KING has moved */
1014 if ( cp_GetColor(cp_src) == COLOR_WHITE )
1015 {
1016 /* if white KING has moved, disallow castling for white */
1017 lrc_obj.castling_possible &= 0x0c;
1018 }
1019 else
1020 {
1021 /* if black KING has moved, disallow castling for black */
1022 lrc_obj.castling_possible &= 0x03;
1023 }
1024
1025 /* has it been castling to the left? */
1026 if ( src - dest == 2 )
1027 {
1028 /* let the ROOK move to pos2 */
1029 set_pos2 = src-1;
1030 set_cp2 = cp_GetFromBoard(src-4);
1031
1032 /* the ROOK must be cleared from the original position */
1033 clr_pos2 = src-4;
1034
1035 chm->other_cp = set_cp2;
1036 chm->other_src = clr_pos2;
1037 chm->other_dest = set_pos2;
1038 }
1039
1040 /* has it been castling to the right? */
1041 else if ( dest - src == 2 )
1042 {
1043 /* let the ROOK move to pos2 */
1044 set_pos2 = src+1;
1045 set_cp2 = cp_GetFromBoard(src+3);
1046
1047 /* the ROOK must be cleared from the original position */
1048 clr_pos2 = src+3;
1049
1050 chm->other_cp = set_cp2;
1051 chm->other_src = clr_pos2;
1052 chm->other_dest = set_pos2;
1053
1054 }
1055
1056 }
1057
1058 /* check for the ROOK */
1059 else if ( cp_GetPiece(cp_src) == PIECE_ROOK )
1060 {
1061 /* disallow white left castling */
1062 if ( src == 0x00 )
1063 lrc_obj.castling_possible &= ~0x01;
1064 /* disallow white right castling */
1065 if ( src == 0x07 )
1066 lrc_obj.castling_possible &= ~0x02;
1067 /* disallow black left castling */
1068 if ( src == 0x70 )
1069 lrc_obj.castling_possible &= ~0x04;
1070 /* disallow black right castling */
1071 if ( src == 0x77 )
1072 lrc_obj.castling_possible &= ~0x08;
1073 }
1074
1075
1076 /* apply new board situation */
1077
1078 cp_SetOnBoard(dest, cp_src);
1079
1080 if ( set_pos2 != ILLEGAL_POSITION )
1081 cp_SetOnBoard(set_pos2, set_cp2);
1082
1083 cp_SetOnBoard(src, PIECE_NONE);
1084
1085 if ( clr_pos2 != ILLEGAL_POSITION )
1086 cp_SetOnBoard(clr_pos2, PIECE_NONE);
1087
1088
1089}
1090
1091/*
1092 this subprocedure decides for evaluation of the current board situation or further (deeper) investigation
1093 Argument pos is the new target position if the current piece
1094
1095*/
1096uint8_t ce_LoopRecur(uint8_t pos)
1097{
1098 eval_t eval;
1099
1100 /* 1. check if target position is occupied by the same player (my_color) */
1101 /* of if pos is somehow illegal or not valid */
1102 if ( cu_IsIllegalPosition(pos, stack_GetCurrElement()->current_color) != 0 )
1103 return 0;
1104
1105 /* 2. move piece to the specified position, capture opponent piece if required */
1106 cu_Move(stack_GetCurrElement()->current_pos, pos);
1107
1108
1109 /* 3. */
1110 /* if depth reached: evaluate */
1111 /* else: go down next level */
1112 /* no eval if there had been any valid half-moves, so the default value (MIN) will be returned. */
1113 if ( stack_Push(stack_GetCurrElement()->current_color) == 0 )
1114 {
1115 eval = ce_Eval();
1116 }
1117 else
1118 {
1119 /* init the element, which has been pushed */
1120 stack_InitCurrElement();
1121 /* start over with ntext level */
1122 ce_LoopPieces();
1123 /* get the best move from opponents view, so invert the result */
1124 eval = -stack_GetCurrElement()->best_eval;
1125 stack_Pop();
1126 }
1127
1128 /* 4. store result */
1129 stack_SetMove(eval, pos);
1130
1131 /* 5. undo the move */
1132 cu_UndoHalfMove();
1133
1134 /* 6. check special modes */
1135 /* the purpose of these checks is to mark special pieces and positions on the board */
1136 /* these marks can be checked by the user interface to highlight special positions */
1137 if ( lrc_obj.check_mode != 0 )
1138 {
1139 stack_element_p e = stack_GetCurrElement();
1140 if ( lrc_obj.check_mode == CHECK_MODE_MOVEABLE )
1141 {
1142 cp_SetOnBoard(e->current_pos, e->current_cp | CP_MARK_MASK );
1143 }
1144 else if ( lrc_obj.check_mode == CHECK_MODE_TARGET_MOVE )
1145 {
1146 if ( e->current_pos == lrc_obj.check_src_pos )
1147 {
1148 cp_SetOnBoard(pos, cp_GetFromBoard(pos) | CP_MARK_MASK );
1149 }
1150 }
1151 }
1152 return 1;
1153}
1154
1155/*==============================================================*/
1156/* move pieces which can move one or more steps into a direction */
1157/*==============================================================*/
1158
1159/*
1160 subprocedure to generate various target positions for some pieces
1161 special cases are handled in the piece specific sub-procedure
1162
1163 Arguments:
1164 d: a list of potential directions
1165 is_multi_step: if the piece can only do one step (zero for KING and KNIGHT)
1166*/
1167static const uint8_t ce_dir_offset_rook[] CHESS_PROGMEM = { 1, 16, (uint8_t)-16, (uint8_t)-1, 0 };
1168static const uint8_t ce_dir_offset_bishop[] CHESS_PROGMEM = { 15, 17, (uint8_t)-17, (uint8_t)-15, 0 };
1169static const uint8_t ce_dir_offset_queen[] CHESS_PROGMEM = { 1, 16, (uint8_t)-16, (uint8_t)-1, 15, 17, (uint8_t)-17, (uint8_t)-15, 0 };
1170static const uint8_t ce_dir_offset_knight[] CHESS_PROGMEM = {14, (uint8_t)-14, 18, (uint8_t)-18, 31, (uint8_t)-31, 33, (uint8_t)-33, 0};
1171
1172void ce_LoopDirsSingleMultiStep(const uint8_t *d, uint8_t is_multi_step)
1173{
1174 uint8_t loop_pos;
1175
1176 /* with all directions */
1177 for(;;)
1178 {
1179 if ( chess_pgm_read(d) == 0 )
1180 break;
1181
1182 /* start again from the initial position */
1183 loop_pos = stack_GetCurrElement()->current_pos;
1184
1185 /* check direction */
1186 do
1187 {
1188 /* check next position into one direction */
1189 loop_pos += chess_pgm_read(d);
1190
1191 /*
1192 go further to ce_LoopRecur()
1193 0 will be returned if the target position is illegal or a piece of the own color
1194 this is used to stop walking into one direction
1195 */
1196 if ( ce_LoopRecur(loop_pos) == 0 )
1197 break;
1198
1199 /* stop if we had hit another piece */
1200 if ( cp_GetPiece(cp_GetFromBoard(loop_pos)) != PIECE_NONE )
1201 break;
1202 } while( is_multi_step );
1203 d++;
1204 }
1205}
1206
1207void ce_LoopRook(void)
1208{
1209 ce_LoopDirsSingleMultiStep(ce_dir_offset_rook, 1);
1210}
1211
1212void ce_LoopBishop(void)
1213{
1214 ce_LoopDirsSingleMultiStep(ce_dir_offset_bishop, 1);
1215}
1216
1217void ce_LoopQueen(void)
1218{
1219 ce_LoopDirsSingleMultiStep(ce_dir_offset_queen, 1);
1220}
1221
1222void ce_LoopKnight(void)
1223{
1224 ce_LoopDirsSingleMultiStep(ce_dir_offset_knight, 0);
1225}
1226
1227
1228
1229/*==============================================================*/
1230/* move king */
1231/*==============================================================*/
1232
1233uint8_t cu_IsKingCastling(uint8_t mask, int8_t direction, uint8_t cnt) U8G2_NOINLINE;
1234
1235/*
1236 checks, if the king can do castling
1237
1238 Arguments:
1239 mask: the bit-mask for the global "castling possible" flag
1240 direction: left castling: -1, right castling 1
1241 cnt: number of fields to be checked: 3 or 2
1242*/
1243uint8_t cu_IsKingCastling(uint8_t mask, int8_t direction, uint8_t cnt)
1244{
1245 uint8_t pos;
1246 uint8_t opponent_color;
1247
1248 /* check if the current board state allows castling */
1249 if ( (lrc_obj.castling_possible & mask) == 0 )
1250 return 0; /* castling not allowed */
1251
1252 /* get the position of the KING, could be white or black king */
1253 pos = stack_GetCurrElement()->current_pos;
1254
1255 /* calculate the color of the opponent */
1256 opponent_color = 1;
1257 opponent_color -= stack_GetCurrElement()->current_color;
1258
1259 /* if the KING itself is given check... */
1260 if ( ce_GetPositionAttackWeight(pos, opponent_color) > 0 )
1261 return 0;
1262
1263
1264 /* check if fields in the desired direction are emtpy */
1265 for(;;)
1266 {
1267 /* go to the next field */
1268 pos += direction;
1269 /* check for a piece */
1270 if ( cp_GetPiece(cp_GetFromBoard(pos)) != PIECE_NONE )
1271 return 0; /* castling not allowed */
1272
1273 /* if some of the fields are under attack */
1274 if ( ce_GetPositionAttackWeight(pos, opponent_color) > 0 )
1275 return 0;
1276
1277 cnt--;
1278 if ( cnt == 0 )
1279 break;
1280 }
1281 return 1; /* castling allowed */
1282}
1283
1284void ce_LoopKing(void)
1285{
1286 /*
1287 there is an interessting timing problem in this procedure
1288 it must be checked for castling first and as second step the normal
1289 KING movement. If we would first check for normal moves, than
1290 any marks might be overwritten by the ROOK in the case of castling.
1291 */
1292
1293 /* castling (this must be done before checking normal moves (see above) */
1294 if ( stack_GetCurrElement()->current_color == COLOR_WHITE )
1295 {
1296 /* white left castling */
1297 if ( cu_IsKingCastling(1, -1, 3) != 0 )
1298 {
1299 /* check for attacked fields */
1300 ce_LoopRecur(stack_GetCurrElement()->current_pos-2);
1301 }
1302 /* white right castling */
1303 if ( cu_IsKingCastling(2, 1, 2) != 0 )
1304 {
1305 /* check for attacked fields */
1306 ce_LoopRecur(stack_GetCurrElement()->current_pos+2);
1307 }
1308 }
1309 else
1310 {
1311 /* black left castling */
1312 if ( cu_IsKingCastling(4, -1, 3) != 0 )
1313 {
1314 /* check for attacked fields */
1315 ce_LoopRecur(stack_GetCurrElement()->current_pos-2);
1316 }
1317 /* black right castling */
1318 if ( cu_IsKingCastling(8, 1, 2) != 0 )
1319 {
1320 /* check for attacked fields */
1321 ce_LoopRecur(stack_GetCurrElement()->current_pos+2);
1322 }
1323 }
1324
1325 /* reuse queen directions */
1326 ce_LoopDirsSingleMultiStep(ce_dir_offset_queen, 0);
1327}
1328
1329
1330/*==============================================================*/
1331/* move pawn */
1332/*==============================================================*/
1333
1334/*
1335 doppelschritt: nur von der grundlinie aus, beide (!) felder vor dem bauern m�ssen frei sein
1336 en passant: nur unmittelbar nachdem ein doppelschritt ausgef�hrt wurde.
1337*/
1338void ce_LoopPawnSideCapture(uint8_t loop_pos)
1339{
1340 if ( gpos_IsIllegal(loop_pos) == 0 )
1341 {
1342 /* get the piece from the board */
1343 /* if the field is NOT empty */
1344 if ( cp_GetPiece(cp_GetFromBoard(loop_pos)) != PIECE_NONE )
1345 {
1346 /* normal capture */
1347 ce_LoopRecur(loop_pos);
1348 /* TODO: check for pawn conversion/promotion */
1349 }
1350 else
1351 {
1352 /* check conditions for en passant capture */
1353 if ( stack_GetCurrElement()->current_color == COLOR_WHITE )
1354 {
1355 if ( lrc_obj.pawn_dbl_move[COLOR_BLACK]+16 == loop_pos )
1356 {
1357 ce_LoopRecur(loop_pos);
1358 /* note: pawn conversion/promotion can not occur */
1359 }
1360 }
1361 else
1362 {
1363 if ( lrc_obj.pawn_dbl_move[COLOR_WHITE] == loop_pos+16 )
1364 {
1365 ce_LoopRecur(loop_pos);
1366 /* note: pawn conversion/promotion can not occur */
1367 }
1368 }
1369 }
1370 }
1371}
1372
1373void ce_LoopPawn(void)
1374{
1375 uint8_t initial_pos = stack_GetCurrElement()->current_pos;
1376 uint8_t my_color = stack_GetCurrElement()->current_color;
1377
1378 uint8_t loop_pos;
1379 uint8_t line;
1380
1381 /* one step forward */
1382
1383 loop_pos = initial_pos;
1384 line = initial_pos;
1385 line >>= 4;
1386 if ( my_color == COLOR_WHITE )
1387 loop_pos += 16;
1388 else
1389 loop_pos -= 16;
1390 if ( gpos_IsIllegal(loop_pos) == 0 )
1391 {
1392 /* if the field is empty */
1393 if ( cp_GetPiece(cp_GetFromBoard(loop_pos)) == PIECE_NONE )
1394 {
1395 /* TODO: check for and loop through piece conversion/promotion */
1396 ce_LoopRecur(loop_pos);
1397
1398 /* second step forward */
1399
1400 /* if pawn is on his starting line */
1401 if ( (my_color == COLOR_WHITE && line == 1) || (my_color == COLOR_BLACK && line == 6 ) )
1402 {
1403 /* the place before the pawn is not occupied, so we can do double moves, see above */
1404
1405 if ( my_color == COLOR_WHITE )
1406 loop_pos += 16;
1407 else
1408 loop_pos -= 16;
1409 if ( cp_GetPiece(cp_GetFromBoard(loop_pos)) == PIECE_NONE )
1410 {
1411 /* this is a special case, other promotions of the pawn can not occur */
1412 ce_LoopRecur(loop_pos);
1413 }
1414 }
1415 }
1416 }
1417
1418 /* capture */
1419
1420 loop_pos = initial_pos;
1421 if ( my_color == COLOR_WHITE )
1422 loop_pos += 15;
1423 else
1424 loop_pos -= 15;
1425 ce_LoopPawnSideCapture(loop_pos);
1426
1427
1428 loop_pos = initial_pos;
1429 if ( my_color == COLOR_WHITE )
1430 loop_pos += 17;
1431 else
1432 loop_pos -= 17;
1433 ce_LoopPawnSideCapture(loop_pos);
1434}
1435
1436/*==============================================================*/
1437/* attacked */
1438/*==============================================================*/
1439
1440/*
1441 from a starting position, search for a piece, that might jump to that postion.
1442 return:
1443 the two global variables
1444 lrc_obj.find_piece_weight[0];
1445 lrc_obj.find_piece_weight[1];
1446 will be increased by the weight of the attacked pieces of that color.
1447 it is usually required to reset these global variables to zero, before using
1448 this function.
1449*/
1450
1451void ce_FindPieceByStep(uint8_t start_pos, uint8_t piece, const uint8_t *d, uint8_t is_multi_step)
1452{
1453 uint8_t loop_pos, cp;
1454
1455 /* with all directions */
1456 for(;;)
1457 {
1458 if ( chess_pgm_read(d) == 0 )
1459 break;
1460
1461 /* start again from the initial position */
1462 loop_pos = start_pos;
1463
1464 /* check direction */
1465 do
1466 {
1467 /* check next position into one direction */
1468 loop_pos += chess_pgm_read(d);
1469
1470 /* check if the board boundary has been crossed */
1471 if ( (loop_pos & 0x088) != 0 )
1472 break;
1473
1474 /* get the colored piece from the board */
1475 cp = cp_GetFromBoard(loop_pos);
1476
1477 /* stop if we had hit another piece */
1478 if ( cp_GetPiece(cp) != PIECE_NONE )
1479 {
1480 /* if it is the piece we are looking for, then add the weight */
1481 if ( cp_GetPiece(cp) == piece )
1482 {
1483 lrc_obj.find_piece_weight[cp_GetColor(cp)] += ce_piece_weight[piece];
1484 lrc_obj.find_piece_cnt[cp_GetColor(cp)]++;
1485 }
1486 /* in any case, break out of the inner loop */
1487 break;
1488 }
1489 } while( is_multi_step );
1490 d++;
1491 }
1492}
1493
1494void ce_FindPawnPiece(uint8_t dest_pos, uint8_t color)
1495{
1496 uint8_t cp;
1497 /* check if the board boundary has been crossed */
1498 if ( (dest_pos & 0x088) == 0 )
1499 {
1500 /* get the colored piece from the board */
1501 cp = cp_GetFromBoard(dest_pos);
1502 /* only if there is a pawn of the matching color */
1503 if ( cp_GetPiece(cp) == PIECE_PAWN )
1504 {
1505 if ( cp_GetColor(cp) == color )
1506 {
1507 /* the weight of the PAWN */
1508 lrc_obj.find_piece_weight[color] += 1;
1509 lrc_obj.find_piece_cnt[color]++;
1510 }
1511 }
1512 }
1513}
1514
1515
1516/*
1517 find out, which pieces do attack a specified field
1518 used to
1519 - check if the KING can do castling
1520 - check if the KING must move
1521
1522 may be used in the eval procedure ... once...
1523
1524 the result is stored in the global array
1525 uint8_t lrc_obj.find_piece_weight[2];
1526 which is indexed with the color.
1527 lrc_obj.find_piece_weight[COLOR_WHITE] is the sum of all white pieces
1528 which can directly move to this field.
1529
1530 example:
1531 if the black KING is at "pos" and lrc_obj.find_piece_weight[COLOR_WHITE] is not zero
1532 (after executing ce_CalculatePositionWeight(pos)) then the KING must be protected or moveed, because
1533 the KING was given check.
1534*/
1535
1536void ce_CalculatePositionWeight(uint8_t pos)
1537{
1538
1539 lrc_obj.find_piece_weight[0] = 0;
1540 lrc_obj.find_piece_weight[1] = 0;
1541 lrc_obj.find_piece_cnt[0] = 0;
1542 lrc_obj.find_piece_cnt[1] = 0;
1543
1544 if ( (pos & 0x088) != 0 )
1545 return;
1546
1547 ce_FindPieceByStep(pos, PIECE_ROOK, ce_dir_offset_rook, 1);
1548 ce_FindPieceByStep(pos, PIECE_BISHOP, ce_dir_offset_bishop, 1);
1549 ce_FindPieceByStep(pos, PIECE_QUEEN, ce_dir_offset_queen, 1);
1550 ce_FindPieceByStep(pos, PIECE_KNIGHT, ce_dir_offset_knight, 0);
1551 ce_FindPieceByStep(pos, PIECE_KING, ce_dir_offset_queen, 0);
1552
1553 ce_FindPawnPiece(pos+17, COLOR_BLACK);
1554 ce_FindPawnPiece(pos+15, COLOR_BLACK);
1555 ce_FindPawnPiece(pos-17, COLOR_WHITE);
1556 ce_FindPawnPiece(pos-15, COLOR_WHITE);
1557}
1558
1559/*
1560 calculate the summed weight of pieces with specified color which can move to a specified position
1561
1562 argument:
1563 pos: the position which should be analysed
1564 color: the color of those pieces which should be analysed
1565 e.g. if a black piece is at 'pos' and 'color' is white then this procedure returns the white atting count
1566*/
1567uint8_t ce_GetPositionAttackWeight(uint8_t pos, uint8_t color)
1568{
1569 ce_CalculatePositionWeight(pos);
1570 return lrc_obj.find_piece_weight[color];
1571}
1572
1573uint8_t ce_GetPositionAttackCount(uint8_t pos, uint8_t color)
1574{
1575 ce_CalculatePositionWeight(pos);
1576 return lrc_obj.find_piece_cnt[color];
1577}
1578
1579
1580/*==============================================================*/
1581/* depth search starts here: loop over all pieces of the current color on the board */
1582/*==============================================================*/
1583
1584void ce_LoopPieces(void)
1585{
1586 stack_element_p e = stack_GetCurrElement();
1587 /* start with lower left position (A1) */
1588 e->current_pos = 0;
1589 do
1590 {
1591 e->current_cp = cp_GetFromBoard(e->current_pos);
1592 /* check if the position on the board is empty */
1593 if ( e->current_cp != 0 )
1594 {
1595 /* only generate moves for the current color */
1596 if ( e->current_color == cp_GetColor(e->current_cp) )
1597 {
1598 chess_Thinking();
1599
1600 /* find out which piece is used */
1601 switch(cp_GetPiece(e->current_cp))
1602 {
1603 case PIECE_NONE:
1604 break;
1605 case PIECE_PAWN:
1606 ce_LoopPawn();
1607 break;
1608 case PIECE_KNIGHT:
1609 ce_LoopKnight();
1610 break;
1611 case PIECE_BISHOP:
1612 ce_LoopBishop();
1613 break;
1614 case PIECE_ROOK:
1615 ce_LoopRook();
1616 break;
1617 case PIECE_QUEEN:
1618 ce_LoopQueen();
1619 break;
1620 case PIECE_KING:
1621 ce_LoopKing();
1622 break;
1623 }
1624 }
1625 }
1626 e->current_pos = cu_NextPos(e->current_pos);
1627 } while( e->current_pos != 0 );
1628}
1629
1630/*==============================================================*/
1631/* user interface */
1632/*==============================================================*/
1633
1634/*
1635eval_t chess_EvalCurrBoard(uint8_t color)
1636{
1637 stack_Init(0);
1638 stack_GetCurrElement()->current_color = color;
1639 ce_LoopPieces();
1640 return stack_GetCurrElement()->best_eval;
1641}
1642*/
1643
1644/* clear any marks on the board */
1645void chess_ClearMarks(void)
1646{
1647 uint8_t i;
1648 for( i = 0; i < 64; i++ )
1649 lrc_obj.board[i] &= ~CP_MARK_MASK;
1650}
1651
1652/*
1653 Mark all pieces which can do moves. This is done by setting flags on the global board
1654*/
1655void chess_MarkMovable(void)
1656{
1657 stack_Init(0);
1658 //stack_GetCurrElement()->current_color = color;
1659 lrc_obj.check_mode = CHECK_MODE_MOVEABLE;
1660 ce_LoopPieces();
1661}
1662
1663/*
1664 Checks, if the piece can move from src_pos to dest_pos
1665
1666 src_pos: The game position of a piece on the chess board
1667*/
1668void chess_MarkTargetMoves(uint8_t src_pos)
1669{
1670 stack_Init(0);
1671 stack_GetCurrElement()->current_color = cp_GetColor(cp_GetFromBoard(src_pos));
1672 lrc_obj.check_src_pos = src_pos;
1673 lrc_obj.check_mode = CHECK_MODE_TARGET_MOVE;
1674 ce_LoopPieces();
1675}
1676
1677/*
1678 first call should start with 255
1679 this procedure will return 255 if
1680 - there are no marks at all
1681 - it has looped over all marks once
1682*/
1683uint8_t chess_GetNextMarked(uint8_t arg, uint8_t is_prev)
1684{
1685 uint8_t i;
1686 uint8_t pos = arg;
1687 for(i = 0; i < 64; i++)
1688 {
1689 if ( is_prev != 0 )
1690 pos = cu_PrevPos(pos);
1691 else
1692 pos = cu_NextPos(pos);
1693 if ( arg != 255 && pos == 0 )
1694 return 255;
1695 if ( cp_IsMarked(cp_GetFromBoard(pos)) )
1696 return pos;
1697 }
1698 return 255;
1699}
1700
1701
1702/* make a manual move: this is a little bit more than cu_Move() */
1703void chess_ManualMove(uint8_t src, uint8_t dest)
1704{
1705 uint8_t cp;
1706
1707 /* printf("chess_ManualMove %02x -> %02x\n", src, dest); */
1708
1709 /* if all other things fail, this is the place where the game is to be decided: */
1710 /* ... if the KING is captured */
1711 cp = cp_GetFromBoard(dest);
1712 if ( cp_GetPiece(cp) == PIECE_KING )
1713 {
1714 lrc_obj.is_game_end = 1;
1715 lrc_obj.lost_side_color = cp_GetColor(cp);
1716 }
1717
1718 /* clear ply history here, to avoid memory overflow */
1719 /* may be the last X moves can be kept here */
1720 cu_ReduceHistoryByFullMove();
1721 /* perform the move on the board */
1722 cu_Move(src, dest);
1723
1724 /* update en passant double move positions: en passant position is removed after two half moves */
1725 lrc_obj.pawn_dbl_move[lrc_obj.ply_count&1] = ILLEGAL_POSITION;
1726
1727 /* update the global half move counter */
1728 lrc_obj.ply_count++;
1729
1730
1731 /* make a small check about the end of the game */
1732 /* use at least depth 1, because we must know if the king can still move */
1733 /* this is: King moves at level 0 and will be captured at level 1 */
1734 /* so we check if the king can move and will not be captured at search level 1 */
1735
1736 stack_Init(1);
1737 ce_LoopPieces();
1738
1739 /* printf("chess_ManualMove/analysis best_from_pos %02x -> best_to_pos %02x\n", stack_GetCurrElement()->best_from_pos, stack_GetCurrElement()->best_to_pos); */
1740
1741 /* analyse the eval result */
1742
1743 /* check if the other player has any moves left */
1744 if ( stack_GetCurrElement()->best_from_pos == ILLEGAL_POSITION )
1745 {
1746 uint8_t color;
1747 /* conditions: */
1748 /* 1. no King, should never happen, opposite color has won */
1749 /* this is already checked above at the beginning if this procedure */
1750 /* 2. King is under attack, opposite color has won */
1751 /* 3. King is not under attack, game is a draw */
1752
1753 uint8_t i = 0;
1754 color = lrc_obj.ply_count;
1755 color &= 1;
1756 do
1757 {
1758 cp = cp_GetFromBoard(i);
1759 /* look for the King */
1760 if ( cp_GetPiece(cp) == PIECE_KING )
1761 {
1762 if ( cp_GetColor(cp) == color )
1763 {
1764 /* check if KING is attacked */
1765 if ( ce_GetPositionAttackCount(i, color^1) != 0 )
1766 {
1767 /* KING is under attack (check) and can not move: Game is lost */
1768 lrc_obj.is_game_end = 1;
1769 lrc_obj.lost_side_color = color;
1770 }
1771 else
1772 {
1773 /* KING is NOT under attack (check) but can not move: Game is a draw */
1774 lrc_obj.is_game_end = 1;
1775 lrc_obj.lost_side_color = 2;
1776 }
1777 /* break out of the loop */
1778 break;
1779 }
1780 }
1781 i = cu_NextPos(i);
1782 } while( i != 0 );
1783 }
1784}
1785
1786/* let the computer do a move */
1787void chess_ComputerMove(uint8_t depth)
1788{
1789 stack_Init(depth);
1790
1791 //stack_GetCurrElement()->current_color = lrc_obj.ply_count;
1792 //stack_GetCurrElement()->current_color &= 1;
1793
1794 cu_ReduceHistoryByFullMove();
1795 ce_LoopPieces();
1796
1797 chess_ManualMove(stack_GetCurrElement()->best_from_pos, stack_GetCurrElement()->best_to_pos);
1798}
1799
1800
1801/*==============================================================*/
1802/* unix code */
1803/*==============================================================*/
1804
1805#ifdef UNIX_MAIN
1806
1807#else
1808
1809/*==============================================================*/
1810/* display menu */
1811/*==============================================================*/
1812
1813extern const uint8_t chess_pieces_body_bm[] CHESS_PROGMEM; // forward decl
1814extern const uint8_t chess_black_pieces_bm[] CHESS_PROGMEM; // forward decl
1815
1816// uint8_t chess_key_code = 0; // obsolete, u8g2 does proper debouncing
1817uint8_t chess_key_cmd = 0;
1818#define CHESS_STATE_MENU 0
1819#define CHESS_STATE_SELECT_START 1
1820#define CHESS_STATE_SELECT_PIECE 2
1821#define CHESS_STATE_SELECT_TARGET_POS 3
1822#define CHESS_STATE_THINKING 4
1823#define CHESS_STATE_GAME_END 5
1824uint8_t chess_state = CHESS_STATE_MENU;
1825uint8_t chess_source_pos = 255;
1826uint8_t chess_target_pos = 255;
1827
1828#define MNU_FONT u8g2_font_5x8_tr
1829#define MNU_ENTRY_HEIGHT 9
1830
1831const char *mnu_title = "Little Rook Chess";
1832const char *mnu_list[] = { "New Game (White)", "New Game (Black)", "Undo Move", "Return" };
1833uint8_t mnu_pos = 0;
1834uint8_t mnu_max = 4;
1835
1836void mnu_DrawHome(uint8_t is_highlight)
1837{
1838 uint8_t x = lrc_u8g->width - 35;
1839 uint8_t y = (lrc_u8g->height-1);
1840 uint8_t t;
1841
1842 u8g2_SetFont(lrc_u8g, u8g2_font_5x7_tr);
1843 u8g2_SetDefaultForegroundColor(lrc_u8g);
1844 if ( chess_state == CHESS_STATE_THINKING )
1845 u8g2_DrawBitmap(lrc_u8g, x, y-8, 1, 8, chess_black_pieces_bm+24);
1846 else
1847 {
1848 t = u8g2_DrawStrP(lrc_u8g, x, y -1, CHESS_PSTR("Options"));
1849
1850 if ( is_highlight )
1851 u8g2_DrawFrame(lrc_u8g, x-1, y - MNU_ENTRY_HEIGHT +1, t, MNU_ENTRY_HEIGHT);
1852 }
1853}
1854
1855void mnu_DrawEntry(uint8_t y, const char *str, uint8_t is_clr_background, uint8_t is_highlight)
1856{
1857 uint8_t t, x;
1858 u8g2_SetFont(lrc_u8g, MNU_FONT);
1859 t = u8g2_GetStrWidth(lrc_u8g, str);
1860 x = u8g2_GetDisplayWidth(lrc_u8g);
1861 x -= t;
1862 x >>= 1;
1863
1864 if ( is_clr_background )
1865 {
1866 u8g2_SetDefaultBackgroundColor(lrc_u8g);
1867 u8g2_DrawBox(lrc_u8g, x-3, (lrc_u8g->height-1) - (y+MNU_ENTRY_HEIGHT-1+2), t+5, MNU_ENTRY_HEIGHT+4);
1868 }
1869
1870 u8g2_SetDefaultForegroundColor(lrc_u8g);
1871 u8g2_DrawStr(lrc_u8g, x, (lrc_u8g->height-1) - y, str);
1872
1873 if ( is_highlight )
1874 {
1875 u8g2_DrawFrame(lrc_u8g, 0, (lrc_u8g->height-1) - y -MNU_ENTRY_HEIGHT +1, u8g2_GetDisplayWidth(lrc_u8g), MNU_ENTRY_HEIGHT);
1876 }
1877}
1878
1879void mnu_Draw(void)
1880{
1881 uint8_t i;
1882 uint8_t t,y;
1883 /* calculate hight of the complete menu */
1884 y = mnu_max;
1885 y++; /* consider also some space for the title */
1886 y++; /* consider also some space for the title */
1887 y *= MNU_ENTRY_HEIGHT;
1888
1889 /* calculate how much space will be left */
1890 t = u8g2_GetDisplayHeight(lrc_u8g);
1891 t -= y;
1892
1893 /* topmost pos start half of that empty space from the top */
1894 t >>= 1;
1895 y = u8g2_GetDisplayHeight(lrc_u8g);
1896 y -= t;
1897
1898 y -= MNU_ENTRY_HEIGHT;
1899 mnu_DrawEntry(y, mnu_title, 0, 0);
1900
1901 y -= MNU_ENTRY_HEIGHT;
1902
1903
1904 u8g2_DrawBitmap(lrc_u8g, 0, 1, 1, 8, chess_black_pieces_bm+24);
1905 u8g2_DrawBitmap(lrc_u8g, 128-8, 1, 1, 8, chess_black_pieces_bm+24);
1906
1907
1908 for( i = 0; i < mnu_max; i++ )
1909 {
1910 y -= MNU_ENTRY_HEIGHT;
1911 mnu_DrawEntry(y, mnu_list[i], 0, i == mnu_pos);
1912 }
1913}
1914
1915void mnu_Step(uint8_t key_cmd)
1916{
1917 if ( key_cmd == CHESS_KEY_NEXT )
1918 {
1919 if ( mnu_pos+1 < mnu_max )
1920 mnu_pos++;
1921 }
1922 else if ( key_cmd == CHESS_KEY_PREV )
1923 {
1924 if ( mnu_pos > 0 )
1925 mnu_pos--;
1926 }
1927}
1928
1929
1930
1931
1932
1933
1934
1935const uint8_t chess_pieces_body_bm[] CHESS_PROGMEM =
1936{
1937 /* PAWN */ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, /* 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x00, */
1938 /* KNIGHT */ 0x00, 0x00, 0x1c, 0x2c, 0x04, 0x04, 0x0e, 0x00,
1939 /* BISHOP */ 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x08, 0x00, 0x00, /* 0x00, 0x00, 0x08, 0x1c, 0x1c, 0x08, 0x00, 0x00, */
1940 /* ROOK */ 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x1c, 0x00,
1941 /* QUEEN */ 0x00, 0x00, 0x14, 0x1c, 0x08, 0x1c, 0x08, 0x00,
1942 /* KING */ 0x00, 0x00, 0x00, 0x08, 0x3e, 0x1c, 0x08, 0x00,
1943};
1944
1945#ifdef NOT_REQUIRED
1946/* white pieces are constructed by painting black pieces and cutting out the white area */
1947const uint8_t chess_white_pieces_bm[] CHESS_PROGMEM =
1948{
1949 /* PAWN */ 0x00, 0x00, 0x0c, 0x12, 0x12, 0x0c, 0x1e, 0x00,
1950 /* KNIGHT */ 0x00, 0x1c, 0x22, 0x52, 0x6a, 0x0a, 0x11, 0x1f,
1951 /* BISHOP */ 0x00, 0x08, 0x14, 0x22, 0x22, 0x14, 0x08, 0x7f,
1952 /* ROOK */ 0x00, 0x55, 0x7f, 0x22, 0x22, 0x22, 0x22, 0x7f,
1953 /* QUEEN */ 0x00, 0x55, 0x2a, 0x22, 0x14, 0x22, 0x14, 0x7f,
1954 /* KING */ 0x08, 0x1c, 0x49, 0x77, 0x41, 0x22, 0x14, 0x7f,
1955};
1956#endif
1957
1958const uint8_t chess_black_pieces_bm[] CHESS_PROGMEM =
1959{
1960 /* PAWN */ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x3c, 0x00, /* 0x00, 0x00, 0x0c, 0x1e, 0x1e, 0x0c, 0x1e, 0x00, */
1961 /* KNIGHT */ 0x00, 0x1c, 0x3e, 0x7e, 0x6e, 0x0e, 0x1f, 0x1f,
1962 /* BISHOP */ 0x00, 0x1c, 0x2e, 0x3e, 0x3e, 0x1c, 0x08, 0x7f, /*0x00, 0x08, 0x1c, 0x3e, 0x3e, 0x1c, 0x08, 0x7f,*/
1963 /* ROOK */ 0x00, 0x55, 0x7f, 0x3e, 0x3e, 0x3e, 0x3e, 0x7f,
1964 /* QUEEN */ 0x00, 0x55, 0x3e, 0x3e, 0x1c, 0x3e, 0x1c, 0x7f,
1965 /* KING -*/ 0x08, 0x1c, 0x49, 0x7f, 0x7f, 0x3e, 0x1c, 0x7f,
1966};
1967
1968
1969#if defined(DOGXL160_HW_GR)
1970#define BOXSIZE 13
1971#define BOXOFFSET 3
1972#else
1973#define BOXSIZE 8
1974#define BOXOFFSET 1
1975#endif
1976
1977u8g2_uint_t chess_low_edge;
1978uint8_t chess_boxsize = 8;
1979uint8_t chess_boxoffset = 1;
1980
1981
1982void chess_DrawFrame(uint8_t pos, uint8_t is_bold)
1983{
1984 u8g2_uint_t x0, y0;
1985
1986 x0 = pos;
1987 x0 &= 15;
1988 if ( lrc_obj.orientation != COLOR_WHITE )
1989 x0 ^= 7;
1990
1991 y0 = pos;
1992 y0>>= 4;
1993 if ( lrc_obj.orientation != COLOR_WHITE )
1994 y0 ^= 7;
1995
1996 x0 *= chess_boxsize;
1997 y0 *= chess_boxsize;
1998
1999 u8g2_SetDefaultForegroundColor(lrc_u8g);
2000 u8g2_DrawFrame(lrc_u8g, x0, chess_low_edge - y0 - chess_boxsize+1, chess_boxsize, chess_boxsize);
2001
2002
2003 if ( is_bold )
2004 {
2005 x0--;
2006 y0++;
2007
2008 u8g2_DrawFrame(lrc_u8g, x0, chess_low_edge - y0 - chess_boxsize +1, chess_boxsize+2, chess_boxsize+2);
2009 }
2010}
2011
2012
2013void chess_DrawBoard(void)
2014{
2015 uint8_t i, j, cp;
2016 const uint8_t *ptr; /* pointer into CHESS_PROGMEM */
2017
2018 {
2019 uint8_t x_offset = 1;
2020 u8g2_SetDefaultForegroundColor(lrc_u8g);
2021 for( i = 0; i < 8*8; i+=8 )
2022 {
2023 for( j = 0; j < 8*8; j+=8 )
2024 {
2025 if ( ((i^j) & 8) == 0 )
2026 {
2027 if ( lrc_obj.orientation == COLOR_WHITE )
2028 {
2029 cp = lrc_obj.board[i+j/8];
2030 }
2031 else
2032 {
2033 cp = lrc_obj.board[(7-i/8)*8+7-j/8];
2034 }
2035 if ( cp_GetPiece(cp) == PIECE_NONE )
2036 {
2037 u8g2_DrawPixel(lrc_u8g, j+0+x_offset, chess_low_edge - i-0);
2038 u8g2_DrawPixel(lrc_u8g, j+0+x_offset, chess_low_edge - i-2);
2039 u8g2_DrawPixel(lrc_u8g, j+0+x_offset, chess_low_edge - i-4);
2040 u8g2_DrawPixel(lrc_u8g, j+0+x_offset, chess_low_edge - i-6);
2041 u8g2_DrawPixel(lrc_u8g, j+2+x_offset, chess_low_edge - i-0);
2042 u8g2_DrawPixel(lrc_u8g, j+2+x_offset, chess_low_edge - i-6);
2043 u8g2_DrawPixel(lrc_u8g, j+4+x_offset, chess_low_edge - i-0);
2044 u8g2_DrawPixel(lrc_u8g, j+4+x_offset, chess_low_edge - i-6);
2045 u8g2_DrawPixel(lrc_u8g, j+6+x_offset, chess_low_edge - i-0);
2046 u8g2_DrawPixel(lrc_u8g, j+6+x_offset, chess_low_edge - i-2);
2047 u8g2_DrawPixel(lrc_u8g, j+6+x_offset, chess_low_edge - i-4);
2048 u8g2_DrawPixel(lrc_u8g, j+6+x_offset, chess_low_edge - i-6);
2049 }
2050 }
2051 }
2052 }
2053 }
2054
2055 for ( i = 0; i < 8; i++ )
2056 {
2057 for ( j = 0; j < 8; j++ )
2058 {
2059 /* get piece from global board */
2060 if ( lrc_obj.orientation == COLOR_WHITE )
2061 {
2062 cp = lrc_obj.board[i*8+j];
2063 }
2064 else
2065 {
2066 cp = lrc_obj.board[(7-i)*8+7-j];
2067 }
2068 if ( cp_GetPiece(cp) != PIECE_NONE )
2069 {
2070 ptr = chess_black_pieces_bm;
2071 ptr += (cp_GetPiece(cp)-1)*8;
2072
2073 u8g2_SetDefaultForegroundColor(lrc_u8g);
2074 u8g2_DrawBitmap(lrc_u8g, j*chess_boxsize+chess_boxoffset-1, chess_low_edge - (i*chess_boxsize+chess_boxsize-chess_boxoffset), 1, 8, ptr);
2075
2076 if ( cp_GetColor(cp) == lrc_obj.strike_out_color )
2077 {
2078 ptr = chess_pieces_body_bm;
2079 ptr += (cp_GetPiece(cp)-1)*8;
2080 u8g2_SetDefaultBackgroundColor(lrc_u8g);
2081 u8g2_DrawBitmap(lrc_u8g, j*chess_boxsize+chess_boxoffset-1, chess_low_edge - (i*chess_boxsize+chess_boxsize-chess_boxoffset), 1, 8, ptr);
2082 }
2083 }
2084 }
2085 }
2086 u8g2_SetDefaultForegroundColor(lrc_u8g);
2087
2088 if ( (chess_source_pos & 0x88) == 0 )
2089 {
2090 chess_DrawFrame(chess_source_pos, 1);
2091 }
2092
2093 if ( (chess_target_pos & 0x88) == 0 )
2094 {
2095 chess_DrawFrame(chess_target_pos, 0);
2096 }
2097
2098}
2099
2100
2101void chess_Thinking(void)
2102{
2103}
2104
2105void chess_Init(u8g2_t *u8g, uint8_t body_color)
2106{
2107 lrc_u8g = u8g;
2108
2109 chess_low_edge = u8g2_GetDisplayHeight(lrc_u8g);
2110 chess_low_edge--;
2111 u8g2_SetBitmapMode(u8g, 1); // restore previous behaviour
2112
2113 {
2114
2115 chess_boxsize = 8;
2116 chess_boxoffset = 1;
2117 }
2118
2119
2120
2121 lrc_obj.strike_out_color = body_color;
2122 chess_SetupBoard();
2123}
2124
2125
2126
2127void chess_Draw(void)
2128{
2129 if ( chess_state == CHESS_STATE_MENU )
2130 {
2131 if ( lrc_obj.ply_count == 0)
2132 mnu_max = 2;
2133 else
2134 mnu_max = 4;
2135 mnu_Draw();
2136 }
2137 else
2138 {
2139 chess_DrawBoard();
2140
2141 {
2142 uint8_t i;
2143 uint8_t entries = lrc_obj.chm_pos;
2144 if ( entries > 4 )
2145 entries = 4;
2146
2147 u8g2_SetFont(lrc_u8g, u8g2_font_5x7_tr);
2148 u8g2_SetDefaultForegroundColor(lrc_u8g);
2149 for( i = 0; i < entries; i++ )
2150 {
2151
2152 u8g2_DrawStr(lrc_u8g, u8g2_GetDisplayWidth(lrc_u8g)-35, 8*(i+1), cu_GetHalfMoveStr(lrc_obj.chm_pos-entries+i));
2153
2154 }
2155
2156 }
2157
2158 if ( chess_state == CHESS_STATE_SELECT_PIECE )
2159 mnu_DrawHome(chess_source_pos == 255);
2160 else if ( chess_state == CHESS_STATE_SELECT_TARGET_POS )
2161 mnu_DrawHome(chess_target_pos == 255);
2162 else
2163 mnu_DrawHome(0);
2164
2165 if ( chess_state == CHESS_STATE_GAME_END )
2166 {
2167 switch( lrc_obj.lost_side_color )
2168 {
2169 case COLOR_WHITE:
2170 mnu_DrawEntry(u8g2_GetDisplayHeight(lrc_u8g) / 2-2, "Black wins", 1, 1);
2171 break;
2172 case COLOR_BLACK:
2173 mnu_DrawEntry(u8g2_GetDisplayHeight(lrc_u8g) / 2-2, "White wins", 1, 1);
2174 break;
2175 default:
2176 mnu_DrawEntry(u8g2_GetDisplayHeight(lrc_u8g) / 2-2, "Stalemate", 1, 1);
2177 break;
2178 }
2179 }
2180 }
2181}
2182
2183
2184void chess_Step(uint8_t keycode)
2185{
2186 chess_key_cmd = keycode;
2187
2188 /*
2189 if ( keycode == CHESS_KEY_NONE )
2190 {
2191 chess_key_cmd = chess_key_code;
2192 chess_key_code = CHESS_KEY_NONE;
2193 }
2194 else
2195 {
2196 chess_key_cmd = CHESS_KEY_NONE;
2197 chess_key_code = keycode;
2198 }
2199 */
2200 //chess_ComputerMove(2);
2201 switch(chess_state)
2202 {
2203 case CHESS_STATE_MENU:
2204 mnu_Step(chess_key_cmd);
2205 if ( chess_key_cmd == CHESS_KEY_SELECT )
2206 {
2207 if ( mnu_pos == 0 )
2208 {
2209 chess_SetupBoard();
2210 lrc_obj.orientation = 0;
2211 chess_state = CHESS_STATE_SELECT_START;
2212 }
2213 else if ( mnu_pos == 1 )
2214 {
2215 chess_SetupBoard();
2216 lrc_obj.orientation = 1;
2217 chess_state = CHESS_STATE_THINKING;
2218 }
2219 else if ( mnu_pos == 2 )
2220 {
2221 if ( lrc_obj.ply_count >= 2 )
2222 {
2223 cu_UndoHalfMove();
2224 cu_UndoHalfMove();
2225 lrc_obj.ply_count-=2;
2226 if ( lrc_obj.ply_count == 0 )
2227 mnu_pos = 0;
2228 }
2229 chess_state = CHESS_STATE_SELECT_START;
2230 }
2231 else if ( mnu_pos == 3 )
2232 {
2233 chess_state = CHESS_STATE_SELECT_START;
2234 }
2235 }
2236 break;
2237 case CHESS_STATE_SELECT_START:
2238 chess_ClearMarks();
2239 chess_MarkMovable();
2240 chess_source_pos = chess_GetNextMarked(255, 0);
2241 chess_target_pos = ILLEGAL_POSITION;
2242 chess_state = CHESS_STATE_SELECT_PIECE;
2243 break;
2244
2245 case CHESS_STATE_SELECT_PIECE:
2246 if ( chess_key_cmd == CHESS_KEY_NEXT )
2247 {
2248 chess_source_pos = chess_GetNextMarked(chess_source_pos, lrc_obj.orientation);
2249 }
2250 else if ( chess_key_cmd == CHESS_KEY_PREV )
2251 {
2252 chess_source_pos = chess_GetNextMarked(chess_source_pos, 1-lrc_obj.orientation );
2253 }
2254 else if ( chess_key_cmd == CHESS_KEY_SELECT )
2255 {
2256 if ( chess_source_pos == 255 )
2257 {
2258 chess_state = CHESS_STATE_MENU;
2259 }
2260 else
2261 {
2262 chess_ClearMarks();
2263 chess_MarkTargetMoves(chess_source_pos);
2264 chess_target_pos = chess_GetNextMarked(255, 0);
2265 chess_state = CHESS_STATE_SELECT_TARGET_POS;
2266 }
2267 }
2268 break;
2269 case CHESS_STATE_SELECT_TARGET_POS:
2270 if ( chess_key_cmd == CHESS_KEY_NEXT )
2271 {
2272 chess_target_pos = chess_GetNextMarked(chess_target_pos, lrc_obj.orientation);
2273 if ( chess_target_pos == 255 )
2274 chess_target_pos = chess_GetNextMarked(chess_target_pos, lrc_obj.orientation);
2275 }
2276 else if ( chess_key_cmd == CHESS_KEY_PREV )
2277 {
2278 chess_target_pos = chess_GetNextMarked(chess_target_pos, 1-lrc_obj.orientation);
2279 if ( chess_target_pos == 255 )
2280 chess_target_pos = chess_GetNextMarked(chess_target_pos, 1-lrc_obj.orientation);
2281 }
2282 else if ( chess_key_cmd == CHESS_KEY_BACK )
2283 {
2284 chess_ClearMarks();
2285 chess_MarkMovable();
2286 chess_target_pos = ILLEGAL_POSITION;
2287 chess_state = CHESS_STATE_SELECT_PIECE;
2288 }
2289 else if ( chess_key_cmd == CHESS_KEY_SELECT )
2290 {
2291 chess_ManualMove(chess_source_pos, chess_target_pos);
2292 if ( lrc_obj.is_game_end != 0 )
2293 chess_state = CHESS_STATE_GAME_END;
2294 else
2295 chess_state = CHESS_STATE_THINKING;
2296 /* clear marks as some kind of feedback to the user... it simply looks better */
2297 chess_source_pos = ILLEGAL_POSITION;
2298 chess_target_pos = ILLEGAL_POSITION;
2299 chess_ClearMarks();
2300 }
2301 break;
2302 case CHESS_STATE_THINKING:
2303 chess_ComputerMove(2);
2304 if ( lrc_obj.is_game_end != 0 )
2305 chess_state = CHESS_STATE_GAME_END;
2306 else
2307 chess_state = CHESS_STATE_SELECT_START;
2308 //chess_state = CHESS_STATE_THINKING
2309 break;
2310 case CHESS_STATE_GAME_END:
2311 if ( chess_key_cmd != CHESS_KEY_NONE )
2312 {
2313 chess_state = CHESS_STATE_MENU;
2314 chess_SetupBoard();
2315 }
2316 break;
2317 }
2318
2319}
2320
2321#endif /* UNIX_MAIN */
2322
2323void loop(void) {
2324 static uint8_t keycode = 0;
2325
2326 u8g2.firstPage();
2327 do {
2328 chess_Draw();
2329 if ( keycode == 0 )
2330 keycode = u8g2.getMenuEvent();
2331 } while ( u8g2.nextPage() );
2332
2333 if ( keycode == 0 )
2334 keycode = u8g2.getMenuEvent();
2335
2336 if ( keycode == U8X8_MSG_GPIO_MENU_DOWN )
2337 keycode = CHESS_KEY_NEXT;
2338 if ( keycode == U8X8_MSG_GPIO_MENU_UP )
2339 keycode = CHESS_KEY_PREV;
2340 //if ( keycode == U8X8_MSG_GPIO_MENU_HOME )
2341 // keycode = CHESS_KEY_SELECT;
2342
2343 chess_Step(keycode);
2344 keycode = 0;
2345 delay(1);
2346}
2347