wpawn/* Graphics Not Rendered in HTML */ wcastle/* Graphics Not Rendered in HTML */ wknight/* Graphics Not Rendered in HTML */ wbishop/* Graphics Not Rendered in HTML */ wqueen/* Graphics Not Rendered in HTML */ wking/* Graphics Not Rendered in HTML */ bpawn/* Graphics Not Rendered in HTML */ bcastle/* Graphics Not Rendered in HTML */ bknight/* Graphics Not Rendered in HTML */ bbishop/* Graphics Not Rendered in HTML */ bqueen/* Graphics Not Rendered in HTML */ bking/* Graphics Not Rendered in HTML */ //These are weights for scoring positions. #define SHIFT_PIECE_SCORE 4 #define SHIFT_POSSIBLE_MOVES 0 #define P_PAWN 0 #define P_ROOK 1 #define P_KNIGHT 2 #define P_BISHOP 3 #define P_QUEEN 4 #define P_KING 5 U8 *imgs[12] = {<1>, <7>, <2>, <8>, <3>, <9>, <4>, <10>, <5>, <11>, <6>, <12>}; I64 piece_scores[6] = { 1 << SHIFT_PIECE_SCORE, 5 << SHIFT_PIECE_SCORE, 3 << SHIFT_PIECE_SCORE, 3 << SHIFT_PIECE_SCORE, 9 << SHIFT_PIECE_SCORE, 100 << SHIFT_PIECE_SCORE }; U8 piece_letters[] = "PRNBQK"; class Move { I8 piece, x, y, pad; I32 score; }; class PieceState { I8 player, type, x, y; }; class GameState { GameState *next, *last, *parent; PieceState pieces[32]; I64 cur_player, mv_num, moved_mask, raw_score; //Positive favors player#0 Bool is_human[2]; Move potential_en_passant_victim; }; GameState setup_state = { NULL, NULL, NULL, { {0, P_KING, 0, 4}, {0, P_QUEEN, 0, 3}, {0, P_BISHOP, 0, 2}, {0, P_BISHOP, 0, 5}, {0, P_KNIGHT, 0, 1}, {0, P_KNIGHT, 0, 6}, {0, P_ROOK, 0, 0}, {0, P_ROOK, 0, 7}, {0, P_PAWN, 1, 0}, {0, P_PAWN, 1, 1}, {0, P_PAWN, 1, 2}, {0, P_PAWN, 1, 3}, {0, P_PAWN, 1, 4}, {0, P_PAWN, 1, 5}, {0, P_PAWN, 1, 6}, {0, P_PAWN, 1, 7}, {1, P_KING, 7, 4}, {1, P_QUEEN, 7, 3}, {1, P_BISHOP, 7, 2}, {1, P_BISHOP, 7, 5}, {1, P_KNIGHT, 7, 1}, {1, P_KNIGHT, 7, 6}, {1, P_ROOK, 7, 0}, {1, P_ROOK, 7, 7}, {1, P_PAWN, 6, 0}, {1, P_PAWN, 6, 1}, {1, P_PAWN, 6, 2}, {1, P_PAWN, 6, 3}, {1, P_PAWN, 6, 4}, {1, P_PAWN, 6, 5}, {1, P_PAWN, 6, 6}, {1, P_PAWN, 6, 7}, }, 0, 0, 0, 0, {TRUE, FALSE}, {0, -1, -1, 0, 0}}; GameState cur_state; CDoc *chess_doc; U0 GameSnapShot() { GameState *tmpg = MAlloc(sizeof(GameState)); MemCopy(tmpg, &cur_state, sizeof(GameState)); QueueInsert(tmpg, cur_state.last); } I64 PieceFind(I64 x, I64 y, I64 player) { I64 i; for (i = 0; i < 32; i++) if (cur_state.pieces[i].player == player && cur_state.pieces[i].x == x && cur_state.pieces[i].y == y) return i; return -1; } #define BD_BORDER 4 #define BD_SIDE (BD_BORDER + 8 + BD_BORDER) #define T_BLKED -1 #define T_PLAYER0 0 #define T_PLAYER1 1 #define T_CLR 2 U0 BrdFill(I8 *brd, PieceState *pieces) { I64 i; MemSet(brd, T_BLKED, sizeof(I8) * BD_SIDE * BD_SIDE); for (i = BD_BORDER; i < 8 + BD_BORDER; i++) brd[BD_SIDE * i + BD_BORDER](I64) = 0x0202020202020202; #assert T_CLR == 2 for (i = 0; i < 32; i++) if (!pieces[i].player) brd[BD_SIDE * (BD_BORDER + pieces[i].y) + BD_BORDER + pieces[i].x] = T_PLAYER0; else if (pieces[i].player == 1) brd[BD_SIDE * (BD_BORDER + pieces[i].y) + BD_BORDER + pieces[i].x] = T_PLAYER1; } I8 BrdPeek(I8 *brd, I64 x, I64 y) { return brd[(y + BD_BORDER) * BD_SIDE + BD_BORDER + x]; } I64 knight_x_offsets[8] = {-1, 1, 2, 2, -1, 1, -2, -2}, knight_y_offsets[8] = {-2, -2, -1, 1, 2, 2, -1, 1}, bishop_x_offsets3[4] = {-1, 1, -1, 1}, bishop_y_offsets3[4] = {-1, -1, 1, 1}; I64 PieceMoves(GameState *state, I64 piece_num, Move *mvs) { PieceState *p = state->pieces, *cur_p = &p[piece_num]; I64 res = 0, i, j, k, x, y, xx = cur_p->x, yy = cur_p->y; I8 brd[BD_SIDE * BD_SIDE * sizeof(I8)]; BrdFill(brd, p); switch [state->pieces[piece_num].type] { case P_PAWN: if (!state->cur_player) { if (BrdPeek(brd, xx + 1, yy) == T_CLR) { mvs[res].x = xx + 1; mvs[res].y = yy; res++; if (xx == 1 && BrdPeek(brd, xx + 2, yy) == T_CLR) { mvs[res].x = xx + 2; mvs[res].y = yy; res++; } } if (BrdPeek(brd, xx + 1, yy - 1) == T_PLAYER1 || state->potential_en_passant_victim.x == xx && state->potential_en_passant_victim.y == yy - 1) { mvs[res].x = xx + 1; mvs[res].y = yy - 1; res++; } if (BrdPeek(brd, xx + 1, yy + 1) == T_PLAYER1 || state->potential_en_passant_victim.x == xx && state->potential_en_passant_victim.y == yy + 1) { mvs[res].x = xx + 1; mvs[res].y = yy + 1; res++; } } else { if (BrdPeek(brd, xx - 1, yy) == T_CLR) { mvs[res].x = xx - 1; mvs[res].y = yy; res++; if (xx == 6 && BrdPeek(brd, xx - 2, yy) == T_CLR) { mvs[res].x = xx - 2; mvs[res].y = yy; res++; } } if (!BrdPeek(brd, xx - 1, yy - 1) || state->potential_en_passant_victim.x == xx && state->potential_en_passant_victim.y == yy - 1) { mvs[res].x = xx - 1; mvs[res].y = yy - 1; res++; } if (!BrdPeek(brd,xx-1,yy+1) || state->potential_en_passant_victim.x==xx && state->potential_en_passant_victim.y==yy+1) { mvs[res].x = xx - 1; mvs[res].y = yy + 1; res++; } } break; case P_ROOK: for (j = 0; j < 4; j++) { x = xx; y = yy; for (i = 1; i < 8; i++) { x += gr_x_offsets2[j]; y += gr_y_offsets2[j]; k = BrdPeek(brd, x, y); if (k == T_CLR || k == cur_p->player ^ 1) { mvs[res].x = x; mvs[res].y = y; res++; if (k != T_CLR) break; } else break; } } break; case P_KNIGHT: for (j = 0; j < 8; j++) { x = xx + knight_x_offsets[j]; y = yy + knight_y_offsets[j]; k = BrdPeek(brd, x, y); if (k == T_CLR || k == cur_p->player ^ 1) { mvs[res].x = x; mvs[res].y = y; res++; } } break; case P_BISHOP: for (j = 0; j < 4; j++) { x = xx; y = yy; for (i = 1; i < 8; i++) { x += bishop_x_offsets3[j]; y += bishop_y_offsets3[j]; k = BrdPeek(brd, x, y); if (k == T_CLR || k == cur_p->player ^ 1) { mvs[res].x = x; mvs[res].y = y; res++; if (k != T_CLR) break; } else break; } } break; case P_QUEEN: for (j = 0; j < 8; j++) { x = xx; y = yy; for (i = 1; i < 8; i++) { x += gr_x_offsets[j]; y += gr_y_offsets[j]; k = BrdPeek(brd, x, y); if (k == T_CLR || k == cur_p->player ^ 1) { mvs[res].x = x; mvs[res].y = y; res++; if (k != T_CLR) break; } else break; } } break; case P_KING: for (j = 0; j < 8; j++) { x = xx + gr_x_offsets[j]; y = yy + gr_y_offsets[j]; k = BrdPeek(brd, x, y); if (k == T_CLR || k == cur_p->player ^ 1) { mvs[res].x = x; mvs[res].y = y; res++; } } if (!Bt(&state->moved_mask, piece_num)) { if (!state->cur_player) { if (state->pieces[0].player >= 0 && !Bt(&state->moved_mask, 0) && BrdPeek(brd, 0, 1) == T_CLR && BrdPeek(brd, 0, 2) == T_CLR && BrdPeek(brd, 0, 3) == T_CLR) { mvs[res].x = 0; mvs[res].y = 2; res++; } if (state->pieces[7].player >= 0 && !Bt(&state->moved_mask, 7) && BrdPeek(brd, 0, 6) == T_CLR && BrdPeek(brd, 0, 5) == T_CLR) { mvs[res].x = 0; mvs[res].y = 6; res++; } } else { if (state->pieces[24].player >= 0 && !Bt(&state->moved_mask, 24) && BrdPeek(brd, 7, 1) == T_CLR && BrdPeek(brd, 7, 2) == T_CLR && BrdPeek(brd, 7, 3) == T_CLR) { mvs[res].x = 7; mvs[res].y = 2; res++; } if (state->pieces[31].player >= 0 && !Bt(&state->moved_mask, 31) && BrdPeek(brd, 7, 6) == T_CLR && BrdPeek(brd, 7, 5) == T_CLR) { mvs[res].x = 7; mvs[res].y = 6; res++; } } } break; } for (i = 0; i < res; i++) mvs[i].piece = piece_num; return res; } Bool SqrInChk(GameState *state, I64 x, I64 y) { I64 i, j, mv_cnt; Move mvs[32]; for (i = 0; i < 32; i++) if (state->pieces[i].player == state->cur_player) { mv_cnt = PieceMoves(state, i, mvs); for (j = 0; j < mv_cnt; j++) if (mvs[j].x == x && mvs[j].y == y) return TRUE; } return FALSE; } I64 PopUpPiece(I64 player) { U8 buf[STR_LEN]; I64 i; CDoc *doc; do { doc = DocNew; DocPrint(doc, "\n\n\n\n\n..."); for (i = P_ROOK; i <= P_QUEEN; i++) { StrPrint(buf, "$SP+LE+X,\"\",%d,%%d$....", i); DocSprite(doc, imgs[2 * i + player], buf); } DocPrint(doc, "\n\n\n"); i = PopUpMenu(doc); DocDel(doc); } while (i < 0); return i; } CDC *brd_dc; I64 cx, cy, cx2, cy2, g, cursor_x, cursor_y, cur_mv_cnt, *s2w, *w2s; Move cur_mvs[32], last_mv; Bool ai_thinking, game_over; extern I64 MoveCnt(GameState *state); Bool MoveDo(GameState *state, I64 piece_num, I64 x2, I64 y2, Bool final) { //returns TRUE if killed a piece. PieceState *p = &state->pieces[piece_num]; I64 j, ty = -1, score_adjust = 0, x1 = p->x, y1 = p->y; j = PieceFind(x2, y2, state->cur_player ^ 1); if (j >= 0) { state->pieces[j].player -= 2; //dead ty = state->pieces[j].type; score_adjust = piece_scores[ty]; } else if (p->type == P_PAWN && AbsI64(y1 - y2) == 1) { //en passant? if (!state->cur_player) j = PieceFind(x2 - 1, y2, 1); else j = PieceFind(x2 + 1, y2, 0); if (j >= 0) { state->pieces[j].player -= 2; //dead ty = state->pieces[j].type; score_adjust = piece_scores[ty]; } } if (p->type == P_PAWN && AbsI64(x1 - x2) == 2) { state->potential_en_passant_victim.x = x2; state->potential_en_passant_victim.y = y2; } else { state->potential_en_passant_victim.x = -1; state->potential_en_passant_victim.y = -1; } if (p->type == P_PAWN && (!x2 || x2 == 7)) { if (!state->is_human[state->cur_player] || !final) p->type = P_QUEEN; else p->type = PopUpPiece(state->cur_player); score_adjust += piece_scores[p->type] - piece_scores[P_PAWN]; } else if (p->type == P_KING && AbsI64(y1 - y2) == 2) { //castling? if (!state->cur_player) { if (y2 - y1 < 0) state->pieces[6].y = y2 + 1; else state->pieces[7].y = y2 - 1; } else { if (y2 - y1 < 0) state->pieces[22].y = y2 + 1; else state->pieces[23].y = y2 - 1; } } p->x = x2; p->y = y2; LBts(&state->moved_mask, piece_num); if (!state->cur_player) state->raw_score += score_adjust; else state->raw_score -= score_adjust; state->cur_player ^= 1; state->mv_num++; if (final) { last_mv.x = x2; last_mv.y = y2; DocPrint(chess_doc, "%02d)", state->mv_num); if (state->cur_player) DocPrint(chess_doc, "$FG,LTGRAY$"); else DocPrint(chess_doc, "$FG,BLACK"); DocPrint(chess_doc, "%c:%c%c-%c%c", piece_letters[p->type], y1 + 'A', x1 + '1', y2 + 'A', x2 + '1'); if (ty >= 0) { if (state->cur_player) DocPrint(chess_doc, "$FG$*$FG,BLACK$"); else DocPrint(chess_doc, "$FG$*$FG,LTGRAY$"); DocPrint(chess_doc, "%c", piece_letters[ty]); } DocPrint(chess_doc, "$FG$\n"); if (!MoveCnt(&cur_state)) { DocPrint(chess_doc, "$FG,RED$Game Over$FG$\n"); game_over = TRUE; } GameSnapShot; } return ToBool(score_adjust); } I64 MoveFind(I64 x, I64 y, Move *mvs, I64 mv_cnt) { I64 i; for (i = 0; i < mv_cnt; i++) if (mvs[i].x == x && mvs[i].y == y) return i; return -1; } Bool HumanMove(GameState *state, I64 piece_num, Move *mvs, I64 mv_cnt, I64 x, I64 y) { if (piece_num >= 0 && MoveFind(x, y, mvs, mv_cnt) >= 0) { MoveDo(state, piece_num, x, y, TRUE); return TRUE; } else return FALSE; } I64 ChkPieceMoves(GameState *state, I64 piece_num, Move *src, I64 mv_cnt) { //Goes through moves lst for one piece, eliminating invalid moves. I64 k, cnt = mv_cnt, res = 0, x, y, dy; GameState state2, *tmpg; Move *dst = src; if (state->cur_player) //Find King k = 16; else k = 0; while (cnt--) { MemCopy(&state2, state, sizeof(GameState)); MoveDo(&state2, piece_num, src->x, src->y, FALSE); //Don't repeat board position tmpg = cur_state.next; while (tmpg != &cur_state) { if (!MemCompare(tmpg->pieces, state2.pieces, sizeof(PieceState) * 32)) goto cm_skip; tmpg = tmpg->next; } if (piece_num == k) { //If king is moving, check his new location. x = state2.pieces[k].x; y = state2.pieces[k].y; dy = y - state->pieces[k].y; if (AbsI64(dy) == 2 && /*castling?*/ (SqrInChk(&state2, x, y - dy) || SqrInChk(&state2, x, y - SignI64(dy)))) goto cm_skip; // This really needs to go into PieceMoves } else { x = state->pieces[k].x; y = state->pieces[k].y; } if (!SqrInChk(&state2, x, y)) { MemCopy(dst++, src, sizeof(Move)); res++; } cm_skip: src++; } return res; } I64 MoveCnt(GameState *state) { I64 i, res = 0, mv_cnt; Move mvs[32]; for (i = 0; i < 32; i++) if (state->pieces[i].player == state->cur_player) { mv_cnt = PieceMoves(state, i, mvs); mv_cnt = ChkPieceMoves(state, i, mvs,mv_cnt); res += mv_cnt; } return res; } U0 DrawIt(CTask *task, CDC *dc) { I64 i, x, y, z, k0, k1; PieceState *s = cur_state.pieces; brd_dc->flags |= DCF_NO_TRANSPARENTS; GrBlot(dc, cx - cx2, cy - cy2, brd_dc); task->text_attr = YELLOW << 4; dc->x = cx; dc->y = cy; dc->flags |= DCF_TRANSFORMATION; MemCopy(dc->r, w2s, sizeof(I64) * 16); if (ai_thinking) { if (cur_state.cur_player) dc->color = BLACK; else dc->color = WHITE; if (Blink) GrPrint(dc, -11 * FONT_WIDTH / 2, 2 * FONT_HEIGHT - cy, "Thinking..."); } dc->color = RED; for (i = 0; i < cur_mv_cnt; i++) GrRect3(dc, (cur_mvs[i].x - 4) * g + g / 4, (cur_mvs[i].y - 4) * g + g / 4, 0, g / 2, g / 2); dc->color = LTRED; dc->thick = 2; GrBorder(dc, (cursor_x - 4) * g + 2, (cursor_y - 4) * g + 2, (cursor_x - 3) * g - 1, (cursor_y - 3) * g - 1); DCDepthBufAlloc(dc); k0 = k1 = 0; for (i = 0; i < 32; i++,s++) { if (s->player >= 0) { //Alive? if (s->x != last_mv.x || s->y != last_mv.y || Blink) { x = g / 2 + (s->x - 4) * g; y = g / 2 + (s->y - 4) * g; z = 0; DCTransform(dc, &x, &y, &z); z = GR_Z_ALL + s->x - s->y; dc->flags &= ~DCF_TRANSFORMATION; Sprite3(dc, x, y, z, imgs[s->type * 2 + s->player]); dc->flags |= DCF_TRANSFORMATION; } } else { //Dead if (s->player == -2) { if (k1 < 8) { x = cx + 100 + k1++ * g / 2; y = cy - 180; } else { x = cx + 100 + (k1++ - 8) * g / 2; y = cy - 130; } z = 0; } else { if (k0 < 8) { x = cx + 100 + k0++ * g / 2; y = cy + 220; } else { x = cx + 100 + (k0++ - 8) * g / 2; y = cy + 170; } z = 0; } dc->flags &= ~DCF_TRANSFORMATION; Sprite3(dc, x, y, z, imgs[s->type * 2 + s->player + 2]); dc->flags |= DCF_TRANSFORMATION; } } if (game_over && Blink) { dc->color = LTRED; GrPrint3(dc, -9 * FONT_WIDTH / 2, -FONT_HEIGHT / 2, 0, "Game Over"); } } #define DEPTH -5 CDC *BrdNew() { I64 i, j, k, w = Fs->pix_width, h = Fs->pix_height; CDC *dc; g = 42; cx = w / 2 + 40; cy = h / 2; w = Sqrt(2.0) * (8 * g + 2 * FONT_WIDTH); h = 8 * g + 2 * FONT_HEIGHT; cx2 = w / 2; cy2 = h / 2; dc = DCNew(w, h); dc->x = cx2; dc->y = cy2; dc->flags |= DCF_TRANSFORMATION; MemCopy(dc->r, w2s, sizeof(I64) * 16); dc->color = YELLOW; GrRect(dc, 0, 0, w, h); dc->color = BLACK; for (i = -4; i < 4; i++) { GrPrint3(dc, -4 * g - 4 - FONT_WIDTH, i * g + g / 2 - 4, 0, "%C", i + 4 + 'A'); GrPrint3(dc, 4 * g + 14, i * g + g / 2 - 4, 0, "%C", i + 4 + 'A'); GrPrint3(dc, i * g + g / 2 + 6, -4 * g - 12 - FONT_HEIGHT, 0, "%C", i + 4 + '1'); GrPrint3(dc, i * g + g / 2 - 2, 4 * g + 9, 0, "%C", i + 4 + '1'); } dc->thick = 1; dc->color = BLACK; for (i = -4; i <= 4; i++) { GrLine3(dc, -4 * g, i * g, 0, 4 * g, i * g, 0); GrLine3(dc, i * g, -4 * g, 0, i * g, 4 * g, 0); GrLine3(dc, -4 * g, i * g, 0, -4 * g, i * g, DEPTH); GrLine3(dc, i * g, 4 * g, 0, i * g, 4 * g, DEPTH); } GrLine3(dc, -4 * g, 4 * g, DEPTH, -4 * g, -4 * g, DEPTH); GrLine3(dc, -4 * g, 4* g, DEPTH, 4 * g, 4 * g, DEPTH); k = 0; for (j = -4; j < 4; j++) { for (i = -4; i < 4; i++) { if ((i + j) & 1) dc->color = WHITE; else dc->color = DKGRAY; GrFloodFill3(dc, i * g + g / 2, j * g + g / 2, 0); } if (j & 1) dc->color = DKGRAY; else dc->color = WHITE; GrFloodFill3(dc, -4 * g, j * g + g / 2, DEPTH / 2); if (j & 1) dc->color = WHITE; else dc->color = DKGRAY; GrFloodFill3(dc, j * g + g / 2, 4 * g, DEPTH / 2); } return dc; } I64 AIStateScore(GameState *state, I64 player) { if (player) return -state->raw_score; else return state->raw_score; } I64 move_depth, kill_depth; I64 AIMove2(GameState *state, I64 depth) { I64 i, possible_moves = 0, best_piece, score, best_score = I64_MIN, mv_cnt, score_adjust; Move best_mv, mvs[32],*ptr; GameState state2; Bool first = TRUE; if (UnusedStack < 0x1000) { //Shouldn't happen DocPrint(chess_doc, "StkOverflow\n"); return AIStateScore(state, state->cur_player); } for (i = 0; i < 32; i++) if (state->pieces[i].player == state->cur_player) { mv_cnt = PieceMoves(state, i, mvs); possible_moves += mv_cnt; ptr = mvs; while (mv_cnt--) { MemCopy(&state2, state, sizeof(GameState)); state2.parent = state; if (MoveDo(&state2, i, ptr->x, ptr->y, FALSE) && depth < kill_depth || depth < move_depth) score =-AIMove2(&state2, depth + 1); else { score = AIStateScore(&state2, state->cur_player); if (score_adjust = AbsI64(state2.raw_score - state->raw_score)) score += score_adjust - piece_scores[state->pieces[i].type]; } if (first || score>best_score) { best_score = score; best_piece = i; MemCopy(&best_mv, ptr, sizeof(Move)); first = FALSE; } ptr++; } } if (!first) MoveDo(state, best_piece, best_mv.x, best_mv.y, FALSE); return best_score + possible_moves << SHIFT_POSSIBLE_MOVES; } class ChessJob { GameState *state; Move *mv; }; U0 AIMove1(ChessJob *cj) { GameState state2; Move *ptr = cj->mv; MemCopy(&state2, cj->state, sizeof(GameState)); state2.parent = cj->state; MoveDo(&state2, ptr->piece, ptr->x, ptr->y, FALSE); ptr->score = -AIMove2(&state2, 1); Free(cj); } I64 MoveCmp(Move *e1, Move *e2) { if (e1->score < e2->score) return 1; else if (e1->score == e2->score) return 0; else return -1; } U0 AIMove(GameState *state) { I64 i, cnt = 0, mv_cnt, best_score; Move mvs[32], *ptr, *ptr2; ChessJob *cj; if (cnt = MoveCnt(state)) {//Collect AI possible valid moves ptr = ptr2 = MAlloc(cnt * sizeof(Move)); for (i = 0; i < 32; i++) if (state->pieces[i].player == state->cur_player) { mv_cnt = PieceMoves(state, i, mvs); mv_cnt = ChkPieceMoves(state, i, mvs, mv_cnt); MemCopy(ptr, mvs, mv_cnt * sizeof(Move)); ptr += mv_cnt; } //Dispatch to MP scoring all moves for (i = 0, ptr = ptr2; i < cnt; i++, ptr++) { ptr->score = -I32_MAX; cj = MAlloc(sizeof(ChessJob)); cj->state = state; cj->mv = ptr; if (mp_count > 1) JobQueue(&AIMove1, cj, i % (mp_count - 1)); else { AIMove1(cj); Yield; } } //Wait for finish for (i = 0, ptr = ptr2; i < cnt; i++, ptr++) while (ptr->score == -I32_MAX) Yield; QuickSort(ptr2, cnt, sizeof(Move), &MoveCmp); //Cnt ties for first best_score = ptr2->score; mv_cnt = 0; ptr = ptr2; while (mv_cnt < cnt && ptr->score == best_score) { mv_cnt++; ptr++; } ptr = ptr2 + RandU16 % mv_cnt; MoveDo(state, ptr->piece, ptr->x, ptr->y, TRUE); Free(ptr2); } } U0 Init() { chess_doc = DocPut; DocClear; "$FD,BLACK$$BG,YELLOW$"; game_over = FALSE; ai_thinking = FALSE; move_depth = 3; kill_depth = 5; cur_mv_cnt = 0; last_mv.x = -1; last_mv.y = -1; MemCopy(&cur_state, &setup_state, sizeof(cur_state)); QueueInit(&cur_state); cur_state.is_human[0] = PopUpNoYes("$FG,BLACK$WHITE is human?"); cur_state.is_human[1] = PopUpNoYes("$FG,BLACK$BLACK is human?"); GameSnapShot; } U0 CleanUp() { QueueDel(&cur_state, TRUE); } U0 SetMouseCursor() { I64 x = (cursor_x - 4) * g + g / 2, y = (cursor_y - 4) * g + g / 2, z = 0; Mat4x4MulXYZ(w2s, &x, &y, &z); x += cx + Fs->pix_left + Fs->scroll_x; y += cy + Fs->pix_top + Fs->scroll_y; if (mouse.pos.x != x || mouse.pos.y != y) MouseSet(x, y); } U0 SetKbdCursor(I64 x, I64 y) { I64 z; x -= cx; y -= cy; z = y; Mat4x4MulXYZ(s2w, &x, &y, &z); cursor_x = ClampI64((x + 4 * g) / g, 0, 7); cursor_y = ClampI64((y + 4 * g) / g, 0, 7); } U0 Chess() { I64 arg1, arg2, cur_piece; SettingsPush; //See SettingsPush AutoComplete; WinBorder; WinMax; Init; MenuPush( "File {" " Abort(,CH_SHIFT_ESC);" " Exit(,CH_ESC);" "}" "Play {" " Move(,CH_SPACE);" " Restart(,'\n');" "}"); Fs->win_inhibit = WIG_TASK_DEFAULT - WIF_SELF_FOCUS - WIF_SELF_BORDER - WIF_FOCUS_TASK_MENU; cursor_x = cursor_y = 3; w2s = Mat4x4IdentNew; //World-to-Scrn Mat4x4RotZ(w2s, -pi / 4); Mat4x4RotX(w2s, pi / 4); s2w = Mat4x4IdentNew; //Scrn-to-World Mat4x4RotX(s2w, -pi / 4); Mat4x4RotZ(s2w, pi / 4); brd_dc = BrdNew; SetMouseCursor; PaletteSetLight(FALSE); Fs->draw_it = &DrawIt; try { while (TRUE) { switch (MessageScan(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN + 1 << MESSAGE_KEY_UP + 1 << MESSAGE_MS_L_DOWN + 1 << MESSAGE_MS_L_UP + 1 << MESSAGE_MS_MOVE)) { case 0: if (!game_over && !cur_state.is_human[cur_state.cur_player]) { ai_thinking = TRUE; AIMove(&cur_state); ai_thinking = FALSE; Beep; } Refresh; break; case MESSAGE_MS_MOVE: SetKbdCursor(arg1, arg2); break; case MESSAGE_MS_L_DOWN: SetKbdCursor(arg1, arg2); ch_mv_start: if (!game_over && cur_state.is_human[cur_state.cur_player]) { cur_piece = PieceFind(cursor_x, cursor_y, cur_state.cur_player); if (cur_piece >= 0) { cur_mv_cnt = PieceMoves(&cur_state, cur_piece, cur_mvs); cur_mv_cnt = ChkPieceMoves(&cur_state, cur_piece, cur_mvs, cur_mv_cnt); } else cur_mv_cnt = 0; } break; case MESSAGE_MS_L_UP: SetKbdCursor(arg1, arg2); ch_mv_end: if (!game_over && cur_state.is_human[cur_state.cur_player]) { if (cur_piece >= 0 && HumanMove(&cur_state, cur_piece, cur_mvs, cur_mv_cnt, cursor_x, cursor_y)) { Beep(22); cur_piece = -1; cur_mv_cnt = 0; } else { cur_piece = -1; cur_mv_cnt = 0; } } break; case MESSAGE_KEY_UP: if (arg1 == CH_SPACE) goto ch_mv_end; break; case MESSAGE_KEY_DOWN: switch (arg1) { case '\n': CleanUp; Init; break; case CH_ESC: case CH_SHIFT_ESC: goto ch_done; case CH_SPACE: goto ch_mv_start; case 0: switch (arg2.u8[0]) { case SC_CURSOR_RIGHT: cursor_y = ClampI64(cursor_y + 1, 0, 7); SetMouseCursor; break; case SC_CURSOR_LEFT: cursor_y = ClampI64(cursor_y - 1, 0, 7); SetMouseCursor; break; case SC_CURSOR_DOWN: cursor_x = ClampI64(cursor_x - 1, 0, 7); SetMouseCursor; break; case SC_CURSOR_UP: cursor_x = ClampI64(cursor_x + 1, 0, 7); SetMouseCursor; break; } } break; } } ch_done: } catch PutExcept; DocClear; SettingsPop; MenuPop; DCDel(brd_dc); Free(s2w); Free(w2s); CleanUp; } Chess; U0 DoChess() { I64 i; CTask *task = User; XTalk(task, "#include \"::/Demo/Games/Chess\";\n"); BirthWait(&task->popup_task); TaskWait(task->popup_task); MessagePostWait(task->popup_task, MESSAGE_KEY_DOWN_UP, CH_SPACE, 0); Sleep(100); BirthWait(&task->popup_task); TaskWait(task->popup_task); MessagePostWait(task->popup_task, MESSAGE_KEY_DOWN_UP, CH_SPACE, 0); Sleep(100); for (i = 0; i < 8; i++) { MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_DOWN); MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_RIGHT); Sleep(25); } MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_UP); Sleep(250); MessagePostWait(task, MESSAGE_KEY_DOWN, CH_SPACE, 0); MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_UP); Sleep(250); MessagePostWait(task, MESSAGE_KEY_DOWN_UP, 0, SC_CURSOR_UP); MessagePostWait(task, MESSAGE_KEY_UP, CH_SPACE, 0); Sleep(1000); MessagePostWait(task, MESSAGE_KEY_DOWN_UP, CH_SHIFT_ESC, 0); DeathWait(&task, TRUE); } Chess/* Graphics Not Rendered in HTML */