mirror of
https://github.com/Valeh2012/PersonalVotingMachine
synced 2024-11-26 10:51:00 +02:00
2370 lines
63 KiB
C
2370 lines
63 KiB
C
|
||
/*
|
||
|
||
LittleRookChess.ino
|
||
|
||
A Simple Chess Engine (ported from U8glib)
|
||
|
||
Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)
|
||
|
||
Copyright (c) 2016, olikraus@gmail.com
|
||
All rights reserved.
|
||
|
||
Redistribution and use in source and binary forms, with or without modification,
|
||
are permitted provided that the following conditions are met:
|
||
|
||
* Redistributions of source code must retain the above copyright notice, this list
|
||
of conditions and the following disclaimer.
|
||
|
||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||
list of conditions and the following disclaimer in the documentation and/or other
|
||
materials provided with the distribution.
|
||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
||
Current Rule Limitation
|
||
- no minor promotion, only "Queening" of the pawn
|
||
- threefold repetition is not detected (same board situation appears three times)
|
||
Note: Could be implemented, but requires tracking of the complete game
|
||
- Fifty-move rule is not checked (no pawn move, no capture within last 50 moves)
|
||
|
||
Words
|
||
Ply a half move
|
||
|
||
General Links
|
||
http://chessprogramming.wikispaces.com/
|
||
|
||
Arduino specific
|
||
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1260055596
|
||
|
||
Prefixes
|
||
chess_ Generic Chess Application Interface
|
||
ce_ Chess engine, used internally, these function should not be called directly
|
||
cu_ Chess utility function
|
||
stack_ Internal function for stack handling
|
||
|
||
Issues
|
||
10.01.2011
|
||
- castling to the right does not move the rook
|
||
--> done
|
||
- castling to the left: King can only move two squares
|
||
--> done
|
||
|
||
11.01.2011
|
||
Next Steps:
|
||
- replace stack_NextCurrentPos with cu_NextPos, cleanup code according to the loop variable
|
||
--> done
|
||
- Castling: Need to check for fields under attack
|
||
--> done
|
||
|
||
- Check for WIN / LOOSE situation, perhaps call ce_Eval() once on the top-level board setup
|
||
just after the real move
|
||
- cleanup cu_Move
|
||
--> almost done
|
||
- add some heuristics to the eval procedure
|
||
- add right side menu
|
||
--> done
|
||
- clean up chess_ManualMove
|
||
--> done
|
||
- finish menu (consider is_game_end, undo move)
|
||
- end condition: if KING is under attack and if KING can not move to a field which is under attack...
|
||
then the game is lost. What will be returned by the Eval procedure? is it -INF?
|
||
--> finished
|
||
|
||
- reduce the use of variable color, all should be reduced to board_orientation and ply&1
|
||
|
||
- chess_GetNextMarked shoud make use of cu_NextPos
|
||
--> done
|
||
- chess_ManualMove: again cleanup, solve draw issue (KING is not in check and no legal moves are available)
|
||
--> done
|
||
22.01.2011
|
||
- simplify eval_t ce_Eval(void)
|
||
- position eval does not work, still moves side pawn :-(
|
||
maybe because all pieces are considered
|
||
--> done
|
||
|
||
17. June 2016
|
||
U8g2/Arduboy Port
|
||
*/
|
||
|
||
#include "u8g2.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
|
||
|
||
/*
|
||
U8glib Example Overview:
|
||
Frame Buffer Examples: clearBuffer/sendBuffer. Fast, but may not work with all Arduino boards because of RAM consumption
|
||
Page Buffer Examples: firstPage/nextPage. Less RAM usage, should work with all Arduino boards.
|
||
U8x8 Text Only Example: No RAM usage, direct communication with display controller. No graphics, 8x8 Text only.
|
||
|
||
This is a page buffer example.
|
||
*/
|
||
|
||
|
||
|
||
void chess_Init(u8g2_t *u8g, uint8_t body_color);
|
||
|
||
|
||
/* Ignore PROGMEM for now */
|
||
#define CHESS_PROGMEM
|
||
#define chess_pgm_read(p) (*(p))
|
||
#define CHESS_PSTR(s) (s)
|
||
#define u8g2_DrawStrP u8g2_DrawStr
|
||
|
||
/* backward compatibility */
|
||
#define u8g2_SetDefaultBackgroundColor(u8g2) \
|
||
u8g2_SetDrawColor(u8g2, 0)
|
||
#define u8g2_SetDefaultForegroundColor(u8g2) \
|
||
u8g2_SetDrawColor(u8g2, 1)
|
||
|
||
|
||
/* menu button definitions: mapped to the u8g2 menu events */
|
||
|
||
#define CHESS_KEY_NONE 0
|
||
#define CHESS_KEY_NEXT U8X8_MSG_GPIO_MENU_NEXT
|
||
#define CHESS_KEY_PREV U8X8_MSG_GPIO_MENU_PREV
|
||
#define CHESS_KEY_SELECT U8X8_MSG_GPIO_MENU_SELECT
|
||
#define CHESS_KEY_BACK U8X8_MSG_GPIO_MENU_HOME
|
||
|
||
|
||
/*
|
||
SAN identifies each piece by a single upper case letter. The standard English
|
||
values: pawn = "P", knight = "N", bishop = "B", rook = "R", queen = "Q", and
|
||
king = "K".
|
||
*/
|
||
|
||
/* numbers for the various pieces */
|
||
#define PIECE_NONE 0
|
||
#define PIECE_PAWN 1
|
||
#define PIECE_KNIGHT 2
|
||
#define PIECE_BISHOP 3
|
||
#define PIECE_ROOK 4
|
||
#define PIECE_QUEEN 5
|
||
#define PIECE_KING 6
|
||
|
||
/* color definitions */
|
||
#define COLOR_WHITE 0
|
||
#define COLOR_BLACK 1
|
||
|
||
/* a mask, which includes COLOR and PIECE number */
|
||
#define COLOR_PIECE_MASK 0x01f
|
||
|
||
#define CP_MARK_MASK 0x20
|
||
|
||
#define ILLEGAL_POSITION 255
|
||
|
||
/* This is the build in upper limit of the search stack */
|
||
/* This value defines the amount of memory allocated for the search stack */
|
||
/* The search depth of this chess engine can never exceed this value */
|
||
#define STACK_MAX_SIZE 5
|
||
|
||
/* chess half move stack: twice the number of undo's, a user can do */
|
||
#define CHM_USER_SIZE 6
|
||
|
||
/* the CHM_LIST_SIZE must be larger than the maximum search depth */
|
||
/* the overall size of ste half move stack */
|
||
#define CHM_LIST_SIZE (STACK_MAX_SIZE+CHM_USER_SIZE+2)
|
||
|
||
typedef int16_t eval_t; /* a variable type to store results from the evaluation */
|
||
//#define EVAL_T_LOST -32768
|
||
#define EVAL_T_MIN -32767
|
||
#define EVAL_T_MAX 32767
|
||
//#define EVAL_T_WIN 32767
|
||
|
||
/* for maintainance of our own stack: this is the definition of one element on the stack */
|
||
struct _stack_element_struct
|
||
{
|
||
/* the current source position which is investigated */
|
||
uint8_t current_pos;
|
||
uint8_t current_cp;
|
||
uint8_t current_color; /* COLOR_WHITE or COLOR_BLACK: must be predefines */
|
||
|
||
/* the move which belongs to that value, both values are game positions */
|
||
uint8_t best_from_pos;
|
||
uint8_t best_to_pos;
|
||
/* the best value, which has been dicovered so far */
|
||
eval_t best_eval;
|
||
};
|
||
typedef struct _stack_element_struct stack_element_t;
|
||
typedef struct _stack_element_struct *stack_element_p;
|
||
|
||
/* chess half move history */
|
||
struct _chm_struct
|
||
{
|
||
uint8_t main_cp; /* the main piece, which is moved */
|
||
uint8_t main_src; /* the source position of the main piece */
|
||
uint8_t main_dest; /* the destination of the main piece */
|
||
|
||
uint8_t other_cp; /* another piece: the captured one, the ROOK in case of castling or PIECE_NONE */
|
||
uint8_t other_src; /* the delete position of other_cp. Often identical to main_dest except for e.p. and castling */
|
||
uint8_t other_dest; /* only used for castling: ROOK destination pos */
|
||
|
||
/* the position of the last pawn, which did a double move forward */
|
||
/* this is required to check en passant conditions */
|
||
/* this array can be indexed by the color of the current player */
|
||
/* this is the condition BEFORE the move was done */
|
||
uint8_t pawn_dbl_move[2];
|
||
|
||
/* flags for the movement of rook and king; required for castling */
|
||
/* a 1 means: castling is (still) possible */
|
||
/* a 0 means: castling not possible */
|
||
/* bit 0 left side white */
|
||
/* bit 1 right side white */
|
||
/* bit 2 left side black */
|
||
/* bit 3 right side black */
|
||
/* this is the condition BEFORE the move was done */
|
||
uint8_t castling_possible;
|
||
};
|
||
|
||
typedef struct _chm_struct chm_t;
|
||
typedef struct _chm_struct *chm_p;
|
||
|
||
/* little rook chess, main structure */
|
||
struct _lrc_struct
|
||
{
|
||
/* half-move (ply) counter: Counts the number of half-moves so far. Starts with 0 */
|
||
/* the lowest bit is used to derive the color of the current player */
|
||
/* will be set to zero in chess_SetupBoard() */
|
||
uint8_t ply_count;
|
||
|
||
/* the half move stack position counter, counts the number of elements in chm_list */
|
||
uint8_t chm_pos;
|
||
|
||
/* each element contains a colored piece, empty fields have value 0 */
|
||
/* the field with index 0 is black (lower left) */
|
||
uint8_t board[64];
|
||
/* the position of the last pawn, which did a double move forward */
|
||
/* this is required to check en passant conditions */
|
||
/* this array can be indexed by the color of the current player */
|
||
uint8_t pawn_dbl_move[2];
|
||
|
||
/* flags for the movement of rook and king; required for castling */
|
||
/* a 1 means: castling is (still) possible */
|
||
/* a 0 means: castling not possible */
|
||
/* bit 0 left side white */
|
||
/* bit 1 right side white */
|
||
/* bit 2 left side black */
|
||
/* bit 3 right side black */
|
||
uint8_t castling_possible;
|
||
|
||
/* board orientation */
|
||
/* 0: white is below COLOR_WHITE */
|
||
/* 1: black is below COLOR_BLACK */
|
||
/* bascially, this can be used as a color */
|
||
uint8_t orientation;
|
||
|
||
/* exchange colors of the pieces */
|
||
/* 0: white has an empty body, use this for bright background color */
|
||
/* 1: black has an empty body, use this for dark backround color */
|
||
uint8_t strike_out_color;
|
||
|
||
/* 0, when the game is ongoing */
|
||
/* 1, when the game is stopped (lost or draw) */
|
||
uint8_t is_game_end;
|
||
/* the color of the side which lost the game */
|
||
/* this value is only valid, when is_game_end is not 0 */
|
||
/* values 0 and 1 represent WHITE and BLACK, 2 means a draw */
|
||
uint8_t lost_side_color;
|
||
|
||
|
||
|
||
/* checks are executed in ce_LoopRecur */
|
||
/* these checks will put some marks on the board */
|
||
/* this will be used by the interface to find out */
|
||
/* legal moves */
|
||
uint8_t check_src_pos;
|
||
uint8_t check_mode; /* CHECK_MODE_NONE, CHECK_MODE_MOVEABLE, CHECK_MODE_TARGET_MOVE */
|
||
|
||
|
||
/* count of the attacking pieces, indexed by color */
|
||
uint8_t find_piece_cnt[2];
|
||
|
||
/* sum of the attacking pieces, indexed by color */
|
||
uint8_t find_piece_weight[2];
|
||
|
||
/* points to the current element of the search stack */
|
||
/* this stack is NEVER empty. The value 0 points to the first element of the stack */
|
||
/* actually "curr_depth" represent half-moves (plies) */
|
||
uint8_t curr_depth;
|
||
uint8_t max_depth;
|
||
stack_element_p curr_element;
|
||
|
||
/* allocated memory for the search stack */
|
||
stack_element_t stack_memory[STACK_MAX_SIZE];
|
||
|
||
/* the half move stack, used for move undo and depth search, size is stored in chm_pos */
|
||
chm_t chm_list[CHM_LIST_SIZE];
|
||
};
|
||
typedef struct _lrc_struct lrc_t;
|
||
|
||
#define CHECK_MODE_NONE 0
|
||
#define CHECK_MODE_MOVEABLE 1
|
||
#define CHECK_MODE_TARGET_MOVE 2
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
/* global variables */
|
||
/*==============================================================*/
|
||
|
||
lrc_t lrc_obj;
|
||
u8g2_t *lrc_u8g; /* pointer to the C object of u8g2 lib, this is used by the chess engine */
|
||
|
||
|
||
/*==============================================================*/
|
||
/* forward declarations */
|
||
/*==============================================================*/
|
||
|
||
/*
|
||
apply no inline to some of the functions:
|
||
avr-gcc very often inlines functions, however not inline saves a lot of program memory!
|
||
On the other hand there are some really short procedures which should be inlined (like cp_GetColor)
|
||
These procedures are marked static to prevent the generation of the expanded procedure, which
|
||
also saves space.
|
||
*/
|
||
|
||
uint8_t stack_Push(uint8_t color) U8G2_NOINLINE;
|
||
void stack_Pop(void) U8G2_NOINLINE;
|
||
void stack_InitCurrElement(void) U8G2_NOINLINE;
|
||
void stack_Init(uint8_t max) U8G2_NOINLINE;
|
||
void stack_SetMove(eval_t val, uint8_t to_pos) U8G2_NOINLINE;
|
||
uint8_t cu_NextPos(uint8_t pos) U8G2_NOINLINE;
|
||
static uint8_t cu_gpos2bpos(uint8_t gpos);
|
||
static uint8_t cp_Construct(uint8_t color, uint8_t piece);
|
||
static uint8_t cp_GetPiece(uint8_t cp);
|
||
static uint8_t cp_GetColor(uint8_t cp);
|
||
uint8_t cp_GetFromBoard(uint8_t pos) U8G2_NOINLINE;
|
||
void cp_SetOnBoard(uint8_t pos, uint8_t cp) U8G2_NOINLINE;
|
||
|
||
void cu_ClearBoard(void) U8G2_NOINLINE;
|
||
void chess_SetupBoard(void) U8G2_NOINLINE;
|
||
eval_t ce_Eval(void);
|
||
|
||
void cu_ClearMoveHistory(void) U8G2_NOINLINE;
|
||
void cu_ReduceHistoryByFullMove(void) U8G2_NOINLINE;
|
||
void cu_UndoHalfMove(void) U8G2_NOINLINE;
|
||
chm_p cu_PushHalfMove(void) U8G2_NOINLINE;
|
||
|
||
|
||
void ce_CalculatePositionWeight(uint8_t pos);
|
||
uint8_t ce_GetPositionAttackWeight(uint8_t pos, uint8_t color);
|
||
|
||
void chess_Thinking(void);
|
||
void ce_LoopPieces(void);
|
||
|
||
|
||
/*==============================================================*/
|
||
/* search stack */
|
||
/*==============================================================*/
|
||
|
||
/* get current element from stack */
|
||
struct _stack_element_struct *stack_GetCurrElement(void)
|
||
{
|
||
return lrc_obj.curr_element;
|
||
}
|
||
|
||
uint8_t stack_Push(uint8_t color)
|
||
{
|
||
if ( lrc_obj.curr_depth == lrc_obj.max_depth )
|
||
return 0;
|
||
lrc_obj.curr_depth++;
|
||
lrc_obj.curr_element = lrc_obj.stack_memory+lrc_obj.curr_depth;
|
||
|
||
/* change view for the evaluation */
|
||
color ^= 1;
|
||
stack_GetCurrElement()->current_color = color;
|
||
|
||
return 1;
|
||
}
|
||
|
||
void stack_Pop(void)
|
||
{
|
||
lrc_obj.curr_depth--;
|
||
lrc_obj.curr_element = lrc_obj.stack_memory+lrc_obj.curr_depth;
|
||
}
|
||
|
||
/* reset the current element on the stack */
|
||
void stack_InitCurrElement(void)
|
||
{
|
||
stack_element_p e = stack_GetCurrElement();
|
||
e->best_eval = EVAL_T_MIN;
|
||
e->best_from_pos = ILLEGAL_POSITION;
|
||
e->best_to_pos = ILLEGAL_POSITION;
|
||
}
|
||
|
||
/* resets the search stack (and the check mode) */
|
||
void stack_Init(uint8_t max)
|
||
{
|
||
lrc_obj.curr_depth = 0;
|
||
lrc_obj.curr_element = lrc_obj.stack_memory;
|
||
lrc_obj.max_depth = max;
|
||
lrc_obj.check_mode = CHECK_MODE_NONE;
|
||
stack_InitCurrElement();
|
||
stack_GetCurrElement()->current_color = lrc_obj.ply_count;
|
||
stack_GetCurrElement()->current_color &= 1;
|
||
}
|
||
|
||
/* assign evaluation value and store the move, if this is the best move */
|
||
/* assumes, that current_pos contains the source position */
|
||
void stack_SetMove(eval_t val, uint8_t to_pos)
|
||
{
|
||
stack_element_p e = stack_GetCurrElement();
|
||
if ( e->best_eval < val )
|
||
{
|
||
e->best_eval = val;
|
||
e->best_from_pos = e->current_pos;
|
||
e->best_to_pos = to_pos;
|
||
}
|
||
}
|
||
|
||
/*
|
||
calculate next position on a 0x88 board
|
||
loop is constructed in this way:
|
||
i = 0;
|
||
do
|
||
{
|
||
...
|
||
i = cu_NextPos(i);
|
||
} while( i != 0 );
|
||
|
||
next pos might be started with an illegal position like 255
|
||
*/
|
||
uint8_t cu_NextPos(uint8_t pos)
|
||
{
|
||
/* calculate next gpos */
|
||
pos++;
|
||
if ( ( pos & 0x08 ) != 0 )
|
||
{
|
||
pos+= 0x10;
|
||
pos&= 0xf0;
|
||
}
|
||
if ( ( pos & 0x80 ) != 0 )
|
||
pos = 0;
|
||
return pos;
|
||
}
|
||
|
||
uint8_t cu_PrevPos(uint8_t pos)
|
||
{
|
||
/* calculate prev gpos */
|
||
pos--;
|
||
if ( ( pos & 0x80 ) != 0 )
|
||
pos = 0x077;
|
||
else if ( ( pos & 0x08 ) != 0 )
|
||
{
|
||
pos &= 0xf0;
|
||
pos |= 0x07;
|
||
}
|
||
return pos;
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
/* position transltion */
|
||
/*==============================================================*/
|
||
/*
|
||
there are two positions
|
||
1. game position (gpos): BCD encoded x-y values
|
||
2. board position (bpos): a number between 0 and 63, only used to access the board.
|
||
*/
|
||
/*
|
||
gpos: game position value
|
||
returns: board position
|
||
note: does not do any checks
|
||
*/
|
||
static uint8_t cu_gpos2bpos(uint8_t gpos)
|
||
{
|
||
uint8_t bpos = gpos;
|
||
bpos &= 0xf0;
|
||
bpos >>= 1;
|
||
gpos &= 0x0f;
|
||
bpos |= gpos;
|
||
return bpos;
|
||
}
|
||
|
||
#define gpos_IsIllegal(gpos) ((gpos) & 0x088)
|
||
|
||
|
||
/*==============================================================*/
|
||
/* colored piece handling */
|
||
/*==============================================================*/
|
||
|
||
#define cp_IsMarked(cp) ((cp) & CP_MARK_MASK)
|
||
|
||
|
||
/*
|
||
piece: one of PIECE_xxx
|
||
color: COLOR_WHITE or COLOR_BLACK
|
||
|
||
returns: A colored piece
|
||
*/
|
||
static uint8_t cp_Construct(uint8_t color, uint8_t piece)
|
||
{
|
||
color <<= 4;
|
||
color |= piece;
|
||
return color;
|
||
}
|
||
|
||
/* inline is better than a macro */
|
||
static uint8_t cp_GetPiece(uint8_t cp)
|
||
{
|
||
cp &= 0x0f;
|
||
return cp;
|
||
}
|
||
|
||
/*
|
||
we could use a macro:
|
||
#define cp_GetColor(cp) (((cp) >> 4)&1)
|
||
however, inlined functions are sometimes much better
|
||
*/
|
||
static uint8_t cp_GetColor(uint8_t cp)
|
||
{
|
||
cp >>= 4;
|
||
cp &= 1;
|
||
return cp;
|
||
}
|
||
|
||
/*
|
||
pos: game position
|
||
returns the colored piece at the given position
|
||
*/
|
||
uint8_t cp_GetFromBoard(uint8_t pos)
|
||
{
|
||
return lrc_obj.board[cu_gpos2bpos(pos)];
|
||
}
|
||
|
||
/*
|
||
pos: game position
|
||
cp: colored piece
|
||
*/
|
||
void cp_SetOnBoard(uint8_t pos, uint8_t cp)
|
||
{
|
||
/*printf("cp_SetOnBoard gpos:%02x cp:%02x\n", pos, cp);*/
|
||
lrc_obj.board[cu_gpos2bpos(pos)] = cp;
|
||
}
|
||
|
||
/*==============================================================*/
|
||
/* global board access */
|
||
/*==============================================================*/
|
||
|
||
void cu_ClearBoard(void)
|
||
{
|
||
uint8_t i;
|
||
/* clear the board */
|
||
for( i = 0; i < 64; i++ )
|
||
lrc_obj.board[i] = PIECE_NONE;
|
||
|
||
lrc_obj.ply_count = 0;
|
||
lrc_obj.orientation = COLOR_WHITE;
|
||
|
||
lrc_obj.pawn_dbl_move[0] = ILLEGAL_POSITION;
|
||
lrc_obj.pawn_dbl_move[1] = ILLEGAL_POSITION;
|
||
|
||
lrc_obj.castling_possible = 0x0f;
|
||
|
||
lrc_obj.is_game_end = 0;
|
||
lrc_obj.lost_side_color = 0;
|
||
|
||
/* clear half move history */
|
||
cu_ClearMoveHistory();
|
||
|
||
}
|
||
|
||
/*
|
||
test setup
|
||
white wins in one move
|
||
*/
|
||
void chess_SetupBoardTest01(void)
|
||
{
|
||
cu_ClearBoard();
|
||
lrc_obj.board[7+7*8] = cp_Construct(COLOR_BLACK, PIECE_KING);
|
||
lrc_obj.board[7+5*8] = cp_Construct(COLOR_WHITE, PIECE_PAWN);
|
||
lrc_obj.board[3] = cp_Construct(COLOR_WHITE, PIECE_KING);
|
||
lrc_obj.board[0+7*8] = cp_Construct(COLOR_BLACK, PIECE_ROOK);
|
||
lrc_obj.board[6] = cp_Construct(COLOR_WHITE, PIECE_QUEEN);
|
||
}
|
||
|
||
/* setup the global board */
|
||
void chess_SetupBoard(void)
|
||
{
|
||
uint8_t i;
|
||
register uint8_t bp, wp;
|
||
|
||
/* clear the board */
|
||
cu_ClearBoard();
|
||
|
||
/* precronstruct pawns */
|
||
wp = cp_Construct(COLOR_WHITE, PIECE_PAWN);
|
||
bp = cp_Construct(COLOR_BLACK, PIECE_PAWN);
|
||
|
||
/* setup pawn */
|
||
for( i = 0; i < 8; i++ )
|
||
{
|
||
lrc_obj.board[i+8] = wp;
|
||
lrc_obj.board[i+6*8] = bp;
|
||
}
|
||
|
||
/* assign remaining pieces */
|
||
|
||
lrc_obj.board[0] = cp_Construct(COLOR_WHITE, PIECE_ROOK);
|
||
lrc_obj.board[1] = cp_Construct(COLOR_WHITE, PIECE_KNIGHT);
|
||
lrc_obj.board[2] = cp_Construct(COLOR_WHITE, PIECE_BISHOP);
|
||
lrc_obj.board[3] = cp_Construct(COLOR_WHITE, PIECE_QUEEN);
|
||
lrc_obj.board[4] = cp_Construct(COLOR_WHITE, PIECE_KING);
|
||
lrc_obj.board[5] = cp_Construct(COLOR_WHITE, PIECE_BISHOP);
|
||
lrc_obj.board[6] = cp_Construct(COLOR_WHITE, PIECE_KNIGHT);
|
||
lrc_obj.board[7] = cp_Construct(COLOR_WHITE, PIECE_ROOK);
|
||
|
||
lrc_obj.board[0+7*8] = cp_Construct(COLOR_BLACK, PIECE_ROOK);
|
||
lrc_obj.board[1+7*8] = cp_Construct(COLOR_BLACK, PIECE_KNIGHT);
|
||
lrc_obj.board[2+7*8] = cp_Construct(COLOR_BLACK, PIECE_BISHOP);
|
||
lrc_obj.board[3+7*8] = cp_Construct(COLOR_BLACK, PIECE_QUEEN);
|
||
lrc_obj.board[4+7*8] = cp_Construct(COLOR_BLACK, PIECE_KING);
|
||
lrc_obj.board[5+7*8] = cp_Construct(COLOR_BLACK, PIECE_BISHOP);
|
||
lrc_obj.board[6+7*8] = cp_Construct(COLOR_BLACK, PIECE_KNIGHT);
|
||
lrc_obj.board[7+7*8] = cp_Construct(COLOR_BLACK, PIECE_ROOK);
|
||
|
||
//chess_SetupBoardTest01();
|
||
|
||
}
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
/* checks */
|
||
/*==============================================================*/
|
||
|
||
/*
|
||
checks if the position is somehow illegal
|
||
*/
|
||
uint8_t cu_IsIllegalPosition(uint8_t pos, uint8_t my_color)
|
||
{
|
||
uint8_t board_cp;
|
||
/* check, if the position is offboard */
|
||
if ( gpos_IsIllegal(pos) != 0 )
|
||
return 1;
|
||
/* get the piece from the board */
|
||
board_cp = cp_GetFromBoard(pos);
|
||
/* check if hit our own pieces */
|
||
if ( board_cp != 0 )
|
||
if ( cp_GetColor(board_cp) == my_color )
|
||
return 1;
|
||
/* all ok, we could go to this position */
|
||
return 0;
|
||
}
|
||
|
||
/*==============================================================*/
|
||
/* evaluation procedure */
|
||
/*==============================================================*/
|
||
|
||
/*
|
||
basic idea is to return a value between EVAL_T_MIN and EVAL_T_MAX
|
||
*/
|
||
|
||
/*
|
||
the weight table uses the PIECE number as index:
|
||
#define PIECE_NONE 0
|
||
#define PIECE_PAWN 1
|
||
#define PIECE_KNIGHT 2
|
||
#define PIECE_BISHOP 3
|
||
#define PIECE_ROOK 4
|
||
#define PIECE_QUEEN 5
|
||
#define PIECE_KING 6
|
||
the king itself is not counted
|
||
*/
|
||
uint8_t ce_piece_weight[] = { 0, 1, 3, 3, 5, 9, 0 };
|
||
uint8_t ce_pos_weight[] = { 0, 1, 1, 2, 2, 1, 1, 0};
|
||
/*
|
||
evaluate the current situation on the global board
|
||
*/
|
||
eval_t ce_Eval(void)
|
||
{
|
||
uint8_t cp;
|
||
uint8_t is_my_king_present = 0;
|
||
uint8_t is_opposit_king_present = 0;
|
||
eval_t material_my_color = 0;
|
||
eval_t material_opposit_color = 0;
|
||
eval_t position_my_color = 0;
|
||
eval_t position_opposit_color = 0;
|
||
eval_t result;
|
||
uint8_t pos;
|
||
|
||
pos = 0;
|
||
do
|
||
{
|
||
/* get colored piece from the board */
|
||
cp = cp_GetFromBoard(pos);
|
||
|
||
if ( cp_GetPiece(cp) != PIECE_NONE )
|
||
{
|
||
if ( stack_GetCurrElement()->current_color == cp_GetColor(cp) )
|
||
{
|
||
/* this is our color */
|
||
/* check if we found our king */
|
||
if ( cp_GetPiece(cp) == PIECE_KING )
|
||
is_my_king_present = 1;
|
||
material_my_color += ce_piece_weight[cp_GetPiece(cp)];
|
||
if ( cp_GetPiece(cp) == PIECE_PAWN || cp_GetPiece(cp) == PIECE_KNIGHT )
|
||
{
|
||
position_my_color += ce_pos_weight[pos&7]*ce_pos_weight[(pos>>4)&7];
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* this is the opposit color */
|
||
if ( cp_GetPiece(cp) == PIECE_KING )
|
||
is_opposit_king_present = 1;
|
||
material_opposit_color += ce_piece_weight[cp_GetPiece(cp)];
|
||
if ( cp_GetPiece(cp) == PIECE_PAWN || cp_GetPiece(cp) == PIECE_KNIGHT )
|
||
{
|
||
position_opposit_color += ce_pos_weight[pos&7]*ce_pos_weight[(pos>>4)&7];
|
||
}
|
||
}
|
||
}
|
||
pos = cu_NextPos(pos);
|
||
} while( pos != 0 );
|
||
|
||
|
||
/* decide if we lost or won the game */
|
||
if ( is_my_king_present == 0 )
|
||
return EVAL_T_MIN; /*_LOST*/
|
||
if ( is_opposit_king_present == 0 )
|
||
return EVAL_T_MAX; /*_WIN*/
|
||
|
||
/* here is the evaluation function */
|
||
|
||
result = material_my_color - material_opposit_color;
|
||
result <<= 3;
|
||
result += position_my_color - position_opposit_color;
|
||
return result;
|
||
}
|
||
|
||
/*==============================================================*/
|
||
/* move backup and restore */
|
||
/*==============================================================*/
|
||
|
||
|
||
/* this procedure must be called to keep the size as low as possible */
|
||
/* if the chm_list is large enough, it could hold the complete history */
|
||
/* but for an embedded controler... it is deleted for every engine search */
|
||
void cu_ClearMoveHistory(void)
|
||
{
|
||
lrc_obj.chm_pos = 0;
|
||
}
|
||
|
||
void cu_ReduceHistoryByFullMove(void)
|
||
{
|
||
uint8_t i;
|
||
while( lrc_obj.chm_pos > CHM_USER_SIZE )
|
||
{
|
||
i = 0;
|
||
for(;;)
|
||
{
|
||
if ( i+2 >= lrc_obj.chm_pos )
|
||
break;
|
||
lrc_obj.chm_list[i] = lrc_obj.chm_list[i+2];
|
||
i++;
|
||
}
|
||
lrc_obj.chm_pos -= 2;
|
||
}
|
||
}
|
||
|
||
void cu_UndoHalfMove(void)
|
||
{
|
||
chm_p chm;
|
||
|
||
if ( lrc_obj.chm_pos == 0 )
|
||
return;
|
||
|
||
lrc_obj.chm_pos--;
|
||
|
||
chm = lrc_obj.chm_list+lrc_obj.chm_pos;
|
||
|
||
lrc_obj.pawn_dbl_move[0] = chm->pawn_dbl_move[0];
|
||
lrc_obj.pawn_dbl_move[1] = chm->pawn_dbl_move[1];
|
||
lrc_obj.castling_possible = chm->castling_possible;
|
||
|
||
cp_SetOnBoard(chm->main_src, chm->main_cp);
|
||
cp_SetOnBoard(chm->main_dest, PIECE_NONE);
|
||
|
||
if ( chm->other_src != ILLEGAL_POSITION )
|
||
cp_SetOnBoard(chm->other_src, chm->other_cp);
|
||
if ( chm->other_dest != ILLEGAL_POSITION )
|
||
cp_SetOnBoard(chm->other_dest, PIECE_NONE);
|
||
|
||
}
|
||
|
||
/*
|
||
assumes, that the following members of the returned chm structure are filled
|
||
uint8_t main_cp; the main piece, which is moved
|
||
uint8_t main_src; the source position of the main piece
|
||
uint8_t main_dest; the destination of the main piece
|
||
|
||
uint8_t other_cp; another piece: the captured one, the ROOK in case of castling or PIECE_NONE
|
||
uint8_t other_src; the delete position of other_cp. Often identical to main_dest except for e.p. and castling
|
||
uint8_t other_dest; only used for castling: ROOK destination pos
|
||
|
||
*/
|
||
chm_p cu_PushHalfMove(void)
|
||
{
|
||
chm_p chm;
|
||
|
||
chm = lrc_obj.chm_list+lrc_obj.chm_pos;
|
||
if ( lrc_obj.chm_pos < CHM_LIST_SIZE-1)
|
||
lrc_obj.chm_pos++;
|
||
|
||
chm->pawn_dbl_move[0] = lrc_obj.pawn_dbl_move[0];
|
||
chm->pawn_dbl_move[1] = lrc_obj.pawn_dbl_move[1];
|
||
chm->castling_possible = lrc_obj.castling_possible;
|
||
return chm;
|
||
}
|
||
|
||
|
||
char chess_piece_to_char[] = "NBRQK";
|
||
|
||
/*
|
||
simple moves on empty field: Ka1-b2
|
||
capture moves: Ka1xb2
|
||
castling: 0-0 or 0-0-0
|
||
*/
|
||
|
||
static void cu_add_pos(char *s, uint8_t pos) U8G2_NOINLINE;
|
||
|
||
static void cu_add_pos(char *s, uint8_t pos)
|
||
{
|
||
*s = pos;
|
||
*s >>= 4;
|
||
*s += 'a';
|
||
s++;
|
||
*s = pos;
|
||
*s &= 15;
|
||
*s += '1';
|
||
}
|
||
|
||
const char *cu_GetHalfMoveStr(uint8_t idx)
|
||
{
|
||
chm_p chm;
|
||
static char buf[7]; /*Ka1-b2*/
|
||
char *p = buf;
|
||
chm = lrc_obj.chm_list+idx;
|
||
|
||
if ( cp_GetPiece(chm->main_cp) != PIECE_NONE )
|
||
{
|
||
if ( cp_GetPiece(chm->main_cp) > PIECE_PAWN )
|
||
{
|
||
*p++ = chess_piece_to_char[cp_GetPiece(chm->main_cp)-2];
|
||
}
|
||
cu_add_pos(p, chm->main_src);
|
||
p+=2;
|
||
if ( cp_GetPiece(chm->other_cp) == PIECE_NONE )
|
||
*p++ = '-';
|
||
else
|
||
*p++ = 'x';
|
||
cu_add_pos(p, chm->main_dest);
|
||
p+=2;
|
||
}
|
||
*p = '\0';
|
||
return buf;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
/* move */
|
||
/*==============================================================*/
|
||
|
||
/*
|
||
Move a piece from source position to a destination on the board
|
||
This function
|
||
- does not perform any checking
|
||
- however it processes "en passant" and casteling
|
||
- backup the move and allow 1x undo
|
||
|
||
2011-02-05:
|
||
- fill pawn_dbl_move[] for double pawn moves
|
||
--> done
|
||
- Implement casteling
|
||
--> done
|
||
- en passant
|
||
--> done
|
||
- pawn conversion/promotion
|
||
--> done
|
||
- half-move backup
|
||
--> done
|
||
- cleanup everything, minimize variables
|
||
--> done
|
||
*/
|
||
|
||
void cu_Move(uint8_t src, uint8_t dest)
|
||
{
|
||
/* start backup structure */
|
||
chm_p chm = cu_PushHalfMove();
|
||
|
||
/* these are the values from the board at the positions, provided as arguments to this function */
|
||
uint8_t cp_src, cp_dest;
|
||
|
||
/* Maybe a second position is cleared and one additional location is set */
|
||
uint8_t clr_pos2;
|
||
uint8_t set_pos2;
|
||
uint8_t set_cp2;
|
||
|
||
/* get values from board */
|
||
cp_src = cp_GetFromBoard(src);
|
||
cp_dest = cp_GetFromBoard(dest);
|
||
|
||
/* fill backup structure */
|
||
|
||
chm->main_cp = cp_src;
|
||
chm->main_src = src;
|
||
chm->main_dest = dest;
|
||
|
||
chm->other_cp = cp_dest; /* prepace capture backup */
|
||
chm->other_src = dest;
|
||
chm->other_dest = ILLEGAL_POSITION;
|
||
|
||
/* setup results as far as possible with some suitable values */
|
||
|
||
clr_pos2 = ILLEGAL_POSITION; /* for en passant and castling, two positions might be cleared */
|
||
set_pos2 = ILLEGAL_POSITION; /* only used for castling */
|
||
set_cp2 = PIECE_NONE; /* ROOK for castling */
|
||
|
||
/* check for PAWN */
|
||
if ( cp_GetPiece(cp_src) == PIECE_PAWN )
|
||
{
|
||
|
||
/* double step: is the distance 2 rows */
|
||
if ( (src - dest == 32) || ( dest - src == 32 ) )
|
||
{
|
||
/* remember the destination position */
|
||
lrc_obj.pawn_dbl_move[cp_GetColor(cp_src)] = dest;
|
||
}
|
||
|
||
/* check if the PAWN is able to promote */
|
||
else if ( (dest>>4) == 0 || (dest>>4) == 7 )
|
||
{
|
||
/* do simple "queening" */
|
||
cp_src &= ~PIECE_PAWN;
|
||
cp_src |= PIECE_QUEEN;
|
||
}
|
||
|
||
/* is it en passant capture? */
|
||
/* check for side move */
|
||
else if ( ((src + dest) & 1) != 0 )
|
||
{
|
||
/* check, if target field is empty */
|
||
if ( cp_GetPiece(cp_dest) == PIECE_NONE )
|
||
{
|
||
/* this is en passant */
|
||
/* no further checking required, because legal moves are assumed here */
|
||
/* however... the captured pawn position must be valid */
|
||
clr_pos2 = lrc_obj.pawn_dbl_move[cp_GetColor(cp_src) ^ 1];
|
||
chm->other_src = clr_pos2;
|
||
chm->other_cp = cp_GetFromBoard(clr_pos2);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* check for the KING */
|
||
else if ( cp_GetPiece(cp_src) == PIECE_KING )
|
||
{
|
||
/* disallow castling, if the KING has moved */
|
||
if ( cp_GetColor(cp_src) == COLOR_WHITE )
|
||
{
|
||
/* if white KING has moved, disallow castling for white */
|
||
lrc_obj.castling_possible &= 0x0c;
|
||
}
|
||
else
|
||
{
|
||
/* if black KING has moved, disallow castling for black */
|
||
lrc_obj.castling_possible &= 0x03;
|
||
}
|
||
|
||
/* has it been castling to the left? */
|
||
if ( src - dest == 2 )
|
||
{
|
||
/* let the ROOK move to pos2 */
|
||
set_pos2 = src-1;
|
||
set_cp2 = cp_GetFromBoard(src-4);
|
||
|
||
/* the ROOK must be cleared from the original position */
|
||
clr_pos2 = src-4;
|
||
|
||
chm->other_cp = set_cp2;
|
||
chm->other_src = clr_pos2;
|
||
chm->other_dest = set_pos2;
|
||
}
|
||
|
||
/* has it been castling to the right? */
|
||
else if ( dest - src == 2 )
|
||
{
|
||
/* let the ROOK move to pos2 */
|
||
set_pos2 = src+1;
|
||
set_cp2 = cp_GetFromBoard(src+3);
|
||
|
||
/* the ROOK must be cleared from the original position */
|
||
clr_pos2 = src+3;
|
||
|
||
chm->other_cp = set_cp2;
|
||
chm->other_src = clr_pos2;
|
||
chm->other_dest = set_pos2;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
/* check for the ROOK */
|
||
else if ( cp_GetPiece(cp_src) == PIECE_ROOK )
|
||
{
|
||
/* disallow white left castling */
|
||
if ( src == 0x00 )
|
||
lrc_obj.castling_possible &= ~0x01;
|
||
/* disallow white right castling */
|
||
if ( src == 0x07 )
|
||
lrc_obj.castling_possible &= ~0x02;
|
||
/* disallow black left castling */
|
||
if ( src == 0x70 )
|
||
lrc_obj.castling_possible &= ~0x04;
|
||
/* disallow black right castling */
|
||
if ( src == 0x77 )
|
||
lrc_obj.castling_possible &= ~0x08;
|
||
}
|
||
|
||
|
||
/* apply new board situation */
|
||
|
||
cp_SetOnBoard(dest, cp_src);
|
||
|
||
if ( set_pos2 != ILLEGAL_POSITION )
|
||
cp_SetOnBoard(set_pos2, set_cp2);
|
||
|
||
cp_SetOnBoard(src, PIECE_NONE);
|
||
|
||
if ( clr_pos2 != ILLEGAL_POSITION )
|
||
cp_SetOnBoard(clr_pos2, PIECE_NONE);
|
||
|
||
|
||
}
|
||
|
||
/*
|
||
this subprocedure decides for evaluation of the current board situation or further (deeper) investigation
|
||
Argument pos is the new target position if the current piece
|
||
|
||
*/
|
||
uint8_t ce_LoopRecur(uint8_t pos)
|
||
{
|
||
eval_t eval;
|
||
|
||
/* 1. check if target position is occupied by the same player (my_color) */
|
||
/* of if pos is somehow illegal or not valid */
|
||
if ( cu_IsIllegalPosition(pos, stack_GetCurrElement()->current_color) != 0 )
|
||
return 0;
|
||
|
||
/* 2. move piece to the specified position, capture opponent piece if required */
|
||
cu_Move(stack_GetCurrElement()->current_pos, pos);
|
||
|
||
|
||
/* 3. */
|
||
/* if depth reached: evaluate */
|
||
/* else: go down next level */
|
||
/* no eval if there had been any valid half-moves, so the default value (MIN) will be returned. */
|
||
if ( stack_Push(stack_GetCurrElement()->current_color) == 0 )
|
||
{
|
||
eval = ce_Eval();
|
||
}
|
||
else
|
||
{
|
||
/* init the element, which has been pushed */
|
||
stack_InitCurrElement();
|
||
/* start over with ntext level */
|
||
ce_LoopPieces();
|
||
/* get the best move from opponents view, so invert the result */
|
||
eval = -stack_GetCurrElement()->best_eval;
|
||
stack_Pop();
|
||
}
|
||
|
||
/* 4. store result */
|
||
stack_SetMove(eval, pos);
|
||
|
||
/* 5. undo the move */
|
||
cu_UndoHalfMove();
|
||
|
||
/* 6. check special modes */
|
||
/* the purpose of these checks is to mark special pieces and positions on the board */
|
||
/* these marks can be checked by the user interface to highlight special positions */
|
||
if ( lrc_obj.check_mode != 0 )
|
||
{
|
||
stack_element_p e = stack_GetCurrElement();
|
||
if ( lrc_obj.check_mode == CHECK_MODE_MOVEABLE )
|
||
{
|
||
cp_SetOnBoard(e->current_pos, e->current_cp | CP_MARK_MASK );
|
||
}
|
||
else if ( lrc_obj.check_mode == CHECK_MODE_TARGET_MOVE )
|
||
{
|
||
if ( e->current_pos == lrc_obj.check_src_pos )
|
||
{
|
||
cp_SetOnBoard(pos, cp_GetFromBoard(pos) | CP_MARK_MASK );
|
||
}
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*==============================================================*/
|
||
/* move pieces which can move one or more steps into a direction */
|
||
/*==============================================================*/
|
||
|
||
/*
|
||
subprocedure to generate various target positions for some pieces
|
||
special cases are handled in the piece specific sub-procedure
|
||
|
||
Arguments:
|
||
d: a list of potential directions
|
||
is_multi_step: if the piece can only do one step (zero for KING and KNIGHT)
|
||
*/
|
||
static const uint8_t ce_dir_offset_rook[] CHESS_PROGMEM = { 1, 16, -16, -1, 0 };
|
||
static const uint8_t ce_dir_offset_bishop[] CHESS_PROGMEM = { 15, 17, -17, -15, 0 };
|
||
static const uint8_t ce_dir_offset_queen[] CHESS_PROGMEM = { 1, 16, -16, -1, 15, 17, -17, -15, 0 };
|
||
static const uint8_t ce_dir_offset_knight[] CHESS_PROGMEM = {14, -14, 18, -18, 31, -31, 33, -33, 0};
|
||
|
||
void ce_LoopDirsSingleMultiStep(const uint8_t *d, uint8_t is_multi_step)
|
||
{
|
||
uint8_t loop_pos;
|
||
|
||
/* with all directions */
|
||
for(;;)
|
||
{
|
||
if ( chess_pgm_read(d) == 0 )
|
||
break;
|
||
|
||
/* start again from the initial position */
|
||
loop_pos = stack_GetCurrElement()->current_pos;
|
||
|
||
/* check direction */
|
||
do
|
||
{
|
||
/* check next position into one direction */
|
||
loop_pos += chess_pgm_read(d);
|
||
|
||
/*
|
||
go further to ce_LoopRecur()
|
||
0 will be returned if the target position is illegal or a piece of the own color
|
||
this is used to stop walking into one direction
|
||
*/
|
||
if ( ce_LoopRecur(loop_pos) == 0 )
|
||
break;
|
||
|
||
/* stop if we had hit another piece */
|
||
if ( cp_GetPiece(cp_GetFromBoard(loop_pos)) != PIECE_NONE )
|
||
break;
|
||
} while( is_multi_step );
|
||
d++;
|
||
}
|
||
}
|
||
|
||
void ce_LoopRook(void)
|
||
{
|
||
ce_LoopDirsSingleMultiStep(ce_dir_offset_rook, 1);
|
||
}
|
||
|
||
void ce_LoopBishop(void)
|
||
{
|
||
ce_LoopDirsSingleMultiStep(ce_dir_offset_bishop, 1);
|
||
}
|
||
|
||
void ce_LoopQueen(void)
|
||
{
|
||
ce_LoopDirsSingleMultiStep(ce_dir_offset_queen, 1);
|
||
}
|
||
|
||
void ce_LoopKnight(void)
|
||
{
|
||
ce_LoopDirsSingleMultiStep(ce_dir_offset_knight, 0);
|
||
}
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
/* move king */
|
||
/*==============================================================*/
|
||
|
||
uint8_t cu_IsKingCastling(uint8_t mask, int8_t direction, uint8_t cnt) U8G2_NOINLINE;
|
||
|
||
/*
|
||
checks, if the king can do castling
|
||
|
||
Arguments:
|
||
mask: the bit-mask for the global "castling possible" flag
|
||
direction: left castling: -1, right castling 1
|
||
cnt: number of fields to be checked: 3 or 2
|
||
*/
|
||
uint8_t cu_IsKingCastling(uint8_t mask, int8_t direction, uint8_t cnt)
|
||
{
|
||
uint8_t pos;
|
||
uint8_t opponent_color;
|
||
|
||
/* check if the current board state allows castling */
|
||
if ( (lrc_obj.castling_possible & mask) == 0 )
|
||
return 0; /* castling not allowed */
|
||
|
||
/* get the position of the KING, could be white or black king */
|
||
pos = stack_GetCurrElement()->current_pos;
|
||
|
||
/* calculate the color of the opponent */
|
||
opponent_color = 1;
|
||
opponent_color -= stack_GetCurrElement()->current_color;
|
||
|
||
/* if the KING itself is given check... */
|
||
if ( ce_GetPositionAttackWeight(pos, opponent_color) > 0 )
|
||
return 0;
|
||
|
||
|
||
/* check if fields in the desired direction are emtpy */
|
||
for(;;)
|
||
{
|
||
/* go to the next field */
|
||
pos += direction;
|
||
/* check for a piece */
|
||
if ( cp_GetPiece(cp_GetFromBoard(pos)) != PIECE_NONE )
|
||
return 0; /* castling not allowed */
|
||
|
||
/* if some of the fields are under attack */
|
||
if ( ce_GetPositionAttackWeight(pos, opponent_color) > 0 )
|
||
return 0;
|
||
|
||
cnt--;
|
||
if ( cnt == 0 )
|
||
break;
|
||
}
|
||
return 1; /* castling allowed */
|
||
}
|
||
|
||
void ce_LoopKing(void)
|
||
{
|
||
/*
|
||
there is an interessting timing problem in this procedure
|
||
it must be checked for castling first and as second step the normal
|
||
KING movement. If we would first check for normal moves, than
|
||
any marks might be overwritten by the ROOK in the case of castling.
|
||
*/
|
||
|
||
/* castling (this must be done before checking normal moves (see above) */
|
||
if ( stack_GetCurrElement()->current_color == COLOR_WHITE )
|
||
{
|
||
/* white left castling */
|
||
if ( cu_IsKingCastling(1, -1, 3) != 0 )
|
||
{
|
||
/* check for attacked fields */
|
||
ce_LoopRecur(stack_GetCurrElement()->current_pos-2);
|
||
}
|
||
/* white right castling */
|
||
if ( cu_IsKingCastling(2, 1, 2) != 0 )
|
||
{
|
||
/* check for attacked fields */
|
||
ce_LoopRecur(stack_GetCurrElement()->current_pos+2);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* black left castling */
|
||
if ( cu_IsKingCastling(4, -1, 3) != 0 )
|
||
{
|
||
/* check for attacked fields */
|
||
ce_LoopRecur(stack_GetCurrElement()->current_pos-2);
|
||
}
|
||
/* black right castling */
|
||
if ( cu_IsKingCastling(8, 1, 2) != 0 )
|
||
{
|
||
/* check for attacked fields */
|
||
ce_LoopRecur(stack_GetCurrElement()->current_pos+2);
|
||
}
|
||
}
|
||
|
||
/* reuse queen directions */
|
||
ce_LoopDirsSingleMultiStep(ce_dir_offset_queen, 0);
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
/* move pawn */
|
||
/*==============================================================*/
|
||
|
||
/*
|
||
doppelschritt: nur von der grundlinie aus, beide (!) felder vor dem bauern m<>ssen frei sein
|
||
en passant: nur unmittelbar nachdem ein doppelschritt ausgef<65>hrt wurde.
|
||
*/
|
||
void ce_LoopPawnSideCapture(uint8_t loop_pos)
|
||
{
|
||
if ( gpos_IsIllegal(loop_pos) == 0 )
|
||
{
|
||
/* get the piece from the board */
|
||
/* if the field is NOT empty */
|
||
if ( cp_GetPiece(cp_GetFromBoard(loop_pos)) != PIECE_NONE )
|
||
{
|
||
/* normal capture */
|
||
ce_LoopRecur(loop_pos);
|
||
/* TODO: check for pawn conversion/promotion */
|
||
}
|
||
else
|
||
{
|
||
/* check conditions for en passant capture */
|
||
if ( stack_GetCurrElement()->current_color == COLOR_WHITE )
|
||
{
|
||
if ( lrc_obj.pawn_dbl_move[COLOR_BLACK]+16 == loop_pos )
|
||
{
|
||
ce_LoopRecur(loop_pos);
|
||
/* note: pawn conversion/promotion can not occur */
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ( lrc_obj.pawn_dbl_move[COLOR_WHITE] == loop_pos+16 )
|
||
{
|
||
ce_LoopRecur(loop_pos);
|
||
/* note: pawn conversion/promotion can not occur */
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void ce_LoopPawn(void)
|
||
{
|
||
uint8_t initial_pos = stack_GetCurrElement()->current_pos;
|
||
uint8_t my_color = stack_GetCurrElement()->current_color;
|
||
|
||
uint8_t loop_pos;
|
||
uint8_t line;
|
||
|
||
/* one step forward */
|
||
|
||
loop_pos = initial_pos;
|
||
line = initial_pos;
|
||
line >>= 4;
|
||
if ( my_color == COLOR_WHITE )
|
||
loop_pos += 16;
|
||
else
|
||
loop_pos -= 16;
|
||
if ( gpos_IsIllegal(loop_pos) == 0 )
|
||
{
|
||
/* if the field is empty */
|
||
if ( cp_GetPiece(cp_GetFromBoard(loop_pos)) == PIECE_NONE )
|
||
{
|
||
/* TODO: check for and loop through piece conversion/promotion */
|
||
ce_LoopRecur(loop_pos);
|
||
|
||
/* second step forward */
|
||
|
||
/* if pawn is on his starting line */
|
||
if ( (my_color == COLOR_WHITE && line == 1) || (my_color == COLOR_BLACK && line == 6 ) )
|
||
{
|
||
/* the place before the pawn is not occupied, so we can do double moves, see above */
|
||
|
||
if ( my_color == COLOR_WHITE )
|
||
loop_pos += 16;
|
||
else
|
||
loop_pos -= 16;
|
||
if ( cp_GetPiece(cp_GetFromBoard(loop_pos)) == PIECE_NONE )
|
||
{
|
||
/* this is a special case, other promotions of the pawn can not occur */
|
||
ce_LoopRecur(loop_pos);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* capture */
|
||
|
||
loop_pos = initial_pos;
|
||
if ( my_color == COLOR_WHITE )
|
||
loop_pos += 15;
|
||
else
|
||
loop_pos -= 15;
|
||
ce_LoopPawnSideCapture(loop_pos);
|
||
|
||
|
||
loop_pos = initial_pos;
|
||
if ( my_color == COLOR_WHITE )
|
||
loop_pos += 17;
|
||
else
|
||
loop_pos -= 17;
|
||
ce_LoopPawnSideCapture(loop_pos);
|
||
}
|
||
|
||
/*==============================================================*/
|
||
/* attacked */
|
||
/*==============================================================*/
|
||
|
||
/*
|
||
from a starting position, search for a piece, that might jump to that postion.
|
||
return:
|
||
the two global variables
|
||
lrc_obj.find_piece_weight[0];
|
||
lrc_obj.find_piece_weight[1];
|
||
will be increased by the weight of the attacked pieces of that color.
|
||
it is usually required to reset these global variables to zero, before using
|
||
this function.
|
||
*/
|
||
|
||
void ce_FindPieceByStep(uint8_t start_pos, uint8_t piece, const uint8_t *d, uint8_t is_multi_step)
|
||
{
|
||
uint8_t loop_pos, cp;
|
||
|
||
/* with all directions */
|
||
for(;;)
|
||
{
|
||
if ( chess_pgm_read(d) == 0 )
|
||
break;
|
||
|
||
/* start again from the initial position */
|
||
loop_pos = start_pos;
|
||
|
||
/* check direction */
|
||
do
|
||
{
|
||
/* check next position into one direction */
|
||
loop_pos += chess_pgm_read(d);
|
||
|
||
/* check if the board boundary has been crossed */
|
||
if ( (loop_pos & 0x088) != 0 )
|
||
break;
|
||
|
||
/* get the colored piece from the board */
|
||
cp = cp_GetFromBoard(loop_pos);
|
||
|
||
/* stop if we had hit another piece */
|
||
if ( cp_GetPiece(cp) != PIECE_NONE )
|
||
{
|
||
/* if it is the piece we are looking for, then add the weight */
|
||
if ( cp_GetPiece(cp) == piece )
|
||
{
|
||
lrc_obj.find_piece_weight[cp_GetColor(cp)] += ce_piece_weight[piece];
|
||
lrc_obj.find_piece_cnt[cp_GetColor(cp)]++;
|
||
}
|
||
/* in any case, break out of the inner loop */
|
||
break;
|
||
}
|
||
} while( is_multi_step );
|
||
d++;
|
||
}
|
||
}
|
||
|
||
void ce_FindPawnPiece(uint8_t dest_pos, uint8_t color)
|
||
{
|
||
uint8_t cp;
|
||
/* check if the board boundary has been crossed */
|
||
if ( (dest_pos & 0x088) == 0 )
|
||
{
|
||
/* get the colored piece from the board */
|
||
cp = cp_GetFromBoard(dest_pos);
|
||
/* only if there is a pawn of the matching color */
|
||
if ( cp_GetPiece(cp) == PIECE_PAWN )
|
||
{
|
||
if ( cp_GetColor(cp) == color )
|
||
{
|
||
/* the weight of the PAWN */
|
||
lrc_obj.find_piece_weight[color] += 1;
|
||
lrc_obj.find_piece_cnt[color]++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
find out, which pieces do attack a specified field
|
||
used to
|
||
- check if the KING can do castling
|
||
- check if the KING must move
|
||
|
||
may be used in the eval procedure ... once...
|
||
|
||
the result is stored in the global array
|
||
uint8_t lrc_obj.find_piece_weight[2];
|
||
which is indexed with the color.
|
||
lrc_obj.find_piece_weight[COLOR_WHITE] is the sum of all white pieces
|
||
which can directly move to this field.
|
||
|
||
example:
|
||
if the black KING is at "pos" and lrc_obj.find_piece_weight[COLOR_WHITE] is not zero
|
||
(after executing ce_CalculatePositionWeight(pos)) then the KING must be protected or moveed, because
|
||
the KING was given check.
|
||
*/
|
||
|
||
void ce_CalculatePositionWeight(uint8_t pos)
|
||
{
|
||
|
||
lrc_obj.find_piece_weight[0] = 0;
|
||
lrc_obj.find_piece_weight[1] = 0;
|
||
lrc_obj.find_piece_cnt[0] = 0;
|
||
lrc_obj.find_piece_cnt[1] = 0;
|
||
|
||
if ( (pos & 0x088) != 0 )
|
||
return;
|
||
|
||
ce_FindPieceByStep(pos, PIECE_ROOK, ce_dir_offset_rook, 1);
|
||
ce_FindPieceByStep(pos, PIECE_BISHOP, ce_dir_offset_bishop, 1);
|
||
ce_FindPieceByStep(pos, PIECE_QUEEN, ce_dir_offset_queen, 1);
|
||
ce_FindPieceByStep(pos, PIECE_KNIGHT, ce_dir_offset_knight, 0);
|
||
ce_FindPieceByStep(pos, PIECE_KING, ce_dir_offset_queen, 0);
|
||
|
||
ce_FindPawnPiece(pos+17, COLOR_BLACK);
|
||
ce_FindPawnPiece(pos+15, COLOR_BLACK);
|
||
ce_FindPawnPiece(pos-17, COLOR_WHITE);
|
||
ce_FindPawnPiece(pos-15, COLOR_WHITE);
|
||
}
|
||
|
||
/*
|
||
calculate the summed weight of pieces with specified color which can move to a specified position
|
||
|
||
argument:
|
||
pos: the position which should be analysed
|
||
color: the color of those pieces which should be analysed
|
||
e.g. if a black piece is at 'pos' and 'color' is white then this procedure returns the white atting count
|
||
*/
|
||
uint8_t ce_GetPositionAttackWeight(uint8_t pos, uint8_t color)
|
||
{
|
||
ce_CalculatePositionWeight(pos);
|
||
return lrc_obj.find_piece_weight[color];
|
||
}
|
||
|
||
uint8_t ce_GetPositionAttackCount(uint8_t pos, uint8_t color)
|
||
{
|
||
ce_CalculatePositionWeight(pos);
|
||
return lrc_obj.find_piece_cnt[color];
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
/* depth search starts here: loop over all pieces of the current color on the board */
|
||
/*==============================================================*/
|
||
|
||
void ce_LoopPieces(void)
|
||
{
|
||
stack_element_p e = stack_GetCurrElement();
|
||
/* start with lower left position (A1) */
|
||
e->current_pos = 0;
|
||
do
|
||
{
|
||
e->current_cp = cp_GetFromBoard(e->current_pos);
|
||
/* check if the position on the board is empty */
|
||
if ( e->current_cp != 0 )
|
||
{
|
||
/* only generate moves for the current color */
|
||
if ( e->current_color == cp_GetColor(e->current_cp) )
|
||
{
|
||
chess_Thinking();
|
||
|
||
/* find out which piece is used */
|
||
switch(cp_GetPiece(e->current_cp))
|
||
{
|
||
case PIECE_NONE:
|
||
break;
|
||
case PIECE_PAWN:
|
||
ce_LoopPawn();
|
||
break;
|
||
case PIECE_KNIGHT:
|
||
ce_LoopKnight();
|
||
break;
|
||
case PIECE_BISHOP:
|
||
ce_LoopBishop();
|
||
break;
|
||
case PIECE_ROOK:
|
||
ce_LoopRook();
|
||
break;
|
||
case PIECE_QUEEN:
|
||
ce_LoopQueen();
|
||
break;
|
||
case PIECE_KING:
|
||
ce_LoopKing();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
e->current_pos = cu_NextPos(e->current_pos);
|
||
} while( e->current_pos != 0 );
|
||
}
|
||
|
||
/*==============================================================*/
|
||
/* user interface */
|
||
/*==============================================================*/
|
||
|
||
/*
|
||
eval_t chess_EvalCurrBoard(uint8_t color)
|
||
{
|
||
stack_Init(0);
|
||
stack_GetCurrElement()->current_color = color;
|
||
ce_LoopPieces();
|
||
return stack_GetCurrElement()->best_eval;
|
||
}
|
||
*/
|
||
|
||
/* clear any marks on the board */
|
||
void chess_ClearMarks(void)
|
||
{
|
||
uint8_t i;
|
||
for( i = 0; i < 64; i++ )
|
||
lrc_obj.board[i] &= ~CP_MARK_MASK;
|
||
}
|
||
|
||
/*
|
||
Mark all pieces which can do moves. This is done by setting flags on the global board
|
||
*/
|
||
void chess_MarkMovable(void)
|
||
{
|
||
stack_Init(0);
|
||
//stack_GetCurrElement()->current_color = color;
|
||
lrc_obj.check_mode = CHECK_MODE_MOVEABLE;
|
||
ce_LoopPieces();
|
||
}
|
||
|
||
/*
|
||
Checks, if the piece can move from src_pos to dest_pos
|
||
|
||
src_pos: The game position of a piece on the chess board
|
||
*/
|
||
void chess_MarkTargetMoves(uint8_t src_pos)
|
||
{
|
||
stack_Init(0);
|
||
stack_GetCurrElement()->current_color = cp_GetColor(cp_GetFromBoard(src_pos));
|
||
lrc_obj.check_src_pos = src_pos;
|
||
lrc_obj.check_mode = CHECK_MODE_TARGET_MOVE;
|
||
ce_LoopPieces();
|
||
}
|
||
|
||
/*
|
||
first call should start with 255
|
||
this procedure will return 255 if
|
||
- there are no marks at all
|
||
- it has looped over all marks once
|
||
*/
|
||
uint8_t chess_GetNextMarked(uint8_t arg, uint8_t is_prev)
|
||
{
|
||
uint8_t i;
|
||
uint8_t pos = arg;
|
||
for(i = 0; i < 64; i++)
|
||
{
|
||
if ( is_prev != 0 )
|
||
pos = cu_PrevPos(pos);
|
||
else
|
||
pos = cu_NextPos(pos);
|
||
if ( arg != 255 && pos == 0 )
|
||
return 255;
|
||
if ( cp_IsMarked(cp_GetFromBoard(pos)) )
|
||
return pos;
|
||
}
|
||
return 255;
|
||
}
|
||
|
||
|
||
/* make a manual move: this is a little bit more than cu_Move() */
|
||
void chess_ManualMove(uint8_t src, uint8_t dest)
|
||
{
|
||
uint8_t cp;
|
||
|
||
/* printf("chess_ManualMove %02x -> %02x\n", src, dest); */
|
||
|
||
/* if all other things fail, this is the place where the game is to be decided: */
|
||
/* ... if the KING is captured */
|
||
cp = cp_GetFromBoard(dest);
|
||
if ( cp_GetPiece(cp) == PIECE_KING )
|
||
{
|
||
lrc_obj.is_game_end = 1;
|
||
lrc_obj.lost_side_color = cp_GetColor(cp);
|
||
}
|
||
|
||
/* clear ply history here, to avoid memory overflow */
|
||
/* may be the last X moves can be kept here */
|
||
cu_ReduceHistoryByFullMove();
|
||
/* perform the move on the board */
|
||
cu_Move(src, dest);
|
||
|
||
/* update en passant double move positions: en passant position is removed after two half moves */
|
||
lrc_obj.pawn_dbl_move[lrc_obj.ply_count&1] = ILLEGAL_POSITION;
|
||
|
||
/* update the global half move counter */
|
||
lrc_obj.ply_count++;
|
||
|
||
|
||
/* make a small check about the end of the game */
|
||
/* use at least depth 1, because we must know if the king can still move */
|
||
/* this is: King moves at level 0 and will be captured at level 1 */
|
||
/* so we check if the king can move and will not be captured at search level 1 */
|
||
|
||
stack_Init(1);
|
||
ce_LoopPieces();
|
||
|
||
/* printf("chess_ManualMove/analysis best_from_pos %02x -> best_to_pos %02x\n", stack_GetCurrElement()->best_from_pos, stack_GetCurrElement()->best_to_pos); */
|
||
|
||
/* analyse the eval result */
|
||
|
||
/* check if the other player has any moves left */
|
||
if ( stack_GetCurrElement()->best_from_pos == ILLEGAL_POSITION )
|
||
{
|
||
uint8_t color;
|
||
/* conditions: */
|
||
/* 1. no King, should never happen, opposite color has won */
|
||
/* this is already checked above at the beginning if this procedure */
|
||
/* 2. King is under attack, opposite color has won */
|
||
/* 3. King is not under attack, game is a draw */
|
||
|
||
uint8_t i = 0;
|
||
color = lrc_obj.ply_count;
|
||
color &= 1;
|
||
do
|
||
{
|
||
cp = cp_GetFromBoard(i);
|
||
/* look for the King */
|
||
if ( cp_GetPiece(cp) == PIECE_KING )
|
||
{
|
||
if ( cp_GetColor(cp) == color )
|
||
{
|
||
/* check if KING is attacked */
|
||
if ( ce_GetPositionAttackCount(i, color^1) != 0 )
|
||
{
|
||
/* KING is under attack (check) and can not move: Game is lost */
|
||
lrc_obj.is_game_end = 1;
|
||
lrc_obj.lost_side_color = color;
|
||
}
|
||
else
|
||
{
|
||
/* KING is NOT under attack (check) but can not move: Game is a draw */
|
||
lrc_obj.is_game_end = 1;
|
||
lrc_obj.lost_side_color = 2;
|
||
}
|
||
/* break out of the loop */
|
||
break;
|
||
}
|
||
}
|
||
i = cu_NextPos(i);
|
||
} while( i != 0 );
|
||
}
|
||
}
|
||
|
||
/* let the computer do a move */
|
||
void chess_ComputerMove(uint8_t depth)
|
||
{
|
||
stack_Init(depth);
|
||
|
||
//stack_GetCurrElement()->current_color = lrc_obj.ply_count;
|
||
//stack_GetCurrElement()->current_color &= 1;
|
||
|
||
cu_ReduceHistoryByFullMove();
|
||
ce_LoopPieces();
|
||
|
||
chess_ManualMove(stack_GetCurrElement()->best_from_pos, stack_GetCurrElement()->best_to_pos);
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
/* unix code */
|
||
/*==============================================================*/
|
||
|
||
|
||
/*==============================================================*/
|
||
/* display menu */
|
||
/*==============================================================*/
|
||
|
||
extern const uint8_t chess_pieces_body_bm[] CHESS_PROGMEM; // forward decl
|
||
extern const uint8_t chess_black_pieces_bm[] CHESS_PROGMEM; // forward decl
|
||
|
||
// uint8_t chess_key_code = 0; // obsolete, u8g2 does proper debouncing
|
||
uint8_t chess_key_cmd = 0;
|
||
#define CHESS_STATE_MENU 0
|
||
#define CHESS_STATE_SELECT_START 1
|
||
#define CHESS_STATE_SELECT_PIECE 2
|
||
#define CHESS_STATE_SELECT_TARGET_POS 3
|
||
#define CHESS_STATE_THINKING 4
|
||
#define CHESS_STATE_GAME_END 5
|
||
uint8_t chess_state = CHESS_STATE_MENU;
|
||
uint8_t chess_source_pos = 255;
|
||
uint8_t chess_target_pos = 255;
|
||
|
||
#define MNU_FONT u8g2_font_5x8_tr
|
||
#define MNU_ENTRY_HEIGHT 9
|
||
|
||
const char *mnu_title = "Little Rook Chess";
|
||
const char *mnu_list[] = { "New Game (White)", "New Game (Black)", "Undo Move", "Return" };
|
||
uint8_t mnu_pos = 0;
|
||
uint8_t mnu_max = 4;
|
||
|
||
void mnu_DrawHome(uint8_t is_highlight)
|
||
{
|
||
uint8_t x = lrc_u8g->width - 35;
|
||
uint8_t y = (lrc_u8g->height-1);
|
||
uint8_t t;
|
||
|
||
u8g2_SetFont(lrc_u8g, u8g2_font_5x7_tr);
|
||
u8g2_SetDefaultForegroundColor(lrc_u8g);
|
||
if ( chess_state == CHESS_STATE_THINKING )
|
||
u8g2_DrawBitmap(lrc_u8g, x, y-8, 1, 8, chess_black_pieces_bm+24);
|
||
else
|
||
{
|
||
t = u8g2_DrawStrP(lrc_u8g, x, y -1, CHESS_PSTR("Options"));
|
||
|
||
if ( is_highlight )
|
||
u8g2_DrawFrame(lrc_u8g, x-1, y - MNU_ENTRY_HEIGHT +1, t, MNU_ENTRY_HEIGHT);
|
||
}
|
||
}
|
||
|
||
void mnu_DrawEntry(uint8_t y, const char *str, uint8_t is_clr_background, uint8_t is_highlight)
|
||
{
|
||
uint8_t t, x;
|
||
u8g2_SetFont(lrc_u8g, MNU_FONT);
|
||
t = u8g2_GetStrWidth(lrc_u8g, str);
|
||
x = u8g2_GetDisplayWidth(lrc_u8g);
|
||
x -= t;
|
||
x >>= 1;
|
||
|
||
if ( is_clr_background )
|
||
{
|
||
u8g2_SetDefaultBackgroundColor(lrc_u8g);
|
||
u8g2_DrawBox(lrc_u8g, x-3, (lrc_u8g->height-1) - (y+MNU_ENTRY_HEIGHT-1+2), t+5, MNU_ENTRY_HEIGHT+4);
|
||
}
|
||
|
||
u8g2_SetDefaultForegroundColor(lrc_u8g);
|
||
u8g2_DrawStr(lrc_u8g, x, (lrc_u8g->height-1) - y, str);
|
||
|
||
if ( is_highlight )
|
||
{
|
||
u8g2_DrawFrame(lrc_u8g, 0, (lrc_u8g->height-1) - y -MNU_ENTRY_HEIGHT +1, u8g2_GetDisplayWidth(lrc_u8g), MNU_ENTRY_HEIGHT);
|
||
}
|
||
}
|
||
|
||
void mnu_Draw(void)
|
||
{
|
||
uint8_t i;
|
||
uint8_t t,y;
|
||
/* calculate hight of the complete menu */
|
||
y = mnu_max;
|
||
y++; /* consider also some space for the title */
|
||
y++; /* consider also some space for the title */
|
||
y *= MNU_ENTRY_HEIGHT;
|
||
|
||
/* calculate how much space will be left */
|
||
t = u8g2_GetDisplayHeight(lrc_u8g);
|
||
t -= y;
|
||
|
||
/* topmost pos start half of that empty space from the top */
|
||
t >>= 1;
|
||
y = u8g2_GetDisplayHeight(lrc_u8g);
|
||
y -= t;
|
||
|
||
y -= MNU_ENTRY_HEIGHT;
|
||
mnu_DrawEntry(y, mnu_title, 0, 0);
|
||
|
||
y -= MNU_ENTRY_HEIGHT;
|
||
|
||
|
||
u8g2_DrawBitmap(lrc_u8g, 0, 1, 1, 8, chess_black_pieces_bm+24);
|
||
u8g2_DrawBitmap(lrc_u8g, 128-8, 1, 1, 8, chess_black_pieces_bm+24);
|
||
|
||
|
||
for( i = 0; i < mnu_max; i++ )
|
||
{
|
||
y -= MNU_ENTRY_HEIGHT;
|
||
mnu_DrawEntry(y, mnu_list[i], 0, i == mnu_pos);
|
||
}
|
||
}
|
||
|
||
void mnu_Step(uint8_t key_cmd)
|
||
{
|
||
if ( key_cmd == CHESS_KEY_NEXT )
|
||
{
|
||
if ( mnu_pos+1 < mnu_max )
|
||
mnu_pos++;
|
||
}
|
||
else if ( key_cmd == CHESS_KEY_PREV )
|
||
{
|
||
if ( mnu_pos > 0 )
|
||
mnu_pos--;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
const uint8_t chess_pieces_body_bm[] CHESS_PROGMEM =
|
||
{
|
||
/* PAWN */ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, /* 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x00, */
|
||
/* KNIGHT */ 0x00, 0x00, 0x1c, 0x2c, 0x04, 0x04, 0x0e, 0x00,
|
||
/* BISHOP */ 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x08, 0x00, 0x00, /* 0x00, 0x00, 0x08, 0x1c, 0x1c, 0x08, 0x00, 0x00, */
|
||
/* ROOK */ 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x1c, 0x00,
|
||
/* QUEEN */ 0x00, 0x00, 0x14, 0x1c, 0x08, 0x1c, 0x08, 0x00,
|
||
/* KING */ 0x00, 0x00, 0x00, 0x08, 0x3e, 0x1c, 0x08, 0x00,
|
||
};
|
||
|
||
#ifdef NOT_REQUIRED
|
||
/* white pieces are constructed by painting black pieces and cutting out the white area */
|
||
const uint8_t chess_white_pieces_bm[] CHESS_PROGMEM =
|
||
{
|
||
/* PAWN */ 0x00, 0x00, 0x0c, 0x12, 0x12, 0x0c, 0x1e, 0x00,
|
||
/* KNIGHT */ 0x00, 0x1c, 0x22, 0x52, 0x6a, 0x0a, 0x11, 0x1f,
|
||
/* BISHOP */ 0x00, 0x08, 0x14, 0x22, 0x22, 0x14, 0x08, 0x7f,
|
||
/* ROOK */ 0x00, 0x55, 0x7f, 0x22, 0x22, 0x22, 0x22, 0x7f,
|
||
/* QUEEN */ 0x00, 0x55, 0x2a, 0x22, 0x14, 0x22, 0x14, 0x7f,
|
||
/* KING */ 0x08, 0x1c, 0x49, 0x77, 0x41, 0x22, 0x14, 0x7f,
|
||
};
|
||
#endif
|
||
|
||
const uint8_t chess_black_pieces_bm[] CHESS_PROGMEM =
|
||
{
|
||
/* PAWN */ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x3c, 0x00, /* 0x00, 0x00, 0x0c, 0x1e, 0x1e, 0x0c, 0x1e, 0x00, */
|
||
/* KNIGHT */ 0x00, 0x1c, 0x3e, 0x7e, 0x6e, 0x0e, 0x1f, 0x1f,
|
||
/* BISHOP */ 0x00, 0x1c, 0x2e, 0x3e, 0x3e, 0x1c, 0x08, 0x7f, /*0x00, 0x08, 0x1c, 0x3e, 0x3e, 0x1c, 0x08, 0x7f,*/
|
||
/* ROOK */ 0x00, 0x55, 0x7f, 0x3e, 0x3e, 0x3e, 0x3e, 0x7f,
|
||
/* QUEEN */ 0x00, 0x55, 0x3e, 0x3e, 0x1c, 0x3e, 0x1c, 0x7f,
|
||
/* KING -*/ 0x08, 0x1c, 0x49, 0x7f, 0x7f, 0x3e, 0x1c, 0x7f,
|
||
};
|
||
|
||
|
||
#if defined(DOGXL160_HW_GR)
|
||
#define BOXSIZE 13
|
||
#define BOXOFFSET 3
|
||
#else
|
||
#define BOXSIZE 8
|
||
#define BOXOFFSET 1
|
||
#endif
|
||
|
||
u8g2_uint_t chess_low_edge;
|
||
uint8_t chess_boxsize = 8;
|
||
uint8_t chess_boxoffset = 1;
|
||
|
||
|
||
void chess_DrawFrame(uint8_t pos, uint8_t is_bold)
|
||
{
|
||
u8g2_uint_t x0, y0;
|
||
|
||
x0 = pos;
|
||
x0 &= 15;
|
||
if ( lrc_obj.orientation != COLOR_WHITE )
|
||
x0 ^= 7;
|
||
|
||
y0 = pos;
|
||
y0>>= 4;
|
||
if ( lrc_obj.orientation != COLOR_WHITE )
|
||
y0 ^= 7;
|
||
|
||
x0 *= chess_boxsize;
|
||
y0 *= chess_boxsize;
|
||
|
||
u8g2_SetDefaultForegroundColor(lrc_u8g);
|
||
u8g2_DrawFrame(lrc_u8g, x0, chess_low_edge - y0 - chess_boxsize+1, chess_boxsize, chess_boxsize);
|
||
|
||
|
||
if ( is_bold )
|
||
{
|
||
x0--;
|
||
y0++;
|
||
|
||
u8g2_DrawFrame(lrc_u8g, x0, chess_low_edge - y0 - chess_boxsize +1, chess_boxsize+2, chess_boxsize+2);
|
||
}
|
||
}
|
||
|
||
|
||
void chess_DrawBoard(void)
|
||
{
|
||
uint8_t i, j, cp;
|
||
const uint8_t *ptr; /* pointer into CHESS_PROGMEM */
|
||
|
||
{
|
||
uint8_t x_offset = 1;
|
||
u8g2_SetDefaultForegroundColor(lrc_u8g);
|
||
for( i = 0; i < 8*8; i+=8 )
|
||
{
|
||
for( j = 0; j < 8*8; j+=8 )
|
||
{
|
||
if ( ((i^j) & 8) == 0 )
|
||
{
|
||
if ( lrc_obj.orientation == COLOR_WHITE )
|
||
{
|
||
cp = lrc_obj.board[i+j/8];
|
||
}
|
||
else
|
||
{
|
||
cp = lrc_obj.board[(7-i/8)*8+7-j/8];
|
||
}
|
||
if ( cp_GetPiece(cp) == PIECE_NONE )
|
||
{
|
||
u8g2_DrawPixel(lrc_u8g, j+0+x_offset, chess_low_edge - i-0);
|
||
u8g2_DrawPixel(lrc_u8g, j+0+x_offset, chess_low_edge - i-2);
|
||
u8g2_DrawPixel(lrc_u8g, j+0+x_offset, chess_low_edge - i-4);
|
||
u8g2_DrawPixel(lrc_u8g, j+0+x_offset, chess_low_edge - i-6);
|
||
u8g2_DrawPixel(lrc_u8g, j+2+x_offset, chess_low_edge - i-0);
|
||
u8g2_DrawPixel(lrc_u8g, j+2+x_offset, chess_low_edge - i-6);
|
||
u8g2_DrawPixel(lrc_u8g, j+4+x_offset, chess_low_edge - i-0);
|
||
u8g2_DrawPixel(lrc_u8g, j+4+x_offset, chess_low_edge - i-6);
|
||
u8g2_DrawPixel(lrc_u8g, j+6+x_offset, chess_low_edge - i-0);
|
||
u8g2_DrawPixel(lrc_u8g, j+6+x_offset, chess_low_edge - i-2);
|
||
u8g2_DrawPixel(lrc_u8g, j+6+x_offset, chess_low_edge - i-4);
|
||
u8g2_DrawPixel(lrc_u8g, j+6+x_offset, chess_low_edge - i-6);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for ( i = 0; i < 8; i++ )
|
||
{
|
||
for ( j = 0; j < 8; j++ )
|
||
{
|
||
/* get piece from global board */
|
||
if ( lrc_obj.orientation == COLOR_WHITE )
|
||
{
|
||
cp = lrc_obj.board[i*8+j];
|
||
}
|
||
else
|
||
{
|
||
cp = lrc_obj.board[(7-i)*8+7-j];
|
||
}
|
||
if ( cp_GetPiece(cp) != PIECE_NONE )
|
||
{
|
||
ptr = chess_black_pieces_bm;
|
||
ptr += (cp_GetPiece(cp)-1)*8;
|
||
|
||
u8g2_SetDefaultForegroundColor(lrc_u8g);
|
||
u8g2_DrawBitmap(lrc_u8g, j*chess_boxsize+chess_boxoffset-1, chess_low_edge - (i*chess_boxsize+chess_boxsize-chess_boxoffset), 1, 8, ptr);
|
||
|
||
if ( cp_GetColor(cp) == lrc_obj.strike_out_color )
|
||
{
|
||
ptr = chess_pieces_body_bm;
|
||
ptr += (cp_GetPiece(cp)-1)*8;
|
||
u8g2_SetDefaultBackgroundColor(lrc_u8g);
|
||
u8g2_DrawBitmap(lrc_u8g, j*chess_boxsize+chess_boxoffset-1, chess_low_edge - (i*chess_boxsize+chess_boxsize-chess_boxoffset), 1, 8, ptr);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
u8g2_SetDefaultForegroundColor(lrc_u8g);
|
||
|
||
if ( (chess_source_pos & 0x88) == 0 )
|
||
{
|
||
chess_DrawFrame(chess_source_pos, 1);
|
||
}
|
||
|
||
if ( (chess_target_pos & 0x88) == 0 )
|
||
{
|
||
chess_DrawFrame(chess_target_pos, 0);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
void chess_Thinking(void)
|
||
{
|
||
}
|
||
|
||
void chess_Init(u8g2_t *u8g, uint8_t body_color)
|
||
{
|
||
lrc_u8g = u8g;
|
||
|
||
chess_low_edge = u8g2_GetDisplayHeight(lrc_u8g);
|
||
chess_low_edge--;
|
||
|
||
|
||
{
|
||
|
||
chess_boxsize = 8;
|
||
chess_boxoffset = 1;
|
||
}
|
||
|
||
|
||
|
||
lrc_obj.strike_out_color = body_color;
|
||
chess_SetupBoard();
|
||
}
|
||
|
||
|
||
|
||
void chess_Draw(void)
|
||
{
|
||
if ( chess_state == CHESS_STATE_MENU )
|
||
{
|
||
if ( lrc_obj.ply_count == 0)
|
||
mnu_max = 2;
|
||
else
|
||
mnu_max = 4;
|
||
mnu_Draw();
|
||
}
|
||
else
|
||
{
|
||
chess_DrawBoard();
|
||
|
||
{
|
||
uint8_t i;
|
||
uint8_t entries = lrc_obj.chm_pos;
|
||
if ( entries > 4 )
|
||
entries = 4;
|
||
|
||
u8g2_SetFont(lrc_u8g, u8g2_font_5x7_tr);
|
||
u8g2_SetDefaultForegroundColor(lrc_u8g);
|
||
for( i = 0; i < entries; i++ )
|
||
{
|
||
|
||
u8g2_DrawStr(lrc_u8g, u8g2_GetDisplayWidth(lrc_u8g)-35, 8*(i+1), cu_GetHalfMoveStr(lrc_obj.chm_pos-entries+i));
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if ( chess_state == CHESS_STATE_SELECT_PIECE )
|
||
mnu_DrawHome(chess_source_pos == 255);
|
||
else if ( chess_state == CHESS_STATE_SELECT_TARGET_POS )
|
||
mnu_DrawHome(chess_target_pos == 255);
|
||
else
|
||
mnu_DrawHome(0);
|
||
|
||
if ( chess_state == CHESS_STATE_GAME_END )
|
||
{
|
||
switch( lrc_obj.lost_side_color )
|
||
{
|
||
case COLOR_WHITE:
|
||
mnu_DrawEntry(u8g2_GetDisplayHeight(lrc_u8g) / 2-2, "Black wins", 1, 1);
|
||
break;
|
||
case COLOR_BLACK:
|
||
mnu_DrawEntry(u8g2_GetDisplayHeight(lrc_u8g) / 2-2, "White wins", 1, 1);
|
||
break;
|
||
default:
|
||
mnu_DrawEntry(u8g2_GetDisplayHeight(lrc_u8g) / 2-2, "Stalemate", 1, 1);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void chess_Step(uint8_t keycode)
|
||
{
|
||
chess_key_cmd = keycode;
|
||
|
||
/*
|
||
if ( keycode == CHESS_KEY_NONE )
|
||
{
|
||
chess_key_cmd = chess_key_code;
|
||
chess_key_code = CHESS_KEY_NONE;
|
||
}
|
||
else
|
||
{
|
||
chess_key_cmd = CHESS_KEY_NONE;
|
||
chess_key_code = keycode;
|
||
}
|
||
*/
|
||
//chess_ComputerMove(2);
|
||
switch(chess_state)
|
||
{
|
||
case CHESS_STATE_MENU:
|
||
mnu_Step(chess_key_cmd);
|
||
if ( chess_key_cmd == CHESS_KEY_SELECT )
|
||
{
|
||
if ( mnu_pos == 0 )
|
||
{
|
||
chess_SetupBoard();
|
||
lrc_obj.orientation = 0;
|
||
chess_state = CHESS_STATE_SELECT_START;
|
||
}
|
||
else if ( mnu_pos == 1 )
|
||
{
|
||
chess_SetupBoard();
|
||
lrc_obj.orientation = 1;
|
||
chess_state = CHESS_STATE_THINKING;
|
||
}
|
||
else if ( mnu_pos == 2 )
|
||
{
|
||
if ( lrc_obj.ply_count >= 2 )
|
||
{
|
||
cu_UndoHalfMove();
|
||
cu_UndoHalfMove();
|
||
lrc_obj.ply_count-=2;
|
||
if ( lrc_obj.ply_count == 0 )
|
||
mnu_pos = 0;
|
||
}
|
||
chess_state = CHESS_STATE_SELECT_START;
|
||
}
|
||
else if ( mnu_pos == 3 )
|
||
{
|
||
chess_state = CHESS_STATE_SELECT_START;
|
||
}
|
||
}
|
||
break;
|
||
case CHESS_STATE_SELECT_START:
|
||
chess_ClearMarks();
|
||
chess_MarkMovable();
|
||
chess_source_pos = chess_GetNextMarked(255, 0);
|
||
chess_target_pos = ILLEGAL_POSITION;
|
||
chess_state = CHESS_STATE_SELECT_PIECE;
|
||
break;
|
||
|
||
case CHESS_STATE_SELECT_PIECE:
|
||
if ( chess_key_cmd == CHESS_KEY_NEXT )
|
||
{
|
||
chess_source_pos = chess_GetNextMarked(chess_source_pos, lrc_obj.orientation);
|
||
}
|
||
else if ( chess_key_cmd == CHESS_KEY_PREV )
|
||
{
|
||
chess_source_pos = chess_GetNextMarked(chess_source_pos, 1-lrc_obj.orientation );
|
||
}
|
||
else if ( chess_key_cmd == CHESS_KEY_SELECT )
|
||
{
|
||
if ( chess_source_pos == 255 )
|
||
{
|
||
chess_state = CHESS_STATE_MENU;
|
||
}
|
||
else
|
||
{
|
||
chess_ClearMarks();
|
||
chess_MarkTargetMoves(chess_source_pos);
|
||
chess_target_pos = chess_GetNextMarked(255, 0);
|
||
chess_state = CHESS_STATE_SELECT_TARGET_POS;
|
||
}
|
||
}
|
||
break;
|
||
case CHESS_STATE_SELECT_TARGET_POS:
|
||
if ( chess_key_cmd == CHESS_KEY_NEXT )
|
||
{
|
||
chess_target_pos = chess_GetNextMarked(chess_target_pos, lrc_obj.orientation);
|
||
if ( chess_target_pos == 255 )
|
||
chess_target_pos = chess_GetNextMarked(chess_target_pos, lrc_obj.orientation);
|
||
}
|
||
else if ( chess_key_cmd == CHESS_KEY_PREV )
|
||
{
|
||
chess_target_pos = chess_GetNextMarked(chess_target_pos, 1-lrc_obj.orientation);
|
||
if ( chess_target_pos == 255 )
|
||
chess_target_pos = chess_GetNextMarked(chess_target_pos, 1-lrc_obj.orientation);
|
||
}
|
||
else if ( chess_key_cmd == CHESS_KEY_BACK )
|
||
{
|
||
chess_ClearMarks();
|
||
chess_MarkMovable();
|
||
chess_target_pos = ILLEGAL_POSITION;
|
||
chess_state = CHESS_STATE_SELECT_PIECE;
|
||
}
|
||
else if ( chess_key_cmd == CHESS_KEY_SELECT )
|
||
{
|
||
chess_ManualMove(chess_source_pos, chess_target_pos);
|
||
if ( lrc_obj.is_game_end != 0 )
|
||
chess_state = CHESS_STATE_GAME_END;
|
||
else
|
||
chess_state = CHESS_STATE_THINKING;
|
||
/* clear marks as some kind of feedback to the user... it simply looks better */
|
||
chess_source_pos = ILLEGAL_POSITION;
|
||
chess_target_pos = ILLEGAL_POSITION;
|
||
chess_ClearMarks();
|
||
}
|
||
break;
|
||
case CHESS_STATE_THINKING:
|
||
chess_ComputerMove(2);
|
||
if ( lrc_obj.is_game_end != 0 )
|
||
chess_state = CHESS_STATE_GAME_END;
|
||
else
|
||
chess_state = CHESS_STATE_SELECT_START;
|
||
break;
|
||
case CHESS_STATE_GAME_END:
|
||
if ( chess_key_cmd != CHESS_KEY_NONE )
|
||
{
|
||
chess_state = CHESS_STATE_MENU;
|
||
chess_SetupBoard();
|
||
}
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
void loop(void) {
|
||
static uint8_t keycode = 0;
|
||
uint8_t k;
|
||
|
||
u8g2_FirstPage(lrc_u8g);
|
||
do {
|
||
chess_Draw();
|
||
} while ( u8g2_NextPage(lrc_u8g) );
|
||
|
||
do
|
||
{
|
||
k = u8g_sdl_get_key();
|
||
} while( k < 0 );
|
||
|
||
keycode = 0;
|
||
if ( k == 273 ) keycode = CHESS_KEY_PREV; /* up */
|
||
if ( k == 274 ) keycode = CHESS_KEY_NEXT; /* down */
|
||
if ( k == 276 ) keycode = CHESS_KEY_PREV; /* left */
|
||
if ( k == 275 ) keycode = CHESS_KEY_NEXT;
|
||
|
||
if ( k == 'n' ) keycode = CHESS_KEY_NEXT; /* down */
|
||
if ( k == 'p' ) keycode = CHESS_KEY_PREV; /* left */
|
||
|
||
if ( k == ' ' ) keycode = CHESS_KEY_SELECT ;
|
||
if ( k == 's' ) keycode = CHESS_KEY_SELECT ;
|
||
if ( k == 'q' ) exit(0);
|
||
|
||
|
||
if ( keycode != 0 )
|
||
chess_Step(keycode);
|
||
keycode = 0;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
u8g2_t u8g2;
|
||
|
||
|
||
void setup(void) {
|
||
//u8g2_Begin(&u8g2);
|
||
chess_Init(&u8g2, 1);
|
||
}
|
||
|
||
|
||
int main(void)
|
||
{
|
||
|
||
u8g2_SetupBuffer_SDL_128x64_4(&u8g2, &u8g2_cb_r0);
|
||
u8x8_InitDisplay(u8g2_GetU8x8(&u8g2));
|
||
u8x8_SetPowerSave(u8g2_GetU8x8(&u8g2), 0);
|
||
|
||
u8g2_SetFont(&u8g2, u8g2_font_helvB18_tr);
|
||
|
||
setup();
|
||
for(;;)
|
||
{
|
||
loop();
|
||
}
|
||
return 0;
|
||
}
|
||
|