Trivial Solutions
                    The LoseThos 64-bit PC Operating System
File:/LT/Demo/GameStarters/Chess.CPZ
This, and all LoseThos files, are public domain.  Do whatever you like.
God is watching; God is just; God is never out-done in generosity.  He talks!

LoseThos code compiles with the 64-bit LoseThos compiler/assembler I wrote.
Boot the LoseThos CD and you can compile it.  Not in Kansas anymore!

Click here for preliminary compiler information you need.




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
  BoolI8 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;
Ltf *chess_ltf;

U0 GameSnapShot()
{
  GameState *tempg=MAlloc(sizeof(GameState));
  MemCpy(tempg,&cur_state,sizeof(GameState));
  InsQue(tempg,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]><(U64)=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 result=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);
  nobound_switch (state->pieces[piece_num].type) {
    case P_PAWN:
      if (!state->cur_player) {
        if (BrdPeek(brd,xx+1,yy)==T_CLR) {
          mvs[result].x=xx+1;
          mvs[result].y=yy;
          result++;
          if (xx==1 && BrdPeek(brd,xx+2,yy)==T_CLR) {
            mvs[result].x=xx+2;
            mvs[result].y=yy;
            result++;
          }
        }
        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[result].x=xx+1;
          mvs[result].y=yy-1;
          result++;
        }
        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[result].x=xx+1;
          mvs[result].y=yy+1;
          result++;
        }
      } else {
        if (BrdPeek(brd,xx-1,yy)==T_CLR) {
          mvs[result].x=xx-1;
          mvs[result].y=yy;
          result++;
          if (xx==6 && BrdPeek(brd,xx-2,yy)==T_CLR) {
            mvs[result].x=xx-2;
            mvs[result].y=yy;
            result++;
          }
        }
        if (!BrdPeek(brd,xx-1,yy-1) ||
          state->potential_en_passant_victim.x==xx &&
          state->potential_en_passant_victim.y==yy-1) {
          mvs[result].x=xx-1;
          mvs[result].y=yy-1;
          result++;
        }
        if (!BrdPeek(brd,xx-1,yy+1) ||
          state->potential_en_passant_victim.x==xx &&
          state->potential_en_passant_victim.y==yy+1) {
          mvs[result].x=xx-1;
          mvs[result].y=yy+1;
          result++;
        }
      }
      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[result].x=x;
            mvs[result].y=y;
            result++;
            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[result].x=x;
          mvs[result].y=y;
          result++;
        }
      }
      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[result].x=x;
            mvs[result].y=y;
            result++;
            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[result].x=x;
            mvs[result].y=y;
            result++;
            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[result].x=x;
          mvs[result].y=y;
          result++;
        }
      }
      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[result].x=0;
            mvs[result].y=2;
            result++;
          }
          if (state->pieces[7].player>=0 &&
              !Bt(&state->moved_mask,7) &&
              BrdPeek(brd,0,6)==T_CLR &&
              BrdPeek(brd,0,5)==T_CLR) {
            mvs[result].x=0;
            mvs[result].y=6;
            result++;
          }
        } 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[result].x=7;
            mvs[result].y=2;
            result++;
          }
          if (state->pieces[31].player>=0 &&
              !Bt(&state->moved_mask,31) &&
              BrdPeek(brd,7,6)==T_CLR &&
              BrdPeek(brd,7,5)==T_CLR) {
            mvs[result].x=7;
            mvs[result].y=6;
            result++;
          }
        }
      }
      break;
  }
  for (i=0;i<result;i++)
    mvs[i].piece=piece_num;
  return result;
}

BoolI64 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[64];
  I64 i;
  Ltf *l;
  do {
    l=LtfNew;
    LtfPutS(l,"\n\n\n\n\n   ");
    for (i=P_ROOK;i<=P_QUEEN;i++) {
      SPrintF(buf,"$PI+LE+X,\"\",%d,%%d$    ",i);
      LtfPutSprite(l,imgs[2*i+player],buf);
    }
    LtfPutS(l,"\n\n\n");
    i=PopUpMenu(l);
    LtfDel(l);
  } while (i<0);
  return i;
}

GrDC *brd_dc;
I64 cx,cy,cx2,cy2,g,cursor_x,cursor_y,
    cur_mv_cnt,*s2w,*w2s;
Move cur_mvs[32],last_mv;
BoolI8 ai_thinking,game_over;

extern I64 MoveCnt(GameState *state);

BoolI64 MoveDo(GameState *state,I64 piece_num,I64 x2,I64 y2,BoolI8 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;
    LtfPrintF(chess_ltf,"%02d)",state->mv_num);
    if (state->cur_player)
      LtfPutS(chess_ltf,"$FG,LTGRAY$");
    else
      LtfPutS(chess_ltf,"$FG,BLACK");
    LtfPrintF(chess_ltf,"%c:%c%c-%c%c",piece_letters[p->type],
      y1+'A',x1+'1',y2+'A',x2+'1');
    if (ty>=0) {
      if (state->cur_player)
        LtfPutS(chess_ltf,"$FG$*$FG,BLACK$");
      else
        LtfPutS(chess_ltf,"$FG$*$FG,LTGRAY$");
      LtfPrintF(chess_ltf,"%c",piece_letters[ty]);
    }
    LtfPutS(chess_ltf,"$FG$\n");
    if (!MoveCnt(&cur_state)) {
      LtfPutS(chess_ltf,"$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;
}

BoolI64 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 list for one piece, eliminating invalid moves.
  I64 k,cnt=mv_cnt,result=0,x,y,dy;
  GameState state2,*tempg;
  Move *dst=src;

  if (state->cur_player) //Find King
    k=16;
  else
    k=0;
  while (cnt--) {
    MemCpy(&state2,state,sizeof(GameState));
    MoveDo(&state2,piece_num,src->x,src->y,FALSE);

    //Don't repeat board position
    tempg=cur_state.next;
    while (tempg!=&cur_state) {
      if (!MemCmp(tempg->pieces,state2.pieces,sizeof(PieceState)*32))
        goto cm_skip;
      tempg=tempg->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)) {
      MemCpy(dst++,src,sizeof(Move));
      result++;
    }
cm_skip:
    src++;
  }
  return result;
}

I64 MoveCnt(GameState *state)
{
  I64 i,result=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);
      result+=mv_cnt;
    }
  return result;
}


U0 DrawIt(TaskStruct *task,GrDC *dc)
{
  I64 i,x,y,z,k0,k1;
  PieceState *s=cur_state.pieces;
  GrDCBlot(dc,cx-cx2,cy-cy2,brd_dc);

  dc->x=cx;
  dc->y=cy;

  dc->flags|=DCF_TRANSFORMATION;
  MemCpy(dc->r,w2s,sizeof(I64)*16);
 
  if (ai_thinking) {
    if (cur_state.cur_player)
      dc->color=BLACK;
    else
      dc->color=WHITE;
    if (Blink(,tP(task)))
      GrPutS(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->pen_width=2;
  GrLineRect4(dc,(cursor_x-4)*g+2,(cursor_y-4)*g+2,(cursor_x-3)*g-1,(cursor_y-3)*g-1);

  GrDCAllocDepthBuf(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(,tP(task))) {
        x=g/2+(s->x-4)*g;
        y=g/2+(s->y-4)*g;
        z=0;
        GrTransform(dc,&x,&y,&z);
        z=GR_Z_ALL+s->x-s->y;

        dc->flags&=~DCF_TRANSFORMATION;
        GrSpritePlot3(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;
      GrSpritePlot3(dc,x,y,z,imgs[s->type*2+s->player+2]);
      dc->flags|=DCF_TRANSFORMATION;
    }
  }
  if (game_over && Blink(,tP(task))) {
    dc->color=LTRED;
    GrPutS3(dc,-9*FONT_WIDTH/2,-FONT_HEIGHT/2,0,"Game Over");
  }
}

#define DEPTH   -5

GrDC *BrdNew()
{
  I64 i,j,k,w=Fs->win_pixel_width,h=Fs->win_pixel_height;
  GrDC *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=GrDCNew(DCT_COLOR4,w,h);
  dc->x=cx2;
  dc->y=cy2;

  dc->flags|=DCF_TRANSFORMATION;
  MemCpy(dc->r,w2s,sizeof(I64)*16);

  dc->color=YELLOW;
  GrRect(dc,0,0,w,h);

  dc->color=BLACK;
  for (i=-4;i<4;i++) {
    GrPrintF3(dc,-4*g-4-FONT_WIDTH,i*g+g/2-4,0,"%C",i+4+'A');
    GrPrintF3(dc,4*g+14,i*g+g/2-4,0,"%C",i+4+'A');
    GrPrintF3(dc,i*g+g/2+6,-4*g-12-FONT_HEIGHT,0,"%C",i+4+'1');
    GrPrintF3(dc,i*g+g/2-2,4*g+9,0,"%C",i+4+'1');
  }

  dc->pen_width=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=MIN_I64,mv_cnt,score_adjust;
  Move best_mv,mvs[32],*ptr;
  GameState state2;
  BoolI8 first=TRUE;

  if (UnusedStk<0x1000) { //Shouldn't happen
    LtfPutS(chess_ltf,"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--) {
        MemCpy(&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;
          MemCpy(&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;
  BoolI8 old_preempt=Preempt(ON);
  MemCpy(&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);
  Preempt(old_preempt);
}

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);
        MemCpy(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=-MAX_I32;
      cj=MAlloc(sizeof(ChessJob));
      cj->state=state;
      cj->mv=ptr;
      if (i%mp_cnt==mp_cnt-1)
        AIMove1(cj);
      else
        MPJob(&AIMove1,cj);
    }

    //Wait for finish
    for (i=0,ptr=ptr2;i<cnt;i++,ptr++)
      while (ptr->score==-MAX_I32)
        Yield;
    QSort(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_ltf=LtfCur;
  LtfClear;
  "$FD,BLACK$$BD,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;
  MemCpy(&cur_state,&setup_state,sizeof(cur_state));
  cur_state.next=cur_state.last=&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()
{
  GameState *tempg=cur_state.next,*tempg1;
  while (tempg!=&cur_state) {
    tempg1=tempg->next;
    RemQue(tempg);
    Free(tempg);
    tempg=tempg1;
  }
}

U0 SetMouseCursor()
{
  I64 x=(cursor_x-4)*g+g/2,
      y=(cursor_y-4)*g+g/2,
      z=0;
  GrRot(w2s,&x,&y,&z);
  x+=cx+Fs->win_pixel_left+Fs->win_scroll_x;
  y+=cy+Fs->win_pixel_top +Fs->win_scroll_y;
  if (ipx!=x || ipy!=y)
    SetIP(x,y);
}

U0 SetKbdCursor(I64 x,I64 y)
{
  I64 z;
  x-=cx;
  y-=cy;
  z=y;
  GrRot(s2w,&x,&y,&z);
  cursor_x=LimitI64((x+4*g)/g,0,7);
  cursor_y=LimitI64((y+4*g)/g,0,7);
}

U0 Chess()
{
  I64 p1,p2,cur_piece;

  SettingsPush; //See SettingsPush
  WinMax;
  WinBorder(OFF);
  Preempt(OFF);
  Init;
  WordStat(OFF);

  Fs->text_attr=YELLOW<<4;

  MenuPush(
  "File {"
  "  Abort(,CH_SHIFT_ESC);"
  "  Exit(,CH_ESC);"
  "}"
  "Play {"
  "  Move(,CH_SPACE);"
  "  Restart(,CH_CR);"
  "}");


  Fs->win_inhibit=WIF_ALL-WIF_BORDER-WIF_MENU;

  cursor_x=cursor_y=3;

  w2s=GrIdent; //World-to-Screen
  GrRotZEqu(w2s,-pi/4);
  GrRotXEqu(w2s,pi/4);

  s2w=GrIdent; //Screen-to-World
  GrRotXEqu(s2w,-pi/4);
  GrRotZEqu(s2w,pi/4);

  brd_dc=BrdNew;

  SetMouseCursor;
  Fs->draw_it=&DrawIt;
  try {
    while (TRUE) {
      switch (ScanMsg(&p1,&p2,1<<MSG_KEY_DOWN+1<<MSG_KEY_UP+1<<MSG_IP_L_DOWN+1<<MSG_IP_L_UP+1<<MSG_IP_MOVE)) {
        case 0:
          if (!game_over && !cur_state.is_human[cur_state.cur_player]) {
            ai_thinking=TRUE;
            AIMove(&cur_state);
            ai_thinking=FALSE;
            Beep(500);
          }
          WinSync;
          break;
        case MSG_IP_MOVE:
          SetKbdCursor(p1,p2);
          break;
        case MSG_IP_L_DOWN:
          SetKbdCursor(p1,p2);
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 MSG_IP_L_UP:
          SetKbdCursor(p1,p2);
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(250);
              cur_piece=-1;
              cur_mv_cnt=0;
            } else {
              cur_piece=-1;
              cur_mv_cnt=0;
            }
          }
          break;
        case MSG_KEY_UP:
          if (p1==CH_SPACE)
            goto ch_mv_end;
          break;
        case MSG_KEY_DOWN:
          switch (p1) {
            case CH_CR:
              CleanUp;
              Init;
              break;
            case CH_ESC:
            case CH_SHIFT_ESC:
              goto ch_done;
            case CH_SPACE:
              goto ch_mv_start;
            case 0:
              switch (p2.u8[0]) {
                case SC_CURSOR_RIGHT:
                  cursor_y=LimitI64(cursor_y+1,0,7);
                  SetMouseCursor;
                  break;
                case SC_CURSOR_LEFT:
                  cursor_y=LimitI64(cursor_y-1,0,7);
                  SetMouseCursor;
                  break;
                case SC_CURSOR_DOWN:
                  cursor_x=LimitI64(cursor_x-1,0,7);
                  SetMouseCursor;
                  break;
                case SC_CURSOR_UP:
                  cursor_x=LimitI64(cursor_x+1,0,7);
                  SetMouseCursor;
                  break;
              }
          }
          break;
      }
    }
ch_done:
  } catch
    CatchAll;
  LtfClear;
  SettingsPop;
  MenuPop;
  GrDCDel(brd_dc);
  Free(s2w);
  Free(w2s);
  CleanUp;
}

Chess;
Historical capture: 2012-03-11. Home | Power on LoseThos | Sources and changes