/* simple chess implementation by v4hn @ 2009 */ #include #include #include typedef enum { BLACK = 0x00, WHITE = 0x20 } chess_color; typedef unsigned short chess_move; typedef signed char chess_pos; typedef char* chess_board; #define IS_FREE(x) (x == 0x20) #define IS_PIECE(x) (x&(0x40)) #define IS(color, x) (!IS_FREE(x) && ((x&0x20) == color)) #define OPPONENT(color) ((chess_color) (color^0x20)) #define POS(x,y) ((chess_pos)(x-65)+((8-y)*8)) #define MOVE(f,t) ((chess_move)(((short) (f) << 8) | (t))) #define FROM(m) ((chess_pos)(m >> 8)) #define TO(m) ((chess_pos)(m & 0xFF)) void print_board(); int valid_pawn_move(chess_move m); int valid_rook_move(chess_move move); int valid_bishop_move(chess_move move); int valid_knight_move(chess_move move); int valid_king_move(chess_move move); int valid_castling_move(chess_color color, chess_move move); int is_reachable(chess_color color, chess_pos pos); int self_check_after(chess_color color, chess_move move); int is_valid(chess_color color, chess_move move); chess_move read_move(chess_color color); int do_move(chess_move move); int move(chess_color color); /* current board */ char* board; /* was the last move a wide pawn jump? (en passant possible)*/ chess_pos pawn_jump; /* is castling still allowed? */ char castling_possible[2]; inline int sgn(int x){ return (x < 0) ? -1 : 1; } void print_board(){ unsigned int row= 0, col= 0; printf("\n" " || A | B | C | D | E | F | G | H ||\n" "=======================================\n"); for(row= 0; row < 8; row++){ printf("%d ||", 8-row); for(col= 0; col < 8; col++){ printf(" %c |", board[row*8+col]); } printf("| %d\n" "---------------------------------------\n", 8-row); } printf("=======================================\n" " || A | B | C | D | E | F | G | H ||\n\n"); } int valid_pawn_move(chess_move m){ return ( IS(WHITE, board[FROM(m)]) ? ((FROM(m)/8 - TO(m)/8 == 1) && (FROM(m)%8 - TO(m)%8 == 0) && IS_FREE(board[TO(m)])) || ((FROM(m)/8 - TO(m)/8 == 1) && (abs(FROM(m)%8 - TO(m)%8) == 1) && (IS(BLACK, board[TO(m)]) || (pawn_jump == TO(m)+8))) || ((FROM(m)/8 - TO(m)/8 == 2) && FROM(m)%8 - TO(m)%8 == 0 && IS_FREE(board[FROM(m)-8]) && IS_FREE(board[TO(m)])) : ((TO(m)/8 - FROM(m)/8 == 1) && (TO(m)%8 - FROM(m)%8 == 0) && IS_FREE(board[TO(m)])) || ((TO(m)/8 - FROM(m)/8 == 1) && (abs(TO(m)%8 - FROM(m)%8) == 1) && (IS(WHITE, TO(m)) || pawn_jump == TO(m)-8)) || ((TO(m)/8 - FROM(m)/8 == 2) && TO(m)%8 - FROM(m)%8 == 0 && IS_FREE(board[FROM(m)+8]) && IS_FREE(board[TO(m)])) ); } int valid_rook_move(chess_move move){ chess_pos i; if((FROM(move)%8 != TO(move)%8) && (FROM(move)/8 != TO(move)/8)) return 0; i= FROM(move); while(i != TO(move) && IS_FREE(board[ i+= sgn(TO(move)-FROM(move)) * ((FROM(move)%8 == TO(move)%8)?8:1) ]) ); return (i == TO(move)); } int valid_bishop_move(chess_move move){ chess_pos i, last_round; if( (!((TO(move)-FROM(move))%9) && !((TO(move)-FROM(move))%7))) return 0; i= last_round= FROM(move); while(i != TO(move) && IS_FREE(board[ i+= sgn(TO(move)-FROM(move)) * ((TO(move)-FROM(move))%9 ? 7 : 9) ])){ if(abs(last_round/8 - i/8) != 1) return 0; last_round= i; } return (i == TO(move)); } int valid_knight_move(chess_move move){ return (abs(FROM(move)%8 - TO(move)%8) == 2 && abs(FROM(move)/8 - TO(move)/8) == 1) || (abs(FROM(move)%8 - TO(move)%8) == 1 && abs(FROM(move)/8 - TO(move)/8) == 2); } int valid_king_move(chess_move move){ return (abs(FROM(move)%8 - TO(move)%8) <= 1) && (abs(FROM(move)/8 - TO(move)/8) <= 1); } int valid_castling_move(chess_color color, chess_move move){ chess_pos i; switch(TO(move)&~0x20){ case 0: if(castling_possible[color == WHITE]%2 == 0) return 0; for(i= 5+8*(color == WHITE ? 7 : 0); i%8 != 7; i++) if(!IS_FREE(board[i])) return 0; for(i= 5+8*(color == WHITE ? 7 : 0); i%8 != 7; i++) if(is_reachable(OPPONENT(color), i)) return 0; break; case 1: if(castling_possible[color == WHITE] < 2) return 0; for(i= 1+8*(color == WHITE ? 7 : 0); i%8 != 4; i++) if(!IS_FREE(board[i])) return 0; for(i= 2+8*(color == WHITE ? 7 : 0); i%8 != 5; i++) if(is_reachable(OPPONENT(color), i)) return 0; break; default: return 0; } return 1; } int is_reachable(chess_color color, chess_pos pos){ chess_pos i; for(i= 0; i < 65; i++){ if(is_valid(color, MOVE(i, pos))) break; } return (i != 65); } int self_check_after(chess_color color, chess_move move){ chess_pos king_pos; char *real_board, hyp_board[65]; int tmp; real_board= board; strcpy(hyp_board, board); board= hyp_board; do_move(move); for(king_pos= 0; king_pos < 65; king_pos++) if( IS(color, board[king_pos]) && ((board[king_pos]|0x20) == 'k') ) break; tmp= is_reachable(OPPONENT(color), king_pos); board= real_board; return tmp; } int is_valid(chess_color color, chess_move move){ if(FROM(move) == -1) return 1; if(FROM(move) == -2) return ((TO(move)&0x20) == color) && valid_castling_move(color, move); if( (FROM(move) == TO(move)) || !IS(color, board[FROM(move)]) || IS(color, board[TO(move)]) ) return 0; switch(board[FROM(move)]|0x20){ case 'p': return valid_pawn_move(move); case 'r': return valid_rook_move(move); case 'n': return valid_knight_move(move); case 'b': return valid_bishop_move(move); case 'q': return valid_rook_move(move) || valid_bishop_move(move); case 'k': return valid_king_move(move); default: return 0; } } chess_move read_move(chess_color color){ char x1, x2; unsigned int y1, y2; char buf[64]; for(;;){ printf("%s move(%s): ", (color == BLACK) ? "blacks" : "whites", (color == BLACK) ? "uc" : "lc"); fgets(buf, sizeof(buf), stdin); if(sscanf(buf, "%c%u-%c%u", &x1, &y1, &x2, &y2) == 4 && (x1&= ~0x20, ('A' <= x1 && x1 <= 'H') && (1 <= y1 && y1 <= 8)) && (x2&= ~0x20, ('A' <= x2 && x2 <= 'H') && (1 <= y2 && y2 <= 8))) return MOVE(POS(x1,y1), POS(x2, y2)); else if(!strcmp(buf, "resign\n")) return 0xFF00; else if(!strcmp(buf, "0-0\n")) return 0xFE00 | color; else if(!strcmp(buf, "0-0-0\n")) return 0xFE01 | color; } } int do_move(chess_move move){ switch(FROM(move)){ case -2: switch(TO(move)&~0x20){ case 0: board[4+8*((TO(move)&0x20) == WHITE ? 7 : 0)]= ' '; board[5+8*((TO(move)&0x20) == WHITE ? 7 : 0)]= 'R' | (TO(move)&0x20); board[6+8*((TO(move)&0x20) == WHITE ? 7 : 0)]= 'K' | (TO(move)&0x20); board[7+8*((TO(move)&0x20) == WHITE ? 7 : 0)]= ' '; break; case 1: board[0+8*((TO(move)&0x20) == WHITE ? 7 : 0)]= ' '; board[1+8*((TO(move)&0x20) == WHITE ? 7 : 0)]= ' '; board[2+8*((TO(move)&0x20) == WHITE ? 7 : 0)]= 'K' | (TO(move)&0x20); board[3+8*((TO(move)&0x20) == WHITE ? 7 : 0)]= 'R' | (TO(move)&0x20); board[4+8*((TO(move)&0x20) == WHITE ? 7 : 0)]= ' '; break; default: break; } break; default: board[TO(move)]= board[FROM(move)]; board[FROM(move)]= ' '; if(((board[TO(move)]|0x20) == 'p') && (pawn_jump == TO(move) + sgn(FROM(move)-TO(move))*8)) board[pawn_jump]= ' '; } return 1; } int move(chess_color color){ chess_move move; char s= '\0'; print_board(); while( !is_valid(color, move= read_move(color)) || self_check_after(color, move) ) printf("invalid move\n"); if(FROM(move) == -2 || (board[FROM(move)]|0x20) == 'k') castling_possible[color == WHITE]= 0; else if(FROM(move) == 8*(color == WHITE ? 7 : 0)) castling_possible[color == WHITE]&= ~2; else if(FROM(move) == 7+8*(color == WHITE ? 7 : 0)) castling_possible[color == WHITE]&= ~1; else if(FROM(move) == -1){ printf("%s resigned\n", (color == BLACK) ? "black" : "white" ); return 0; } do_move(move); if( (board[TO(move)]|0x20 == 'p') && (TO(move)/8 == ((board[TO(move)]&0x20) == WHITE ? 0 : 7))){ while(s != 'Q' && s != 'N' && s != 'R' && s != 'B'){ printf("select promotion out of /q/ueen, k/n/ight, /r/ook, /b/ishop: "); s= (getchar()&~0x20); } board[TO(move)]= (board[TO(move)]&0x20) | s; } pawn_jump= ((board[TO(move)]|0x20) == 'p' && abs(FROM(move) - TO(move)) == 16) ? TO(move) : 0; return 1; } int main(){ board= malloc(65*sizeof(char)); strcpy(board, "RNBQKBNR" /*black*/ "PPPPPPPP" " " " " " " " " "ppPppppp" "rn qkbnr" /*white*/ ); pawn_jump= 0; castling_possible[0]= castling_possible[1]= 3; while(move(WHITE) && move(BLACK)); return 0; }